package com.neverwinterdp.registry.lock; import java.util.List; import java.util.SortedSet; import java.util.TreeSet; import com.neverwinterdp.registry.BatchOperations; import com.neverwinterdp.registry.ErrorCode; import com.neverwinterdp.registry.Node; import com.neverwinterdp.registry.NodeCreateMode; import com.neverwinterdp.registry.Registry; import com.neverwinterdp.registry.RegistryException; import com.neverwinterdp.registry.event.NodeEvent; import com.neverwinterdp.registry.event.NodeWatcher; public class Lock { private Registry registry ; private String lockDir ; private String name ; private LockId lockId ; public Lock(Registry registry, String dir, String name) { this.registry = registry; this.lockDir = dir ; this.name = name ; } public Registry getRegistry() { return this.registry ; } public String getName() { return this.name ; } public String getLockDir() { return this.lockDir ; } public LockId lock(long timeout) throws RegistryException { if(lockId != null) { throw new RegistryException(ErrorCode.Unknown, "The lock is already created") ; } String lockPath = lockDir + "/" + name + "-" + registry.getSessionId() + "-" ; Node node = registry.create(lockPath , new byte[0], NodeCreateMode.EPHEMERAL_SEQUENTIAL); lockId = new LockId(node.getPath()) ; SortedSet<LockId> currentLockIds = getSortedLockIds() ; LockId ownerId = currentLockIds.first() ; if(ownerId.equals(lockId)) { return lockId ; } else { LockWatcher watcher = new LockWatcher(timeout) ; watcher.watch(currentLockIds); watcher.waitForLock(); } return lockId ; } public void unlock() throws RegistryException { if(lockId == null) return ; registry.delete(lockId.getPath()); lockId = null ; } public <T> T execute(BatchOperations<T> op, int retry, long timeoutThreshold) throws RegistryException { for(int i = 0;i < retry; i++) { try { lock(timeoutThreshold * (i + 1)) ; T result = op.execute(registry); unlock(); return result; } catch (RegistryException e) { if(e.getErrorCode() != ErrorCode.Timeout) { throw e; } } catch (Exception e) { throw new RegistryException(ErrorCode.Unknown, e); } } throw new RegistryException(ErrorCode.Unknown, "Fail after " + retry + "tries"); } private SortedSet<LockId> getSortedLockIds() throws RegistryException { List<String> names = registry.getChildren(lockDir) ; SortedSet<LockId> sortedLockIds = new TreeSet<LockId>(); for (String nodeName : names) { if(nodeName.startsWith(this.name)) { sortedLockIds.add(new LockId(lockDir + "/" + nodeName)); } } return sortedLockIds; } class LockWatcher extends NodeWatcher { long startTime; long timeout; boolean obtainedLock = false; public LockWatcher(long timeout) { this.startTime = System.currentTimeMillis(); this.timeout = timeout ; } @Override public void onEvent(NodeEvent event) { try { if(event.getType() != NodeEvent.Type.DELETE) { return ; } SortedSet<LockId> currentLockIds = getSortedLockIds() ; LockId ownerId = currentLockIds.first() ; if(ownerId.equals(lockId)) { synchronized(this) { obtainedLock = true ; notifyAll() ; } return ; } else { watch(currentLockIds); } } catch(RegistryException ex) { throw new RuntimeException(ex) ; } } public void watch(SortedSet<LockId> currentLockIds) throws RegistryException { SortedSet<LockId> lessThanMe = currentLockIds.headSet(lockId); LockId previousLock = lessThanMe.last(); registry.watchExists(previousLock.getPath(), this); } public void waitForLock() throws RegistryException { synchronized(this) { try { wait(timeout - (System.currentTimeMillis() - startTime)); } catch (InterruptedException e) { throw new RegistryException(ErrorCode.Timeout, e) ; } if(!obtainedLock) { //check again SortedSet<LockId> currentLockIds = getSortedLockIds() ; LockId ownerId = currentLockIds.first() ; if(ownerId.equals(lockId)) { obtainedLock = true; } else { String lockIdPath = lockId.getPath(); registry.delete(lockIdPath); lockId = null; throw new RegistryException(ErrorCode.Timeout, "Cannot obtain a lock at " + lockIdPath + " after " + timeout + "ms") ; } } } } } }