Permalink
Browse files

Basic LOCK/UNLOCK support for WebDAV

  • Loading branch information...
justinsb committed Dec 20, 2013
1 parent f668043 commit 928d41f5e176708cf312067651d2f21bec55fcc4
@@ -3,6 +3,5 @@
import java.util.concurrent.locks.Lock;
public interface CloudLock extends Lock {
// CloudLockToken getLockToken();
CloudLockToken getLockToken();
}
@@ -3,8 +3,6 @@
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import javax.inject.Singleton;
@@ -22,7 +20,7 @@ public Lockable(String key) {
this.key = key;
}
synchronized InMemoryLockToken lock() {
synchronized InMemoryLockToken tryLock() {
if (!locks.isEmpty()) {
return null;
}
@@ -33,106 +31,122 @@ synchronized InMemoryLockToken lock() {
return lock;
}
synchronized InMemoryLockToken unlock(String lockToken) {
synchronized boolean unlock(String lockToken) {
for (int i = 0; i < locks.size(); i++) {
InMemoryLockToken lock = locks.get(i);
if (lockToken.equals(lock.getLockToken())) {
if (lockToken.equals(lock.getTokenId())) {
locks.remove(i);
return lock;
return true;
}
}
return null;
return false;
}
synchronized InMemoryLockToken find(String lockToken) {
for (int i = 0; i < locks.size(); i++) {
InMemoryLockToken lock = locks.get(i);
if (lockToken.equals(lock.getLockToken())) {
if (lockToken.equals(lock.getTokenId())) {
return lock;
}
}
return null;
}
}
/**
* The lock implements the Java Lock. It is a lockable, not a held lock!
*
*/
static class InMemoryLock implements CloudLock {
final Lockable lockable;
public InMemoryLock(Lockable lockable) {
this.lockable = lockable;
}
@Override
public void lock() {
throw new UnsupportedOperationException();
}
@Override
public void lockInterruptibly() throws InterruptedException {
throw new UnsupportedOperationException();
}
@Override
public boolean tryLock() {
throw new UnsupportedOperationException();
}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
throw new UnsupportedOperationException();
}
@Override
public void unlock() {
throw new UnsupportedOperationException();
}
@Override
public Condition newCondition() {
throw new UnsupportedOperationException();
}
}
// /**
// * The lock implements the Java Lock. It is a lockable, not a held lock!
// *
// */
// static class InMemoryLock implements CloudLock {
// final Lockable lockable;
//
// public InMemoryLock(Lockable lockable) {
// this.lockable = lockable;
// }
//
// @Override
// public void lock() {
// throw new UnsupportedOperationException();
// }
//
// @Override
// public void lockInterruptibly() throws InterruptedException {
// throw new UnsupportedOperationException();
// }
//
// @Override
// public boolean tryLock() {
// throw new UnsupportedOperationException();
// }
//
// @Override
// public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
// throw new UnsupportedOperationException();
// }
//
// @Override
// public void unlock() {
// throw new UnsupportedOperationException();
// }
//
// @Override
// public Condition newCondition() {
// throw new UnsupportedOperationException();
// }
//
// @Override
// public CloudLockToken getLockToken() {
// throw new UnsupportedOperationException();
// }
//
// }
/**
* A LockToken represents a hold on a lock.
*
*/
static class InMemoryLockToken {
static class InMemoryLockToken implements CloudLockToken {
final Lockable lock;
final String token;
final String tokenId;
public InMemoryLockToken(Lockable lock, String token) {
public InMemoryLockToken(Lockable lock, String tokenId) {
this.lock = lock;
this.token = token;
this.tokenId = tokenId;
}
public String getLockToken() {
return token;
@Override
public String getTokenId() {
return tokenId;
}
}
final Map<String, InMemoryLock> locks = Maps.newHashMap();
final Map<String, Lockable> locks = Maps.newHashMap();
@Override
public InMemoryLock get(String key) {
InMemoryLock lockset;
private Lockable getLockable(String key) {
Lockable lockset;
synchronized (locks) {
lockset = locks.get(key);
if (lockset == null) {
lockset = new InMemoryLock(new Lockable(key));
lockset = new Lockable(key);
locks.put(key, lockset);
}
}
return lockset;
}
@Override
public CloudLockToken tryLock(String key) {
return getLockable(key).tryLock();
}
@Override
public boolean unlock(String key, String lockToken) {
return getLockable(key).unlock(lockToken);
}
// @Override
// public InMemoryLockToken lock(String uri) {
// LockSet lockset;
@@ -1,12 +1,9 @@
package com.cloudata.files.locks;
public interface LockService {
CloudLock get(String key);
// CloudLockToken lock(String key);
//
// CloudLockToken unlock(String key, String lockToken);
//
// CloudLockToken findLock(String key, String lockToken);
CloudLockToken tryLock(String key);
boolean unlock(String key, String lockToken);
}
@@ -0,0 +1,54 @@
package com.cloudata.files.webdav;
import io.netty.handler.codec.http.HttpObject;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpResponseStatus;
import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.cloudata.files.fs.FsPath;
import com.cloudata.files.locks.CloudLockToken;
import com.cloudata.files.locks.LockService;
import com.cloudata.files.webdav.model.LockRequest;
import com.cloudata.files.webdav.model.LockResponse;
public class LockHandler extends MethodHandler {
private static final Logger log = LoggerFactory.getLogger(LockHandler.class);
public static final String LOCK_PREFIX = "opaquelocktoken:";
public LockHandler(WebdavRequestHandler requestHandler) {
super(requestHandler);
}
@Override
protected HttpObject doAction(FsPath fsPath) throws Exception {
LockService lockService = getLockService();
WebdavRequest request = getRequest();
LockRequest lockRequest;
try {
lockRequest = new LockRequest(request);
} catch (IOException e) {
log.warn("Error parsing request", e);
throw new IllegalArgumentException();
}
log.debug("LockRequest: " + lockRequest);
CloudLockToken lockToken = lockService.tryLock(request.getUri());
if (lockToken == null) {
throw new WebdavResponseException(HttpResponseStatus.LOCKED);
}
String href = LOCK_PREFIX + lockToken.getTokenId();
LockResponse lockResponse = new LockResponse(href);
HttpResponse response = buildXmlResponse(HttpResponseStatus.OK, lockResponse);
response.headers().set("Lock-Token", "<" + href + ">");
return response;
}
}
@@ -30,6 +30,7 @@
import com.cloudata.files.fs.FsCredentials;
import com.cloudata.files.fs.FsPath;
import com.cloudata.files.fs.FsVolume;
import com.cloudata.files.locks.LockService;
import com.cloudata.files.webdav.chunks.ChunkAccumulator;
import com.cloudata.files.webdav.model.WebdavXmlWriter;
import com.cloudata.files.webdav.model.XmlSerializable;
@@ -95,12 +96,19 @@ protected boolean requiresValidPath() {
protected abstract HttpObject doAction(FsPath fsPath) throws Exception;
protected LockService getLockService() {
return this.requestHandler.webdav.lockService;
}
protected ListenableFuture<HttpObject> doActionAsync(FsPath fsPath) throws Exception {
return Futures.immediateFuture(doAction(fsPath));
}
private boolean checkIfHeader(String ifHeader) {
throw new UnsupportedOperationException();
// (<opaquelocktoken:6de84ba6-7063-477b-9ca6-a246ce69ed8b>)
//
// // See: http://www.webdav.org/specs/rfc2518.html#HEADER_If
// log.warn("If-Header parsing is totally stubbed out");
@@ -0,0 +1,49 @@
package com.cloudata.files.webdav;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpObject;
import io.netty.handler.codec.http.HttpResponseStatus;
import com.cloudata.files.fs.FsPath;
import com.cloudata.files.locks.LockService;
import com.google.common.base.Strings;
public class UnlockHandler extends MethodHandler {
public UnlockHandler(WebdavRequestHandler requestHandler) {
super(requestHandler);
}
@Override
protected HttpObject doAction(FsPath fsPath) throws Exception {
LockService lockService = getLockService();
WebdavRequest request = getRequest();
String lockToken = request.getHeader("Lock-Token");
if (Strings.isNullOrEmpty(lockToken)) {
throw new IllegalArgumentException("Lock-Token is required");
}
lockToken = lockToken.trim();
if (lockToken.startsWith("<")) {
lockToken = lockToken.substring(1).trim();
}
if (lockToken.startsWith(LockHandler.LOCK_PREFIX)) {
lockToken = lockToken.substring(LockHandler.LOCK_PREFIX.length()).trim();
}
if (lockToken.endsWith(">")) {
lockToken = lockToken.substring(0, lockToken.length() - 1);
}
boolean unlocked = lockService.unlock(request.getUri(), lockToken);
if (!unlocked) {
throw new IllegalArgumentException("Lock not found");
}
FullHttpResponse response = buildFullResponse(HttpResponseStatus.NO_CONTENT);
return response;
}
}
Oops, something went wrong.

0 comments on commit 928d41f

Please sign in to comment.