Skip to content

Commit

Permalink
Support MOVE
Browse files Browse the repository at this point in the history
  • Loading branch information
justinsb committed Dec 20, 2013
1 parent bbfb919 commit 4f10248
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 29 deletions.
20 changes: 20 additions & 0 deletions cloudata-files/src/main/java/com/cloudata/files/fs/FsClient.java
Expand Up @@ -251,6 +251,7 @@ private void createNewEntry(FsPath parent, ByteString name, InodeData.Builder in
ByteString dirEntryValue = ByteStrings.encode(created.getInode());
Modifier[] modifiers;
if (overwrite) {
// TODO: Should we record any existing key in the deleted section?
modifiers = new Modifier[] {};
} else {
modifiers = new Modifier[] { IfNotExists.INSTANCE };
Expand Down Expand Up @@ -343,4 +344,23 @@ public boolean delete(FsPath fsPath) throws IOException {
return store.delete(key);
}
}

public void move(FsPath fsPath, FsPath newParentFsPath, ByteString newName) throws IOException {
long id = fsPath.getId();

ByteString oldKey = buildDirEntryKey(fsPath);
ByteString newKey = buildDirEntryKey(newParentFsPath, newName);

// Create the new dir entry, delete the old one
{
ByteString dirEntryValue = ByteStrings.encode(id);
Modifier[] modifiers = new Modifier[] { IfNotExists.INSTANCE };

if (!store.put(newKey, dirEntryValue, modifiers)) {
throw new FsFileAlreadyExistsException();
}
}

store.delete(oldKey);
}
}
27 changes: 27 additions & 0 deletions cloudata-files/src/main/java/com/cloudata/files/fs/FsVolume.java
Expand Up @@ -28,4 +28,31 @@ public static FsVolume fromHost(String host) {
public ByteString getPrefix() {
return prefix;
}

@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (int) (key ^ (key >>> 32));
return result;
}

