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;
}
}