package com.ejie.x38.webdav.locking; import java.util.UUID; /** * a helper class for ResourceLocks, represents the Locks * * @author re * */ public class MemoryLockedObject extends LockedObject<MemoryResourceLocks> { /** * Describing the depth of a locked collection. If the locked resource is * not a collection, depth is 0 / doesn't matter. */ protected int _lockDepth; /** * Describing the timeout of a locked object (ms) */ protected long _expiresAt; /** * owner of the lock. shared locks can have multiple owners. is null if no * owner is present */ // protected String[] _owner = null; protected String[] _owner = null; /** * children of that lock */ protected MemoryLockedObject[] _children = null; protected MemoryLockedObject _parent = null; /** * weather the lock is exclusive or not. if owner=null the exclusive value * doesn't matter */ protected boolean _exclusive = false; /** * weather the lock is a write or read lock */ protected String _type = null; /** * @param resLocks * the resourceLocks where locks are stored * @param path * the path to the locked object * @param temporary * indicates if the LockedObject should be temporary or not */ public MemoryLockedObject(MemoryResourceLocks resLocks, String path, boolean temporary) { _path = path; _id = UUID.randomUUID().toString(); _resourceLocks = resLocks; if (!temporary) { _resourceLocks.get_locks().put(path, this); _resourceLocks.get_locksByID().put(_id, this); } else { _resourceLocks.get_tempLocks().put(path, this); _resourceLocks.get_tempLocksByID().put(_id, this); } _resourceLocks.addCleanupCounter(); } /** * adds a new owner to a lock * * @param owner * string that represents the owner * @return true if the owner was added, false otherwise */ public boolean addLockedObjectOwner(String owner) { if (_owner == null) { _owner = new String[1]; } else { int size = _owner.length; String[] newLockObjectOwner = new String[size + 1]; // check if the owner is already here (that should actually not // happen) for (int i = 0; i < size; i++) { if (_owner[i].equals(owner)) { return false; } } System.arraycopy(_owner, 0, newLockObjectOwner, 0, size); _owner = newLockObjectOwner; } _owner[_owner.length - 1] = owner; return true; } /** * tries to remove the owner from the lock * * @param owner * string that represents the owner */ public void removeLockedObjectOwner(String owner) { try { if (_owner != null) { int size = _owner.length; for (int i = 0; i < size; i++) { // check every owner if it is the requested one if (_owner[i].equals(owner)) { // remove the owner String[] newLockedObjectOwner = new String[size - 1]; for (int j = 0; j < (size - 1); j++) { if (j < i) { newLockedObjectOwner[j] = _owner[j]; } else { newLockedObjectOwner[j] = _owner[j + 1]; } } _owner = newLockedObjectOwner; } } if (_owner.length == 0) { _owner = null; } } } catch (ArrayIndexOutOfBoundsException e) { System.out.println("LockedObject.removeLockedObjectOwner()"); System.out.println(e.toString()); } } /** * adds a new child lock to this lock * * @param newChild * new child */ public void addChild(MemoryLockedObject newChild) { if (_children == null) { _children = new MemoryLockedObject[0]; } int size = _children.length; MemoryLockedObject[] newChildren = new MemoryLockedObject[size + 1]; System.arraycopy(_children, 0, newChildren, 0, size); newChildren[size] = newChild; _children = newChildren; } /** * deletes this Lock object. assumes that it has no children and no owners * (does not check this itself) * */ public void removeLockedObject() { if (this != _resourceLocks._root && !this.getPath().equals("/")) { int size = _parent._children.length; for (int i = 0; i < size; i++) { if (_parent._children[i].equals(this)) { MemoryLockedObject[] newChildren = new MemoryLockedObject[size - 1]; for (int i2 = 0; i2 < (size - 1); i2++) { if (i2 < i) { newChildren[i2] = _parent._children[i2]; } else { newChildren[i2] = _parent._children[i2 + 1]; } } if (newChildren.length != 0) { _parent._children = newChildren; } else { _parent._children = null; } break; } } // removing from hashtable _resourceLocks._locksByID.remove(getID()); _resourceLocks._locks.remove(getPath()); // now the garbage collector has some work to do } } /** * deletes this Lock object. assumes that it has no children and no owners * (does not check this itself) * */ public void removeTempLockedObject() { if (this != _resourceLocks._tempRoot) { // removing from tree if (_parent != null && _parent._children != null) { int size = _parent._children.length; for (int i = 0; i < size; i++) { if (_parent._children[i].equals(this)) { MemoryLockedObject[] newChildren = new MemoryLockedObject[size - 1]; for (int i2 = 0; i2 < (size - 1); i2++) { if (i2 < i) { newChildren[i2] = _parent._children[i2]; } else { newChildren[i2] = _parent._children[i2 + 1]; } } if (newChildren.length != 0) { _parent._children = newChildren; } else { _parent._children = null; } break; } } // removing from hashtable _resourceLocks._tempLocksByID.remove(getID()); _resourceLocks._tempLocks.remove(getPath()); // now the garbage collector has some work to do } } } /** * checks if a lock of the given exclusivity can be placed, only considering * children up to "depth" * * @param exclusive * wheather the new lock should be exclusive * @param depth * the depth to which should be checked * @return true if the lock can be placed */ public boolean checkLocks(boolean exclusive, int depth) { if (checkParents(exclusive) && checkChildren(exclusive, depth)) { return true; } return false; } /** * helper of checkLocks(). looks if the parents are locked * * @param exclusive * wheather the new lock should be exclusive * @return true if no locks at the parent path are forbidding a new lock */ protected boolean checkParents(boolean exclusive) { if (_path.equals("/")) { return true; } else { if (_owner == null) { // no owner, checking parents return _parent != null && _parent.checkParents(exclusive); } else { // there already is a owner return !(_exclusive || exclusive) && _parent.checkParents(exclusive); } } } /** * helper of checkLocks(). looks if the children are locked * * @param exclusive * wheather the new lock should be exclusive * @return true if no locks at the children paths are forbidding a new lock * @param depth * depth */ protected boolean checkChildren(boolean exclusive, int depth) { if (_children == null) { // a file return _owner == null || !(_exclusive || exclusive); } else { // a folder if (_owner == null) { // no owner, checking children if (depth != 0) { boolean canLock = true; int limit = _children.length; for (int i = 0; i < limit; i++) { if (!_children[i].checkChildren(exclusive, depth - 1)) { canLock = false; } } return canLock; } else { // depth == 0 -> we don't care for children return true; } } else { // there already is a owner return !(_exclusive || exclusive); } } } /** * Sets a new timeout for the LockedObject * * @param timeout */ public void refreshTimeout(int timeout) { _expiresAt = System.currentTimeMillis() + (timeout * 1000); } /** * Gets the timeout for the LockedObject * * @return timeout */ public long getTimeoutMillis() { return (_expiresAt - System.currentTimeMillis()); } /** * Return true if the lock has expired. * * @return true if timeout has passed */ public boolean hasExpired() { if (_expiresAt != 0) { return (System.currentTimeMillis() > _expiresAt); } else { return true; } } /** * Gets the LockID (locktoken) for the LockedObject * * @return locktoken */ public String getID() { return _id; } /** * Gets the owners for the LockedObject * * @return owners */ public String[] getOwner() { return _owner; } /** * Gets the path for the LockedObject * * @return path */ public String getPath() { return _path; } /** * Sets the exclusivity for the LockedObject * * @param exclusive */ public void setExclusive(boolean exclusive) { _exclusive = exclusive; } /** * Gets the exclusivity for the LockedObject * * @return exclusivity */ public boolean isExclusive() { return _exclusive; } /** * Gets the exclusivity for the LockedObject * * @return exclusivity */ public boolean isShared() { return !_exclusive; } /** * Gets the type of the lock * * @return type */ public String getType() { return _type; } /** * Gets the depth of the lock * * @return depth */ public int getLockDepth() { return _lockDepth; } }