@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
FsVolume other = (FsVolume) obj;
if (key != other.key) {
return false;
}
return true;
}

}
Expand Up @@ -48,7 +48,6 @@ public HttpObject doAction(FsPath fsPath) throws Exception {
// TODO: Handle ETAG??

// If-Modified-Since Validation

String ifModifiedSince = request.getHeader(HttpHeaders.Names.IF_MODIFIED_SINCE);
if (!Strings.isNullOrEmpty(ifModifiedSince)) {
Date ifModifiedSinceDate;
Expand All @@ -63,7 +62,7 @@ public HttpObject doAction(FsPath fsPath) throws Exception {
long ifModifiedSinceDateSeconds = ifModifiedSinceDate.getTime() / 1000;
long fileLastModifiedSeconds = fsPath.getModified() / 1000;
if (ifModifiedSinceDateSeconds == fileLastModifiedSeconds) {
// throw new WebdavResponseException(HttpResponseStatus.NOT_MODIFIED);
throw new WebdavResponseException(HttpResponseStatus.NOT_MODIFIED);
}
}

Expand Down
Expand Up @@ -14,7 +14,6 @@
import java.io.FileNotFoundException;
import java.net.URI;
import java.util.Calendar;
import java.util.Collections;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.concurrent.Callable;
Expand All @@ -37,7 +36,6 @@
import com.google.common.base.Charsets;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.AsyncFunction;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
Expand Down Expand Up @@ -255,28 +253,7 @@ public FsPath call() throws Exception {
String host = "";
String path = resolvePath;

List<String> pathTokens;

if (path.equals("") || path.equals("/")) {
pathTokens = Collections.emptyList();
} else {
if (path.contains("//")) {
path = path.replace("//", "/");
}

if (path.startsWith("/")) {
path = path.substring(1);
}

if (path.endsWith("/")) {
path = path.substring(0, path.length() - 1);
}

pathTokens = Lists.newArrayList();
for (String token : Splitter.on('/').split(path)) {
pathTokens.add(Urls.decodeComponent(token));
}
}
List<String> pathTokens = Urls.pathToTokens(path);

FsVolume volume = FsVolume.fromHost(host);
return getFsClient().resolve(volume, credentials, pathTokens);
Expand Down
@@ -0,0 +1,66 @@
package com.cloudata.files.webdav;

import io.netty.handler.codec.http.HttpObject;
import io.netty.handler.codec.http.HttpResponseStatus;

import java.io.FileNotFoundException;
import java.net.URI;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.cloudata.files.fs.FsCredentials;
import com.cloudata.files.fs.FsPath;
import com.cloudata.files.fs.FsVolume;
import com.google.common.base.Strings;
import com.google.protobuf.ByteString;

public class MoveHandler extends MethodHandler {

private static final Logger log = LoggerFactory.getLogger(MoveHandler.class);

public MoveHandler(WebdavRequestHandler requestHandler) {
super(requestHandler);
}

@Override
protected HttpObject doAction(FsPath fsPath) throws Exception {
WebdavRequest request = getRequest();

String destination = request.getHeader("Destination");
if (Strings.isNullOrEmpty(destination)) {
throw new IllegalArgumentException();
}

String newName;
FsPath newParentFsPath;

{
URI uri = URI.create(destination);
String path = uri.getPath();

String parentPath = Urls.getParentPath(path);
List<String> pathTokens = Urls.pathToTokens(parentPath);
newName = Urls.getLastPathComponent(path, true);

FsVolume volume = fsPath.getVolume();
if (!volume.equals(FsVolume.fromHost(uri.getHost()))) {
log.error("Move attempted between different volumes: {} {}", getUri(), uri);
throw new IllegalArgumentException();
}
FsCredentials credentials = request.getCredentials();

newParentFsPath = getFsClient().resolve(volume, credentials, pathTokens);
}

if (newParentFsPath == null) {
throw new FileNotFoundException();
}

getFsClient().move(fsPath, newParentFsPath, ByteString.copyFromUtf8(newName));

return buildFullResponse(HttpResponseStatus.CREATED);
}

}
30 changes: 30 additions & 0 deletions cloudata-files/src/main/java/com/cloudata/files/webdav/Urls.java
Expand Up @@ -2,8 +2,12 @@

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Collections;
import java.util.List;

import com.google.common.base.Splitter;
import com.google.common.base.Throwables;
import com.google.common.collect.Lists;

public class Urls {

Expand Down Expand Up @@ -52,4 +56,30 @@ public static String decodeComponent(String component) {
}
}

public static List<String> pathToTokens(String path) {
List<String> pathTokens;

if (path.equals("") || path.equals("/")) {
pathTokens = Collections.emptyList();
} else {
if (path.contains("//")) {
path = path.replace("//", "/");
}

if (path.startsWith("/")) {
path = path.substring(1);
}

if (path.endsWith("/")) {
path = path.substring(0, path.length() - 1);
}

pathTokens = Lists.newArrayList();
for (String token : Splitter.on('/').split(path)) {
pathTokens.add(Urls.decodeComponent(token));
}
}
return pathTokens;
}

}
Expand Up @@ -94,10 +94,10 @@ public ListenableFuture<HttpObject> processRequest() throws Exception {
handler = new PutHandler(this);
} else if (request.getMethod() == HttpMethod.DELETE) {
handler = new DeleteHandler(this);
} else if (request.getMethod().name().equals("PROPPATCH")) {
throw new UnsupportedOperationException();
} else if (request.getMethod().name().equals("MOVE")) {
handler = new MoveHandler(this);
} else {
log.warn("Method not allowed: " + request.getMethod());
log.warn("Method not allowed: {}", request);

throw new WebdavResponseException(HttpResponseStatus.METHOD_NOT_ALLOWED);
}
Expand Down

0 comments on commit 4f10248

Please sign in to comment.