commit - 4dc61f92ad9f3fa09122a917118cb69c45840845
commit + c69f66ef9a0647aa8c88102bf4d4ff9eaa4c2d65
blob - 7028744f430a3e18a81939ea3f859c9b5e991eb1
blob + b50dc5fadc645fab21c1f92e533ec272bcb8d346
--- NOTICE
+++ NOTICE
=========================================================================
Moxo S3 DAV Proxy Server
-Copyright 2007 Matthias L. Jugel.
+Copyright 2007, 2009 Matthias L. Jugel.
This product includes software developed at
blob - 7cdf942f8bdb14058f151c877fbf6464d0769b03
blob + cc9de27a8bca90a735b26b36d7a4451b76a6ce78
--- README
+++ README
Moxo S3 DAV Proxy Server
-Copyright 2007 Matthias L. Jugel. See LICENSE for details.
+Copyright 2007, 2009 Matthias L. Jugel. See LICENSE for details.
http://thinkberg.com/
This is a first go on two issues:
- Create an executable JAR with all required libraries. The Main is already prepared to do
that but I have not yet fully understood how to get maven to package the jars right next
to the compiled classes.
+- implement a good way to handle ETags for webdav (using vfs)
- WebDAV property handling
- S3 ACL support
- separated jar packages for the vfs backend and the dav frontend
blob - 1582d815464b5a6997af06d36a8f835f6544238a
blob + 7ab330241c049a148fc7a2505e2460890c3acc73
--- pom.xml
+++ pom.xml
</dependency>
</dependencies>
<build>
+ <resources>
+ <resource>
+ <directory>src/main/resources</directory>
+ </resource>
+ </resources>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
+ <verbose>true</verbose>
+ <fork>true</fork>
<source>1.5</source>
<target>1.5</target>
</configuration>
</plugin>
+
+ <!--
+ mvn test
+ Documentation: http://maven.apache.org/plugins/maven-surefire-plugin/
+ -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <includes>
+ <include>**/MoxoTest.java</include>
+ </includes>
+ </configuration>
+ </plugin>
+
+ <!--
+ jar
+ mvn package
+ Documentation: http://maven.apache.org/plugins/maven-jar-plugin/
+ -->
+ <!--<plugin>-->
+ <!--<groupId>org.apache.maven.plugins</groupId>-->
+ <!--<artifactId>maven-jar-plugin</artifactId>-->
+ <!--<executions>-->
+ <!--<execution>-->
+ <!--<phase>package</phase>-->
+ <!--<goals>-->
+ <!--<goal>jar</goal>-->
+ <!--</goals>-->
+ <!--<configuration>-->
+ <!--<classifier>client</classifier>-->
+ <!--<includes>-->
+ <!--<include>**/client/*</include>-->
+ <!--<include>**/servlet/*</include>-->
+ <!--<include>barcode-client.properties</include>-->
+ <!--<include>log4j.properties</include>-->
+ <!--<include>images/*</include>-->
+ <!--<include>templates/*</include>-->
+ <!--</includes>-->
+ <!--</configuration>-->
+ <!--</execution>-->
+ <!--</executions>-->
+ <!--</plugin>-->
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
blob - 12c455d7b8e0da5274b892269a2919198c3cace9
blob + 3e03b367c7a948895653aafa48ac0d07ee118c5f
--- src/main/java/com/thinkberg/moxo/dav/CopyMoveBase.java
+++ src/main/java/com/thinkberg/moxo/dav/CopyMoveBase.java
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
+import java.text.ParseException;
/**
* @author Matthias L. Jugel
FileObject object = getVFSObject(request.getPathInfo());
FileObject targetObject = getDestination(request);
+
try {
- // check that we can write the target
- LockManager.getInstance().checkCondition(targetObject, getIf(request));
- // if we move, check that we can actually write on the source
+ final LockManager lockManager = LockManager.getInstance();
+ LockManager.EvaluationResult evaluation = lockManager.evaluateCondition(targetObject, getIf(request));
+ if (!evaluation.result) {
+ response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
+ return;
+ }
if ("MOVE".equals(request.getMethod())) {
- LockManager.getInstance().checkCondition(object, getIf(request));
+ evaluation = lockManager.evaluateCondition(object, getIf(request));
+ if (!evaluation.result) {
+ response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
+ return;
+ }
}
} catch (LockException e) {
- if (e.getLocks() != null) {
- response.sendError(SC_LOCKED);
- } else {
- response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
- }
+ response.sendError(SC_LOCKED);
return;
+ } catch (ParseException e) {
+ response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
+ return;
}
blob - bd7f9830c75e3fb90449db9a7f2685aeeff2e237
blob + 1cd9fda42df8a35bd14e6be5cd93938a0344a9ce
--- src/main/java/com/thinkberg/moxo/dav/DeleteHandler.java
+++ src/main/java/com/thinkberg/moxo/dav/DeleteHandler.java
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
+import java.text.ParseException;
/**
* @author Matthias L. Jugel
}
try {
- LockManager.getInstance().checkCondition(object, getIf(request));
- } catch (LockException e) {
- if (e.getLocks() != null) {
- response.sendError(SC_LOCKED);
- } else {
+ if (!LockManager.getInstance().evaluateCondition(object, getIf(request)).result) {
response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
+ return;
}
+ } catch (LockException e) {
+ response.sendError(WebdavHandler.SC_LOCKED);
return;
+ } catch (ParseException e) {
+ response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
+ return;
}
if (object.exists()) {
blob - 2d54a47a9b4dc425e1300cefea568d7718c7f90f
blob + 2a0d7c02a9d33230c3492d40a0a444e5c8d0efb1
--- src/main/java/com/thinkberg/moxo/dav/GetHandler.java
+++ src/main/java/com/thinkberg/moxo/dav/GetHandler.java
void setHeader(HttpServletResponse response, FileContent content) throws FileSystemException {
response.setHeader("Last-Modified", Util.getDateString(content.getLastModifiedTime()));
response.setHeader("Content-Type", content.getContentInfo().getContentType());
+ response.setHeader("ETag", String.format("%x", content.getFile().hashCode()));
}
blob - d4223a80e73f36560895ea20948921ce11bb8739
blob + 2cf6621edb3bdded57a86109a03ee75a17d97cfc
--- src/main/java/com/thinkberg/moxo/dav/LockHandler.java
+++ src/main/java/com/thinkberg/moxo/dav/LockHandler.java
import com.thinkberg.moxo.dav.lock.Lock;
import com.thinkberg.moxo.dav.lock.LockConflictException;
-import com.thinkberg.moxo.dav.lock.LockException;
import com.thinkberg.moxo.dav.lock.LockManager;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URL;
+import java.text.ParseException;
import java.util.Iterator;
+import java.util.List;
/**
* Handle WebDAV LOCK requests.
FileObject object = getVFSObject(request.getPathInfo());
try {
- Lock lock = LockManager.getInstance().checkCondition(object, getIf(request));
- if (lock != null) {
- sendLockAcquiredResponse(response, lock);
+ final LockManager manager = LockManager.getInstance();
+ final LockManager.EvaluationResult evaluation = manager.evaluateCondition(object, getIf(request));
+ if (!evaluation.result) {
+ response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
return;
+ } else {
+ if (!evaluation.locks.isEmpty()) {
+ LOG.debug(String.format("discovered locks: %s", evaluation.locks));
+ sendLockAcquiredResponse(response, evaluation.locks.get(0));
+ return;
+ }
}
- } catch (LockException e) {
- // handle locks below
+ } catch (LockConflictException e) {
+ List<Lock> locks = e.getLocks();
+ for (Lock lock : locks) {
+ if (Lock.EXCLUSIVE.equals(lock.getType())) {
+ response.sendError(WebdavHandler.SC_LOCKED);
+ return;
+ }
+ }
+ } catch (ParseException e) {
+ response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
+ return;
}
try {
blob - 26c2f2d817fea565d73ce19efee0bf802f26c328
blob + 3edc720043d1911ec3a98cd4b63e24554884e6a9
--- src/main/java/com/thinkberg/moxo/dav/MkColHandler.java
+++ src/main/java/com/thinkberg/moxo/dav/MkColHandler.java
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
+import java.text.ParseException;
/**
* @author Matthias L. Jugel
FileObject object = getVFSObject(request.getPathInfo());
try {
- LockManager.getInstance().checkCondition(object, getIf(request));
- } catch (LockException e) {
- if (e.getLocks() != null) {
- response.sendError(SC_LOCKED);
- } else {
+ if (!LockManager.getInstance().evaluateCondition(object, getIf(request)).result) {
response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
+ return;
}
+ } catch (LockException e) {
+ response.sendError(WebdavHandler.SC_LOCKED);
return;
+ } catch (ParseException e) {
+ response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
+ return;
}
if (object.exists()) {
blob - 47200c23bde07c62df6a59cebb06f1d458fe4661
blob + 029e0da28dec7284d703d42725af8549d2f49ae5
--- src/main/java/com/thinkberg/moxo/dav/PropPatchHandler.java
+++ src/main/java/com/thinkberg/moxo/dav/PropPatchHandler.java
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URL;
+import java.text.ParseException;
import java.util.List;
/**
FileObject object = getVFSObject(request.getPathInfo());
try {
- LockManager.getInstance().checkCondition(object, getIf(request));
- } catch (LockException e) {
- if (e.getLocks() != null) {
- response.sendError(SC_LOCKED);
- } else {
+ if (!LockManager.getInstance().evaluateCondition(object, getIf(request)).result) {
response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
+ return;
}
+ } catch (LockException e) {
+ response.sendError(WebdavHandler.SC_LOCKED);
return;
+ } catch (ParseException e) {
+ response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
+ return;
}
SAXReader saxReader = new SAXReader();
blob - fc92a3d6e348a399694d0d684681d25c85f99d27
blob + ed879c915a369d9649cbba45551e983960a733ad
--- src/main/java/com/thinkberg/moxo/dav/PutHandler.java
+++ src/main/java/com/thinkberg/moxo/dav/PutHandler.java
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.text.ParseException;
/**
* @author Matthias L. Jugel
FileObject object = getVFSObject(request.getPathInfo());
try {
- LockManager.getInstance().checkCondition(object, getIf(request));
- } catch (LockException e) {
- if (e.getLocks() != null) {
- response.sendError(SC_LOCKED);
- } else {
+ if (!LockManager.getInstance().evaluateCondition(object, getIf(request)).result) {
response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
+ return;
}
+ } catch (LockException e) {
+ response.sendError(WebdavHandler.SC_LOCKED);
return;
+ } catch (ParseException e) {
+ response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
+ return;
}
-
// it is forbidden to write data on a folder
if (object.exists() && FileType.FOLDER.equals(object.getType())) {
response.sendError(HttpServletResponse.SC_FORBIDDEN);
InputStream is = request.getInputStream();
OutputStream os = object.getContent().getOutputStream();
- int bytesCopied = Util.copyStream(is, os);
- LOG.debug("sending " + bytesCopied + "/" + request.getHeader("Content-length") + " bytes");
+ long bytesCopied = Util.copyStream(is, os);
+ String contentLengthHeader = request.getHeader("Content-length");
+ LOG.debug(String.format("sent %d/%s bytes", bytesCopied, contentLengthHeader == null ? "unknown" : contentLengthHeader));
os.flush();
object.close();
blob - 6cc9911ce6b996e0c0610c4987c090772f356817
blob + 9b6e71d63c5a52df24c66a3d45ff9da5b93a6115
--- src/main/java/com/thinkberg/moxo/dav/Util.java
+++ src/main/java/com/thinkberg/moxo/dav/Util.java
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.channels.Channels;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.WritableByteChannel;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
// }
- public static int copyStream(InputStream is, OutputStream os) throws IOException {
- byte[] buffer = new byte[8192];
- int bytesRead, bytesCount = 0;
- while ((bytesRead = is.read(buffer)) != -1) {
- os.write(buffer, 0, bytesRead);
- bytesCount += bytesRead;
+ public static long copyStream(final InputStream is, final OutputStream os) throws IOException {
+ ReadableByteChannel rbc = Channels.newChannel(is);
+ WritableByteChannel wbc = Channels.newChannel(os);
+
+ int bytesWritten = 0;
+ final ByteBuffer buffer = ByteBuffer.allocateDirect(16 * 1024);
+ while (rbc.read(buffer) != -1) {
+ buffer.flip();
+ bytesWritten += wbc.write(buffer);
+ buffer.compact();
}
- os.flush();
+ buffer.flip();
+ while (buffer.hasRemaining()) {
+ bytesWritten += wbc.write(buffer);
+ }
- return bytesCount;
+ rbc.close();
+ wbc.close();
+
+ return bytesWritten;
}
}
blob - bb559777fd0928f6c9ee3f7bb12b897d76ffc0d7
blob + b52d655c1ac100625581cb02aba7e7f923b502b6
--- src/main/java/com/thinkberg/moxo/dav/WebdavHandler.java
+++ src/main/java/com/thinkberg/moxo/dav/WebdavHandler.java
FileSystemManager fsm = VFS.getManager();
// create a virtual filesystemusing the url provided or fall back to RAM
- fileSystemRoot = fsm.resolveFile(properties.getStringProperty("vfs.url", "ram:/"));
+ fileSystemRoot = fsm.resolveFile(properties.getStringProperty("vfs.uri", "ram:/"));
LOG.info("created virtual file system: " + fileSystemRoot);
} catch (FileSystemException e) {
depthValue = Integer.parseInt(depth);
}
- LOG.debug("request header: Depth: " + (depthValue == Integer.MAX_VALUE ? "infinity" : depthValue));
+ LOG.debug(String.format("request header: Depth: %s", (depthValue == Integer.MAX_VALUE ? "infinity" : depthValue)));
return depthValue;
}
String overwrite = request.getHeader("Overwrite");
boolean overwriteValue = overwrite == null || "T".equals(overwrite);
- LOG.debug("request header: Overwrite: " + overwriteValue);
+ LOG.debug(String.format("request header: Overwrite: %s", overwriteValue));
return overwriteValue;
}
if (null != targetUrlStr) {
URL target = new URL(targetUrlStr);
targetObject = getVFSObject(target.getPath());
- LOG.debug("request header: Destination: " + targetObject.getName().getPath());
+ LOG.debug(String.format("request header: Destination: %s", targetObject.getName().getPath()));
}
return targetObject;
String getIfHeader = request.getHeader("If");
if (null != getIfHeader) {
- LOG.debug("request header: If: " + getIfHeader);
+ LOG.debug(String.format("request header: If: '%s'", getIfHeader));
}
return getIfHeader;
}
String timeout = request.getHeader("Timeout");
if (null != timeout) {
String[] timeoutValues = timeout.split(",[ ]*");
- LOG.debug("request header: Timeout: " + Arrays.asList(timeoutValues).toString());
+ LOG.debug(String.format("request header: Timeout: %s", Arrays.asList(timeoutValues).toString()));
if ("infinity".equalsIgnoreCase(timeoutValues[0])) {
return -1;
} else {
blob - 60ddbc7d9240cdb82e2d97eba5c93761a077ca9d
blob + 7b8460688401f8da61a40a80db7fe85b44f0b4c3
--- src/main/java/com/thinkberg/moxo/dav/data/DavResource.java
+++ src/main/java/com/thinkberg/moxo/dav/data/DavResource.java
PROP_LOCK_DISCOVERY,
PROP_RESOURCETYPE,
PROP_SOURCE,
- PROP_SUPPORTED_LOCK,
- PROP_QUOTA_AVAILABLE_BYTES
+ PROP_SUPPORTED_LOCK
);
protected final FileObject object;
blob - 28d71a7844d3ecca4d5bc448fb9e7c6e47913291
blob + 1e07eee9313edf1cf95329fbec218d5878378116
--- src/main/java/com/thinkberg/moxo/dav/lock/Lock.java
+++ src/main/java/com/thinkberg/moxo/dav/lock/Lock.java
public String toString() {
- return new StringBuffer().append("Lock[")
- .append(object).append(",")
- .append(type).append(",")
- .append(scope).append("]").toString();
+ return String.format("Lock[%s,%s,%s,%s]", object, type, scope, token);
}
}
blob - /dev/null
blob + 217437da42098d75357b98160f381a27ba1e4c95 (mode 644)
--- /dev/null
+++ src/main/java/com/thinkberg/moxo/dav/lock/LockConditionRequiredException.java
+/*
+ * Copyright 2009 Matthias L. Jugel.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.thinkberg.moxo.dav.lock;
+
+import java.util.List;
+
+public class LockConditionRequiredException extends LockException {
+ public LockConditionRequiredException(List<Lock> locks) {
+ super(locks);
+ }
+}
blob - 937cae1dfcf4c4ed1d768feb4e82ebf20cff4a46
blob + 1ca6c2f973c038a647cd769743a77a78708243bc
--- src/main/java/com/thinkberg/moxo/dav/lock/LockException.java
+++ src/main/java/com/thinkberg/moxo/dav/lock/LockException.java
public List<Lock> getLocks() {
return locks;
}
+
+ public String toString() {
+ return String.format("[%s: %s]", this.getClass(), locks);
+ }
}
blob - 3a78f6d504fbd03a7ef5615a5b68b6a02b64931e
blob + ffac2fafb4c81fc700c4686ab276f1625eebe709
--- src/main/java/com/thinkberg/moxo/dav/lock/LockManager.java
+++ src/main/java/com/thinkberg/moxo/dav/lock/LockManager.java
package com.thinkberg.moxo.dav.lock;
import com.thinkberg.moxo.vfs.DepthFileSelector;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
import org.apache.commons.vfs.FileObject;
import org.apache.commons.vfs.FileSelectInfo;
import org.apache.commons.vfs.FileSystemException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.text.ParseException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/**
* The lock manager is responsible for exclusive and shared write locks on the
*/
public class LockManager {
private static LockManager instance = null;
+ private static final Log LOG = LogFactory.getLog(LockManager.class);
+ // condition parser patterns and tokens
+ private static final Pattern IF_PATTERN = Pattern.compile("(<[^>]+>)|(\\([^)]+\\))");
+ private static final Pattern CONDITION_PATTERN = Pattern.compile("([Nn][Oo][Tt])|(<[^>]+>)|(\\[[^]]+\\])");
+ private static final char TOKEN_LOWER_THAN = '<';
+ private static final char TOKEN_LEFT_BRACE = '(';
+ private static final char TOKEN_LEFT_BRACKET = '[';
+
/**
* Get an instance of the lock manager.
*
}
/**
- * Check a condition for a file object. The condition check looks for locks on the
- * given file object and will throw exceptions if the condition does not meet the
- * lock requirements (i.e. lock token in the condition differes from the token in
- * the discovered lock) or no condition exists if a lock was discovered. If no lock
- * was discovered but a condition exists a lock condition exception will be thrown.
+ * Evaluate an 'If:' header condition.
+ * The condition may be a tagged list or an untagged list. Tagged lists define the resource, the condition
+ * applies to in front of the condition (ex. 1, 2, 5, 6). Conditions may be inverted by using 'Not' at the
+ * beginning of the condition (ex. 3, 4, 6). The list constitutes an OR expression while the list of
+ * conditions within braces () constitutes an AND expression.
+ * <p/>
+ * Evaluate example 2:<br/>
+ * <code>
+ * URI(/resource1) { (
+ * is-locked-with(urn:uuid:181d4fae-7d8c-11d0-a765-00a0c91e6bf2)
+ * AND matches-etag(W/"A weak ETag") )
+ * OR ( matches-etag("strong ETag") ) }
+ * </code>
+ * <p/>
+ * Examples:
+ * <ol>
+ * <li> <http://cid:8080/litmus/unmapped_url> (<opaquelocktoken:cd6798>)</li>
+ * <li> </resource1> (<urn:uuid:181d4fae-7d8c-11d0-a765-00a0c91e6bf2> [W/"A weak ETag"]) (["strong ETag"])</li>
+ * <li> (<urn:uuid:181d4fae-7d8c-11d0-a765-00a0c91e6bf2>) (Not <DAV:no-lock>)</li>
+ * <li> (Not <urn:uuid:181d4fae-7d8c-11d0-a765-00a0c91e6bf2> <urn:uuid:58f202ac-22cf-11d1-b12d-002035b29092>)</li>
+ * <li> </specs/rfc2518.doc> (["4217"])</li>
+ * <li> </specs/rfc2518.doc> (Not ["4217"])</li>
+ * </ol>
*
- * @param object the file object in question
- * @param ifCond the condition to check
- * @return the lock found for the given if condition and the object
- * @throws FileSystemException if the object or path cannot be accessed
- * @throws LockConflictException if there is a a lock but no condition
- * @throws LockConditionFailedException if the condition and lock does not match
+ * @param contextObject the contextual resource (needed when the If: condition is not tagged)
+ * @param ifCondition the string of the condition as sent by the If: header
+ * @return evaluation of the condition expression
+ * @throws ParseException if the condition does not meet the syntax requirements
+ * @throws LockConflictException
+ * @throws FileSystemException
*/
- public Lock checkCondition(FileObject object, String ifCond)
- throws FileSystemException, LockConflictException, LockConditionFailedException {
- List<Lock> locks = discoverLock(object);
- if (null != locks && !locks.isEmpty()) {
- // if there is no condition but a lock, this must fail
- if (null == ifCond) {
+ public EvaluationResult evaluateCondition(FileObject contextObject, String ifCondition)
+ throws FileSystemException, LockConflictException, ParseException {
+ List<Lock> locks = discoverLock(contextObject);
+ EvaluationResult evaluation = new EvaluationResult();
+
+ if (ifCondition == null || "".equals(ifCondition)) {
+ if (locks != null) {
throw new LockConflictException(locks);
}
+ evaluation.result = true;
+ return evaluation;
+ }
- // simple check whether the token is in the condition (TODO: check for NOT)
- for (Lock lock : locks) {
- if (ifCond.indexOf("<" + lock.getToken() + ">") != -1) {
- return lock;
- }
+ Matcher matcher = IF_PATTERN.matcher(ifCondition);
+ FileObject resource = contextObject;
+ while (matcher.find()) {
+ String token = matcher.group();
+ switch (token.charAt(0)) {
+ case TOKEN_LOWER_THAN:
+ String resourceUri = token.substring(1, token.length() - 1);
+ try {
+ resource = contextObject.getFileSystem().resolveFile(new URI(resourceUri).getPath());
+ locks = discoverLock(resource);
+ } catch (URISyntaxException e) {
+ throw new ParseException(ifCondition, matcher.start());
+ }
+ break;
+ case TOKEN_LEFT_BRACE:
+ LOG.debug(String.format("URI(%s) {", resource));
+ Matcher condMatcher = CONDITION_PATTERN.matcher(token.substring(1, token.length() - 1));
+ boolean expressionResult = true;
+ while (condMatcher.find()) {
+ String condToken = condMatcher.group();
+ boolean negate = false;
+ if (condToken.matches("[Nn][Oo][Tt]")) {
+ negate = true;
+ condMatcher.find();
+ condToken = condMatcher.group();
+ }
+ switch (condToken.charAt(0)) {
+ case TOKEN_LOWER_THAN:
+ String lockToken = condToken.substring(1, condToken.length() - 1);
+
+ boolean foundLock = false;
+ if (locks != null) {
+ for (Lock lock : locks) {
+ if (lockToken.equals(lock.getToken())) {
+ evaluation.locks.add(lock);
+ foundLock = true;
+ break;
+ }
+ }
+ }
+ final boolean foundLockResult = negate ? !foundLock : foundLock;
+ LOG.debug(String.format(" %sis-locked-with(%s) = %b",
+ negate ? "NOT " : "", lockToken, foundLockResult));
+ expressionResult = expressionResult && foundLockResult;
+ break;
+ case TOKEN_LEFT_BRACKET:
+ String eTag = condToken.substring(1, condToken.length() - 1);
+ String resourceETag = String.format("%x", resource.hashCode());
+ boolean resourceTagMatches = resourceETag.equals(eTag);
+ final boolean matchesEtagResult = negate ? !resourceTagMatches : resourceTagMatches;
+ LOG.debug(String.format(" %smatches-etag(%s) = %b",
+ negate ? "NOT " : "", eTag, matchesEtagResult));
+ expressionResult = expressionResult && matchesEtagResult;
+ break;
+ default:
+ throw new ParseException(String.format("syntax error in condition '%s' at %d",
+ ifCondition, matcher.start() + condMatcher.start()),
+ matcher.start() + condMatcher.start());
+ }
+ }
+
+ evaluation.result = evaluation.result || expressionResult;
+ LOG.debug("} => " + evaluation.result);
+ break;
+ default:
+ throw new ParseException(String.format("syntax error in condition '%s' at %d", ifCondition, matcher.start()),
+ matcher.start());
}
- throw new LockConditionFailedException(locks);
- } else if (null != ifCond) {
- // no lock but a condition must fail too
- throw new LockConditionFailedException(null);
}
- return null;
+ // regardless of the evaluation, if the object is locked but there is no valed lock token in the
+ // conditions we must fail with a lock conflict too
+ if (evaluation.result && (locks != null && !locks.isEmpty()) && evaluation.locks.isEmpty()) {
+ throw new LockConflictException(locks);
+ }
+ return evaluation;
}
+ public class EvaluationResult {
+ public List<Lock> locks = new ArrayList<Lock>();
+ public boolean result = false;
+ public String toString() {
+ return String.format("EvaluationResult[%b,%s]", result, locks);
+ }
+ }
+
/**
* Add a lock to the list of shared locks of a given object.
*
}, false, new ArrayList());
}
}
+
}
blob - c8a306a053d53a8a29bdcfae8a10e3c852f7f3b0
blob + 094aa76addd911ee350612557694b1c5fecd0879
--- src/main/java/com/thinkberg/moxo/servlet/MoxoWebDAVServlet.java
+++ src/main/java/com/thinkberg/moxo/servlet/MoxoWebDAVServlet.java
// }
+ // show we are doing the litmus test
+ String litmusTest = request.getHeader("X-Litmus");
+ if (null == litmusTest) {
+ litmusTest = request.getHeader("X-Litmus-Second");
+ }
+ if (litmusTest != null) {
+ LOG.info(String.format("WebDAV Litmus Test: %s", litmusTest));
+ }
+
String method = request.getMethod();
- if (request.getHeader("X-Litmus") != null) {
- LOG.info(String.format("WebDAV Litmus Test: %s", request.getHeader("X-Litmus")));
- }
LOG.debug(String.format(">> %s %s", request.getMethod(), request.getPathInfo()));
if (handlers.containsKey(method)) {
handlers.get(method).service(request, response);
}
Response jettyResponse = ((Response) response);
String reason = jettyResponse.getReason();
- LOG.debug(String.format("<< %s (%b%s)", request.getMethod(), jettyResponse.getStatus(), reason != null ? ": " + reason : ""));
+ LOG.debug(String.format("<< %s (%d%s)", request.getMethod(), jettyResponse.getStatus(), reason != null ? ": " + reason : ""));
}
}
blob - 5c2181514077735de9eaee6f6f59f9db52ebc405
blob + 1c80a615bbd89066c8f4fccfe435b9b490d829b9
--- src/main/java/com/thinkberg/moxo/vfs/jets3t/Jets3tFileSystem.java
+++ src/main/java/com/thinkberg/moxo/vfs/jets3t/Jets3tFileSystem.java
try {
service = Jets3tConnector.getInstance().getService();
if (!service.isBucketAccessible(bucketId)) {
- LOG.info("creating new S3 bucket (" + bucketId + ") for file system");
+ LOG.info(String.format("creating new S3 bucket '%s' for file system root", bucketId));
bucket = service.createBucket(bucketId);
} else {
- LOG.info("using existing S3 bucket: " + bucketId);
+ LOG.info(String.format("using existing S3 bucket '%s' for file system root", bucketId));
bucket = new S3Bucket(bucketId);
}
} catch (S3ServiceException e) {
}
}
+ public void destroyFileSystem() throws FileSystemException {
+ try {
+ service.deleteBucket(bucket);
+ } catch (S3ServiceException e) {
+ throw new FileSystemException("can't delete file system root", e);
+ }
+
+
+ }
+
@SuppressWarnings({"unchecked"})
protected void addCapabilities(Collection caps) {
caps.addAll(S3FileProvider.capabilities);
protected FileObject createFile(FileName fileName) throws Exception {
return new Jets3tFileObject(fileName, this, service, bucket);
}
+
+
}
blob - 9ac36182f7b6d695e49901c76968ce5efc0a39bf
blob + 674ed457589717c031e07eeae6ec32eea0b1deb7
--- src/test/java/com/thinkberg/moxo/MoxoTest.java
+++ src/test/java/com/thinkberg/moxo/MoxoTest.java
String propertiesFileName = System.getProperty("moxo.properties", "moxo.properties");
Jets3tProperties properties = Jets3tProperties.getInstance(propertiesFileName);
- String vfsUrl = properties.getStringProperty("vfs.url", null);
+ String vfsUrl = properties.getStringProperty("vfs.uri", null);
if (null != vfsUrl && vfsUrl.startsWith("s3:")) {
s.addTestSuite(S3FileNameTest.class);
s.addTestSuite(S3FileProviderTest.class);
blob - 2fa2574ff79738bae97230feeef528a7ff9781d9
blob + df58a524ec201431dcad1cd542b86f58aed5384d
--- src/test/java/com/thinkberg/moxo/dav/DavLockManagerTest.java
+++ src/test/java/com/thinkberg/moxo/dav/DavLockManagerTest.java
super();
}
- public void testAcquireSharedFileLock() {
+ public void testAcquireSingleSharedFileLock() {
Lock sharedLock = new Lock(aFile, Lock.WRITE, Lock.SHARED, OWNER_STR, 0, 3600);
try {
LockManager.getInstance().acquireLock(sharedLock);
}
}
- public void testAcquireExclusiveLock() {
+ public void testFailToAcquireExclusiveLockOverSharedLock() {
Lock sharedLock = new Lock(aFile, Lock.WRITE, Lock.SHARED, OWNER_STR, 0, 3600);
Lock exclusiveLock = new Lock(aFile, Lock.WRITE, Lock.EXCLUSIVE, OWNER_STR, 0, 3600);
try {
assertEquals(LockConflictException.class, e.getClass());
}
}
+
+ public void testConditionUnmappedFails() throws Exception {
+ final String condition = "<http://cid:8080/litmus/unmapped_url> (<opaquelocktoken:cd6798>)";
+ assertFalse("condition for unmapped resource must fail",
+ LockManager.getInstance().evaluateCondition(aFile, condition).result);
+ }
+
+ public void testConditionSimpleLockToken() throws Exception {
+ Lock aLock = new Lock(aFile, Lock.WRITE, Lock.SHARED, OWNER_STR, 0, 3600);
+ final String condition = "(<" + aLock.getToken() + ">)";
+ LockManager.getInstance().acquireLock(aLock);
+ assertTrue("condition with existing lock token should not fail",
+ LockManager.getInstance().evaluateCondition(aFile, condition).result);
+ }
+
+ public void testConditionSimpleLockLokenWrong() throws Exception {
+ Lock aLock = new Lock(aFile, Lock.WRITE, Lock.SHARED, OWNER_STR, 0, 3600);
+ final String condition = "(<" + aLock.getToken() + "x>)";
+ LockManager.getInstance().acquireLock(aLock);
+ try {
+ LockManager.getInstance().evaluateCondition(aFile, condition);
+ } catch (LockConflictException e) {
+ assertFalse("condition with wrong lock token must fail on locked resource", e.getLocks().isEmpty());
+ }
+ }
+
+ public void testConditionSimpleLockTokenAndETag() throws Exception {
+ Lock aLock = new Lock(aFile, Lock.WRITE, Lock.SHARED, OWNER_STR, 0, 3600);
+ final String condition = "(<" + aLock.getToken() + "> [" + Integer.toHexString(aFile.hashCode()) + "])";
+ LockManager.getInstance().acquireLock(aLock);
+ assertTrue("condition with existing lock token and correct ETag should not fail",
+ LockManager.getInstance().evaluateCondition(aFile, condition).result);
+ }
+
+ public void testConditionSimpleLockTokenWrongAndETag() throws Exception {
+ Lock aLock = new Lock(aFile, Lock.WRITE, Lock.SHARED, OWNER_STR, 0, 3600);
+ final String condition = "(<" + aLock.getToken() + "x> [" + Integer.toHexString(aFile.hashCode()) + "])";
+ LockManager.getInstance().acquireLock(aLock);
+ try {
+ LockManager.getInstance().evaluateCondition(aFile, condition);
+ } catch (LockConflictException e) {
+ assertFalse("condition with non-existing lock token and correct ETag should fail",
+ e.getLocks().isEmpty());
+ }
+ }
+
+ public void testConditionSimpleLockTokenAndETagWrong() throws Exception {
+ Lock aLock = new Lock(aFile, Lock.WRITE, Lock.SHARED, OWNER_STR, 0, 3600);
+ final String condition = "(<" + aLock.getToken() + "> [" + Integer.toHexString(aFile.hashCode()) + "x])";
+ LockManager.getInstance().acquireLock(aLock);
+ assertFalse("condition with existing lock token and incorrect ETag should fail",
+ LockManager.getInstance().evaluateCondition(aFile, condition).result);
+ }
+
+ public void testConditionSimpleLockTokenWrongAndETagWrong() throws Exception {
+ Lock aLock = new Lock(aFile, Lock.WRITE, Lock.SHARED, OWNER_STR, 0, 3600);
+ final String condition = "(<" + aLock.getToken() + "x> [" + Integer.toHexString(aFile.hashCode()) + "x])";
+ LockManager.getInstance().acquireLock(aLock);
+ assertFalse("condition with non-existing lock token and incorrect ETag should fail",
+ LockManager.getInstance().evaluateCondition(aFile, condition).result);
+ }
+
+ public void testConditionSimpleLockTokenWrongAndETagOrSimpleETag() throws Exception {
+ Lock aLock = new Lock(aFile, Lock.WRITE, Lock.SHARED, OWNER_STR, 0, 3600);
+ final String eTag = Integer.toHexString(aFile.hashCode());
+ final String condition = "(<" + aLock.getToken() + "x> [" + eTag + "]) ([" + eTag + "])";
+ LockManager.getInstance().acquireLock(aLock);
+ try {
+ LockManager.getInstance().evaluateCondition(aFile, condition);
+ } catch (LockConflictException e) {
+ assertFalse("condition with one correct ETag in list should not fail on locked resource",
+ e.getLocks().isEmpty());
+ }
+ }
+
+ public void testConditionSimpleNegatedLockTokenWrongAndETag() throws Exception {
+ Lock aLock = new Lock(aFile, Lock.WRITE, Lock.SHARED, OWNER_STR, 0, 3600);
+ final String eTag = Integer.toHexString(aFile.hashCode());
+ final String condition = "(Not <" + aLock.getToken() + "x> [" + eTag + "])";
+ assertTrue("condition with negated wrong lock token and correct ETag should not fail on unlocked resource",
+ LockManager.getInstance().evaluateCondition(aFile, condition).result);
+ }
+
+
+ public void testConditionMustNotFail() throws Exception {
+ Lock aLock = new Lock(aFile, Lock.WRITE, Lock.SHARED, OWNER_STR, 0, 3600);
+ final String condition = "(<" + aLock.getToken() + "x>) (Not <DAV:no-lock>)";
+ assertTrue("using (Not <DAV:no-lock>) in condition list must not fail on unlocked resource",
+ LockManager.getInstance().evaluateCondition(aFile, condition).result);
+ }
+
+
+ public void testComplexConditionWithBogusLockToken() throws Exception {
+ Lock aLock = new Lock(aFile, Lock.WRITE, Lock.SHARED, OWNER_STR, 0, 3600);
+ final String eTag = Integer.toHexString(aFile.hashCode());
+ final String condition = "(<" + aLock.getToken() + "> [" + eTag + "x]) (Not <DAV:no-lock> [" + eTag + "x])";
+ LockManager.getInstance().acquireLock(aLock);
+ assertFalse("complex condition with bogus eTag should fail",
+ LockManager.getInstance().evaluateCondition(aFile, condition).result);
+ }
+
+
+// assertFalse(lockManager.evaluateCondition(aFile, "</resource1> (<urn:uuid:181d4fae-7d8c-11d0-a765-00a0c91e6bf2> [W/\"A weak ETag\"]) ([\"strong ETag\"])");
+// lockManager.evaluateCondition(aFile, "(<urn:uuid:181d4fae-7d8c-11d0-a765-00a0c91e6bf2>) (Not <DAV:no-lock>)");
+// lockManager.evaluateCondition(aFile, "(Not <urn:uuid:181d4fae-7d8c-11d0-a765-00a0c91e6bf2> <urn:uuid:58f202ac-22cf-11d1-b12d-002035b29092>)");
+// lockManager.evaluateCondition(aFile, "</specs/rfc2518.doc> ([\"4217\"])");
+// lockManager.evaluateCondition(aFile, "</specs/rfc2518.doc> (Not [\"4217\"])");
+// lockManager.evaluateCondition(aFile, "(<urn:uuid:181d4fae-7d8c-11d0-a765-00a0c91e6bf2>) (Not <DAV:no-lock>) </specs/rfc2518.doc> (Not [\"4217\"])");
+// lockManager.evaluateCondition(aFile, "(<opaquelocktoken:10a098> [10a198]) (Not <DAV:no-lock> [10a198])");
+
+// public void testLockConditionRequiredException() {
+// Lock sharedLock = new Lock(aFile, Lock.WRITE, Lock.SHARED, OWNER_STR, 0, 3600);
+// try {
+// LockManager.getInstance().acquireLock(sharedLock);
+// LockManager.getInstance().checkCondition(aFile, null);
+// assertTrue("checkCondition() should fail", false);
+// } catch (Exception e) {
+// assertEquals(LockConditionRequiredException.class, e.getClass());
+// }
+// }
}
blob - 38485aeefa013dff633c31b5e00104bd1efcdd46
blob + 5b2ab9887bbf9efd98ff97084bc3b832cb1ed6ef
--- src/test/java/com/thinkberg/moxo/vfs/S3FileProviderTest.java
+++ src/test/java/com/thinkberg/moxo/vfs/S3FileProviderTest.java
package com.thinkberg.moxo.vfs;
+import com.thinkberg.moxo.vfs.jets3t.Jets3tFileSystem;
import org.apache.commons.vfs.*;
import java.io.IOException;
assertFalse(destFolder.exists());
}
- public void testMoveFolder() throws FileSystemException {
+// public void testMoveFolder() throws FileSystemException {
+//
+// }
- }
-
public void testCloseFileSystem() throws FileSystemException {
FileSystem fs = VFS.getManager().resolveFile(ROOT).getFileSystem();
VFS.getManager().closeFileSystem(fs);
}
+ public void testDestroyFileSystem() throws FileSystemException {
+ FileSystem fs = VFS.getManager().resolveFile(ROOT).getFileSystem();
+ assertTrue(fs instanceof Jets3tFileSystem);
+ ((Jets3tFileSystem) fs).destroyFileSystem();
+ }
}
blob - 790d3007daff25069746237120ced9b14239a05a
blob + 76282b1a08134826075cca2b0d02c7972257af5c
--- src/test/java/com/thinkberg/moxo/vfs/S3TestCase.java
+++ src/test/java/com/thinkberg/moxo/vfs/S3TestCase.java
import junit.framework.TestCase;
import org.jets3t.service.Jets3tProperties;
+import java.util.Random;
+
/**
* @author Matthias L. Jugel
*/
static {
String propertiesFileName = System.getProperty("moxo.properties", "moxo.properties");
Jets3tProperties properties = Jets3tProperties.getInstance(propertiesFileName);
- ROOT = properties.getStringProperty("vfs.url", "ram:/");
+ System.out.println("ignoring original vfs.url settings for test: " + properties.getStringProperty("vfs.uri", "ram:/"));
+ ROOT = "s3://MOXOTEST" + String.format("%X", new Random(System.currentTimeMillis()).nextLong()) + "/";
+ System.out.println("using " + ROOT);
}
}