Home
english
Home
.NET Server
Java Server
.NET Client
AJAX Client
AJAX Browser
Map Drive
Pricing
Contacts
info@ithit.com



Creating Class 2 / Class 3 WebDAV Server in Java

In addition to features provided by Class 1 / Class 3 WebDAV server, Class 2 / Class 3 WebDAV server supports files and folders locking.

Locking is required to protect item from being modified by other users. Many WebDAV clients such as Microsoft Web Folders, Mac OS X WebDAV client and Microsoft Office require Class 2 WebDAV server.

To create a Class 2 / Class 3 server you must implement Lock interface on your folder and file items. Also before updating file content or item custom properties as well as when copying, moving and deleting item you must verify if client provided a valid lock-token.

How WebDAV Client Discovers Server Compliance

When discovering WebDAV item compliance many WebDAV clients rely on DAV header returned with OPTIONS request. After you implement Lock interface on an item the server will respond with DAV: 1, 2, 3 header, meaning the item supports locking. The server will also return LOCK and UNLOCK verbs in Allow and Public headers.

As soon as some WebDAV clients may rely on DAV or Allow / Public headers returned in response to folder item, it is recommended to add Lock interface on a folder items even if you do not plan to implement lock methods on folders.

Locking the Item

Lock interface provides the means for locking the hierarchy item, updating lock timeout and accessing the list of applied locks. When WebDAV client issues lock request WebDAV engine calls Lock.lock() method passing information about requested lock. In your lock() implementation you must generate the new lock-token (usually a GUID), associate the lock-token with the item in the repository and return the lock-token to the engine. Optionally in your lock() implementation you can modify the timeout. The engine than sends lock and timeout values back to WebDAV client.

   public LockResult lock(boolean shared, boolean deep, long timeout, String owner)
            throws LockedException, MultistatusException, ServerException {
        if (itemHasLock(shared))
            throw new LockedException();

        if (deep) { // check if no items are locked in this subtree
            checkNoItemsLocked(this, shared);
        }

        String token = UUID.randomUUID().toString();
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        calendar.add(Calendar.SECOND, (int) timeout);
        Timestamp expires = timeout >= 0 ? new Timestamp(calendar.getTimeInMillis()) : null;

        getDataAccess().executeUpdate("INSERT INTO Locks (ItemID,Token,Shared,Deep,Expires,Owner)"
                + " VALUES(?, ?, ?, ?, ?, ?)",
                getId(), token, shared, deep, expires, owner);
        return new LockResult(token, timeout);
    }


    protected boolean itemHasLock(boolean skipShared) throws ServerException {
        List<LockInfo> locks = getActiveLocks();
        if (locks.size() == 0)
            return false;
        return !(skipShared && locks.get(0).isShared());
    }

 

    protected void checkNoItemsLocked(HierarchyItemImpl root, boolean skipShared)
            throws ServerException, MultistatusException {

        MultistatusException mr = new MultistatusException();
        checkNoItemsLocked(mr, root, skipShared);
        if (mr.getResponses().length > 0)
            throw mr;
    }

 

    protected void checkNoItemsLocked(MultistatusException mr, HierarchyItemImpl root, boolean skipShared)
            throws ServerException {

        FolderImpl folder = root instanceof FolderImpl ? (FolderImpl) root : null;
        if (folder != null)
            for (HierarchyItemImpl child : folder.getChildren()) {
                if (child.itemHasLock(skipShared))
                    mr.addResponse(child.getPath(), WebDavStatus.LOCKED);
                checkNoItemsLocked(mr, child, skipShared);
            }
    }

Lock Parameters

The shared argument specifies if client is requesting an exclusive or shared lock. If user set exclusive lock other users will not be able to set any locks. If user set shared lock other users will be able to set only shared lock on the item. There could be only 1 exclusive lock set on an item or it can have 1 or more shared locks. If the item is locked and the new lock can not be applied you can throw LockedException, this will inform the client about lock conflict on server.

The deep argument specifies if the lock should be applied only to this item or to the entire subtree. In case of deep lock (no matter shared or exclusive) you must verify if no exclusive lock is applied to any child item. In case of deep exclusive lock you must verify if no shared lock is applied to any child item. If any lock conflicts occurs, to provide WebDAV client with information about locked children, you can throw MultistatusException with information about each locked child item.

The lock must be automatically removed by server after amount of time specified in timeout parameter are elapsed. Negative value means infinite lock, that should not be remover automatically. You can set the timeout that is different from the one requested by the client. The actual timeout that you apply must be returned to the framework via LockResult return value together with the generated lock-token. The WebDAV framework will than return the updated timeout to WebDAV client.

The owner argument specifies the name of the user applying the lock. Note that this is only an informational sting provided by WebDAV client and it cannot be used for security purposes. To get the name of authenticated user you must use other mechanisms.

Refreshing Lock

The WebDAV client can change the lock timeout at later times. In this case framework will call Lock.refreshLock method providing the lock-token and new timeout value. Again in this method you can set the timeout that is different from the one requested by client.

Updating the Locked Item

When WebDAV client is updating any server item it sends to server the list of lock-tokens. You can access these tokens on server side via Engine.getClientLockTokens method call. In your WebDAV server Class 2 / Class 3 implementation before modifying any locked items you must check if WebDAV client provided necessary lock-token for this item. If no valid lock-token was provided you must throw LockedException.

public void createFolder(String name) throws LockedException, ServerException {
    if (!clientHasToken())
        throw new LockedException();
...
}
public boolean clientHasToken() throws ServerException {

    List<LockInfo> itemLocks = getActiveLocks();
    if (itemLocks.size() == 0)
        return true;
    List<String> clientLockTokens = getEngine().getClientLockTokens(getEngine().getRequest());
    for (String clientLockToken : clientLockTokens)
        for (LockInfo itemLock : itemLocks)
            if (clientLockToken.equals(itemLock.getToken()))
                return true;
    return false;
}

Listing Locks

The WebDAV client application can request the list of locks applied to the item. In this case framework calls Lock.getActiveLocks() method:

public List<LockInfo> getActiveLocks() throws ServerException {
    int itemId = getId();
    ArrayList<LockInfo> l = new ArrayList<LockInfo>();

    l.addAll(getLocks(getId(), false)); // get all locks
    while (true) {
        Integer res = getDataAccess().executeInt("SELECT Parent FROM Repository WHERE ID = ?", itemId);
        if (res == null)
            break;
        itemId = res;
        if (itemId <= 0)
            break;
        l.addAll(getLocks(itemId, true));  // get only deep locks
    }

    return l;
}

 

private List<LockInfo> getLocks(int itemId, boolean onlyDeep) throws ServerException {
    if (onlyDeep)
        return getDataAccess().readLocks("SELECT Token, Shared, Deep, Expires, Owner"
                + " FROM Locks"
                + " WHERE ItemID = ?"
                + " AND Deep = ?", itemId, true);
    else
        return getDataAccess().readLocks("SELECT Token, Shared, Deep, Expires, Owner FROM Locks WHERE ItemID = ?", itemId);
}


What WebDAV software would you like to have?

Selected Customers:
Country: Norway
DnB NOR Group
Country: Finland
Bank of Finland
Country: United Kingdom
Bechtle Direct
Country: Sweden
BT Industries
Country: USA
California Chamber of Commerce
Country: Denmark
Danfoss Group
Country: Denmark
DFDS
Country: USA
Fluke Networks
Country: USA
HNI Corporation
Country: USA
IHS Inc
Country: USA
LandAmerica Financial Group
Country: Canada
Laurentian University
Country: USA
Microsoft
Country: Israel
RADVISION
Country: Ukraine
Raiffeisen Bank
Country: Netherlands
Sanoma Uitgevers
Country: USA
Siemens
Country: Australia
WorkCover NSW
Country: Ukraine
OTP Bank
Country: USA
Intel Corporation
Country: Austria
Austrian Federal Railways
Home .NET Server Java Server .NET Client AJAX Client AJAX Browser Map Drive Pricing Contacts

Updated: Wednesday, March 11, 2009