Commit Diff


commit - 606c7aeff77ed85d0c8374e5ef56a9fc92549687
commit + de6ddb74522969f7a13e7a09c05b9406d30b5ad6
blob - 366af8a561343bd5d1b259929b35c50840a49bae
blob + 1582d815464b5a6997af06d36a8f835f6544238a
--- pom.xml
+++ pom.xml
@@ -1,71 +1,83 @@
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-  <modelVersion>4.0.0</modelVersion>
-  <groupId>com.thinkberg.moxo</groupId>
-  <artifactId>moxo</artifactId>
-  <packaging>jar</packaging>
-  <version>1.0-SNAPSHOT</version>
-  <name>Moxo S3 DAV Proxy</name>
-  <url>http://thinkberg.com</url>
-  <repositories>
-    <repository>
-      <id>codehaus-m2-repository</id>
-      <name>Codehaus Maven 2.x Repository</name>
-      <url>http://repository.codehaus.org</url>
-    </repository>
-  </repositories>
-  <dependencies>
-    <dependency>
-      <groupId>junit</groupId>
-      <artifactId>junit</artifactId>
-      <version>3.8.1</version>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>jets3t</groupId>
-      <artifactId>jets3t</artifactId>
-      <version>0.5.0</version>
-    </dependency>
-    <dependency>
-      <groupId>commons-httpclient</groupId>
-      <artifactId>commons-httpclient</artifactId>
-      <version>3.0.1</version>
-    </dependency>
-    <dependency>
-      <groupId>commons-vfs</groupId>
-      <artifactId>commons-vfs</artifactId>
-      <version>1.0</version>
-    </dependency>
-    <dependency>
-      <groupId>org.mortbay.jetty</groupId>
-      <artifactId>jetty</artifactId>
-      <version>6.1.1</version>
-    </dependency>
-    <dependency>
-      <groupId>dom4j</groupId>
-      <artifactId>dom4j</artifactId>
-      <version>1.6.1</version>
-    </dependency>
-    <dependency>
-      <groupId>jaxen</groupId>
-      <artifactId>jaxen</artifactId>
-      <version>1.1</version>
-    </dependency>
-  </dependencies>
-  <build>
-    <plugins>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-jar-plugin</artifactId>
-        <configuration>
-          <archive>
-            <manifest>
-              <mainClass>com.thinkberg.moxo.MoxoJettyRunner</mainClass>
-              <addClasspath>true</addClasspath>
-            </manifest>
-          </archive>
-        </configuration>
-      </plugin>
-    </plugins>
-  </build>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>com.thinkberg.moxo</groupId>
+    <artifactId>moxo</artifactId>
+    <packaging>jar</packaging>
+    <version>1.0-SNAPSHOT</version>
+    <name>Moxo S3 DAV Proxy</name>
+    <url>http://thinkberg.com</url>
+    <repositories>
+        <repository>
+            <id>codehaus-m2-repository</id>
+            <name>Codehaus Maven 2.x Repository</name>
+            <url>http://repository.codehaus.org</url>
+        </repository>
+        <repository>
+            <name>jets3t</name>
+            <id>jets3t</id>
+            <url>http://jets3t.s3.amazonaws.com/maven2</url>
+        </repository>
+    </repositories>
+    <dependencies>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>3.8.1</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>net.java.dev.jets3t</groupId>
+            <artifactId>jets3t</artifactId>
+            <version>0.6.1</version>
+        </dependency>
+        <dependency>
+            <groupId>commons-httpclient</groupId>
+            <artifactId>commons-httpclient</artifactId>
+            <version>3.0.1</version>
+        </dependency>
+        <dependency>
+            <groupId>commons-vfs</groupId>
+            <artifactId>commons-vfs</artifactId>
+            <version>1.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.mortbay.jetty</groupId>
+            <artifactId>jetty</artifactId>
+            <version>6.1.1</version>
+        </dependency>
+        <dependency>
+            <groupId>dom4j</groupId>
+            <artifactId>dom4j</artifactId>
+            <version>1.6.1</version>
+        </dependency>
+        <dependency>
+            <groupId>jaxen</groupId>
+            <artifactId>jaxen</artifactId>
+            <version>1.1</version>
+        </dependency>
+    </dependencies>
+    <build>
+        <plugins>
+            <plugin>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>1.5</source>
+                    <target>1.5</target>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <configuration>
+                    <archive>
+                        <manifest>
+                            <mainClass>com.thinkberg.moxo.MoxoJettyRunner</mainClass>
+                            <addClasspath>true</addClasspath>
+                        </manifest>
+                    </archive>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
 </project>
blob - 81f9dc3075c006734e4d4a76f76f86d0023776df
blob + 834ca79e40db93587eb89c07798c51befb4cdbe8
--- src/main/java/com/thinkberg/moxo/Main.java
+++ src/main/java/com/thinkberg/moxo/Main.java
@@ -16,11 +16,7 @@
 
 package com.thinkberg.moxo;
 
-import java.io.BufferedInputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
+import java.io.*;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.net.MalformedURLException;
@@ -41,7 +37,6 @@ import java.util.jar.JarInputStream;
 public class Main {
   private final static URL location = Main.class.getProtectionDomain().getCodeSource().getLocation();
 
-  @SuppressWarnings({"RedundantArrayCreation"})
   public static void main(String args[])
           throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
     ClassLoader parentClassLoader = Thread.currentThread().getContextClassLoader();
@@ -63,6 +58,7 @@ public class Main {
     }
 
     Class mainClass = classLoader.loadClass("com.thinkberg.moxo.MoxoJettyRunner");
+    @SuppressWarnings({"RedundantArrayCreation"})
     final Method main = mainClass.getDeclaredMethod("main", new Class[]{String[].class});
     main.invoke(null, new Object[]{args});
   }
@@ -72,6 +68,7 @@ public class Main {
    *
    * @return the classpath
    */
+  @SuppressWarnings({"ConstantConditions"})
   private static URL[] initClassPath() {
     List<URL> urlArray = new ArrayList<URL>();
     InputStream manifestIn = null;
@@ -112,7 +109,7 @@ public class Main {
         // ignore errors
       }
     }
-    return urlArray.toArray(new URL[0]);
+    return urlArray.toArray(new URL[urlArray.size()]);
   }
 
   /**
blob - e1d96cf755ad2bafbd99a230101d870629bf7423
blob + fad5566e89f5e3a1e30cdf358aff899558c340ff
--- src/main/java/com/thinkberg/moxo/MoxoJettyRunner.java
+++ src/main/java/com/thinkberg/moxo/MoxoJettyRunner.java
@@ -40,7 +40,7 @@ public class MoxoJettyRunner {
 
     try {
       Server server = new Server();
-      XmlConfiguration xmlConfiguration = new XmlConfiguration(getResource(CONF_JETTY_XML));
+      XmlConfiguration xmlConfiguration = new XmlConfiguration(getXmlConfigurationUrl());
       xmlConfiguration.configure(server);
       server.start();
       server.join();
@@ -50,12 +50,11 @@ public class MoxoJettyRunner {
     }
   }
 
-  @SuppressWarnings({"SameParameterValue"})
-  private static URL getResource(String resource) {
-    URL url = MoxoJettyRunner.class.getResource("/" + resource);
+  private static URL getXmlConfigurationUrl() {
+    URL url = MoxoJettyRunner.class.getResource("/" + MoxoJettyRunner.CONF_JETTY_XML);
     if (null == url) {
       try {
-        url = new File(resource).toURL();
+        url = new File(MoxoJettyRunner.CONF_JETTY_XML).toURL();
         System.err.println("Loading configuration from file: " + url.toExternalForm());
       } catch (MalformedURLException e) {
         // ignore ...
blob - 15364b45c9595d79770be30f05df170501a74e69 (mode 644)
blob + /dev/null
--- src/main/java/com/thinkberg/moxo/ResourceManager.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright 2007 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;
-
-import org.apache.commons.vfs.FileObject;
-import org.apache.commons.vfs.FileSystemException;
-import org.apache.commons.vfs.FileSystemManager;
-import org.apache.commons.vfs.VFS;
-import org.jets3t.service.Jets3tProperties;
-
-/**
- * The resource manager is responsible for providing a virtual file system root.
- *
- * @author Matthias L. Jugel
- */
-public class ResourceManager {
-  private static FileObject root;
-
-  static {
-    try {
-      String propertiesFileName = System.getProperty("moxo.properties", "moxo.properties");
-      Jets3tProperties properties = Jets3tProperties.getInstance(propertiesFileName);
-      FileSystemManager fsm = VFS.getManager();
-      FileObject rootObject = fsm.resolveFile("s3://" + properties.getStringProperty("bucket", null));
-      root = fsm.createVirtualFileSystem(rootObject);
-      System.err.println("Created virtual file system: " + rootObject);
-    } catch (FileSystemException e) {
-      System.err.println("Can't create virtual file system: " + e.getMessage());
-      e.printStackTrace();
-    }
-  }
-
-  public static FileObject getFileObject(String path) throws FileSystemException {
-    return root.resolveFile(path);
-  }
-}
blob - b6e185d27e3e58d31107a6a92117d79beee64682
blob + 12c455d7b8e0da5274b892269a2919198c3cace9
--- src/main/java/com/thinkberg/moxo/dav/CopyMoveBase.java
+++ src/main/java/com/thinkberg/moxo/dav/CopyMoveBase.java
@@ -16,7 +16,6 @@
 
 package com.thinkberg.moxo.dav;
 
-import com.thinkberg.moxo.ResourceManager;
 import com.thinkberg.moxo.dav.lock.LockException;
 import com.thinkberg.moxo.dav.lock.LockManager;
 import org.apache.commons.vfs.FileObject;
@@ -35,7 +34,7 @@ public abstract class CopyMoveBase extends WebdavHandl
 
   public void service(HttpServletRequest request, HttpServletResponse response) throws IOException {
     boolean overwrite = getOverwrite(request);
-    FileObject object = ResourceManager.getFileObject(request.getPathInfo());
+    FileObject object = getVFSObject(request.getPathInfo());
     FileObject targetObject = getDestination(request);
 
     try {
blob - 3533537d1b03790bb012ae0befe0c5539f1330a5
blob + bd7f9830c75e3fb90449db9a7f2685aeeff2e237
--- src/main/java/com/thinkberg/moxo/dav/DeleteHandler.java
+++ src/main/java/com/thinkberg/moxo/dav/DeleteHandler.java
@@ -16,9 +16,10 @@
 
 package com.thinkberg.moxo.dav;
 
-import com.thinkberg.moxo.ResourceManager;
 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 org.apache.commons.vfs.FileObject;
 import org.apache.commons.vfs.FileSelectInfo;
 import org.apache.commons.vfs.FileSelector;
@@ -33,6 +34,7 @@ import java.io.IOException;
  * @version $Id$
  */
 public class DeleteHandler extends WebdavHandler {
+  private static final Log LOG = LogFactory.getLog(DeleteHandler.class);
 
   private final static FileSelector ALL_FILES_SELECTOR = new FileSelector() {
     public boolean includeFile(FileSelectInfo fileSelectInfo) throws Exception {
@@ -45,7 +47,7 @@ public class DeleteHandler extends WebdavHandler {
   };
 
   public void service(HttpServletRequest request, HttpServletResponse response) throws IOException {
-    FileObject object = ResourceManager.getFileObject(request.getPathInfo());
+    FileObject object = getVFSObject(request.getPathInfo());
     if (request instanceof Request) {
       String fragment = ((Request) request).getUri().getFragment();
       if (fragment != null) {
@@ -66,7 +68,9 @@ public class DeleteHandler extends WebdavHandler {
     }
 
     if (object.exists()) {
-      if (object.delete(ALL_FILES_SELECTOR) > 0) {
+      int deletedObjects = object.delete(ALL_FILES_SELECTOR);
+      LOG.debug("deleted " + deletedObjects + " objects");
+      if (deletedObjects > 0) {
         response.setStatus(HttpServletResponse.SC_OK);
       } else {
         response.sendError(HttpServletResponse.SC_FORBIDDEN);
blob - bcfd04dc1bfcf5fd118934e8a0108b8e9f592b13
blob + 2d54a47a9b4dc425e1300cefea568d7718c7f90f
--- src/main/java/com/thinkberg/moxo/dav/GetHandler.java
+++ src/main/java/com/thinkberg/moxo/dav/GetHandler.java
@@ -16,7 +16,6 @@
 
 package com.thinkberg.moxo.dav;
 
-import com.thinkberg.moxo.ResourceManager;
 import org.apache.commons.vfs.FileContent;
 import org.apache.commons.vfs.FileObject;
 import org.apache.commons.vfs.FileSystemException;
@@ -35,7 +34,7 @@ import java.io.OutputStream;
 public class GetHandler extends WebdavHandler {
 
   public void service(HttpServletRequest request, HttpServletResponse response) throws IOException {
-    FileObject object = ResourceManager.getFileObject(request.getPathInfo());
+    FileObject object = getVFSObject(request.getPathInfo());
 
     if (object.exists()) {
       if (FileType.FOLDER.equals(object.getType())) {
blob - 51fd09bff59cddb5c4212b9d20ddb56a28d8c5d3
blob + 214793924ede616c96a919382f76715c89613a58
--- src/main/java/com/thinkberg/moxo/dav/HeadHandler.java
+++ src/main/java/com/thinkberg/moxo/dav/HeadHandler.java
@@ -16,7 +16,6 @@
 
 package com.thinkberg.moxo.dav;
 
-import com.thinkberg.moxo.ResourceManager;
 import org.apache.commons.vfs.FileObject;
 import org.apache.commons.vfs.FileType;
 
@@ -31,7 +30,7 @@ import java.io.IOException;
 public class HeadHandler extends GetHandler {
 
   public void service(HttpServletRequest request, HttpServletResponse response) throws IOException {
-    FileObject object = ResourceManager.getFileObject(request.getPathInfo());
+    FileObject object = getVFSObject(request.getPathInfo());
 
     if (object.exists()) {
       if (FileType.FOLDER.equals(object.getType())) {
blob - 8e7e95dc2594e7ce20d4a8e2afd6b1d6729fb992
blob + d4223a80e73f36560895ea20948921ce11bb8739
--- src/main/java/com/thinkberg/moxo/dav/LockHandler.java
+++ src/main/java/com/thinkberg/moxo/dav/LockHandler.java
@@ -16,22 +16,21 @@
 
 package com.thinkberg.moxo.dav;
 
-import com.thinkberg.moxo.ResourceManager;
 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 org.apache.commons.vfs.FileObject;
-import org.dom4j.Document;
-import org.dom4j.DocumentException;
-import org.dom4j.DocumentHelper;
-import org.dom4j.Element;
-import org.dom4j.Node;
+import org.dom4j.*;
+import org.dom4j.io.OutputFormat;
 import org.dom4j.io.SAXReader;
 import org.dom4j.io.XMLWriter;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.net.URL;
 import java.util.Iterator;
@@ -43,6 +42,8 @@ import java.util.Iterator;
  * @version $Id$
  */
 public class LockHandler extends WebdavHandler {
+  private static final Log LOG = LogFactory.getLog(LockHandler.class);
+
   private static final String TAG_LOCKSCOPE = "lockscope";
   private static final String TAG_LOCKTYPE = "locktype";
   private static final String TAG_OWNER = "owner";
@@ -53,7 +54,7 @@ public class LockHandler extends WebdavHandler {
   private static final String HEADER_LOCK_TOKEN = "Lock-Token";
 
   public void service(HttpServletRequest request, HttpServletResponse response) throws IOException {
-    FileObject object = ResourceManager.getFileObject(request.getPathInfo());
+    FileObject object = getVFSObject(request.getPathInfo());
 
     try {
       Lock lock = LockManager.getInstance().checkCondition(object, getIf(request));
@@ -91,7 +92,7 @@ public class LockHandler extends WebdavHandler {
         }
       }
 
-      log("LOCK(" + lockType + ", " + lockScope + ", " + owner + ")");
+      LOG.debug("LOCK(" + lockType + ", " + lockScope + ", " + owner + ")");
 
       Lock requestedLock = new Lock(object, lockType, lockScope, owner, getDepth(request), getTimeout(request));
       try {
@@ -109,6 +110,9 @@ public class LockHandler extends WebdavHandler {
   }
 
   private void sendLockAcquiredResponse(HttpServletResponse response, Lock lock) throws IOException {
+    if (!lock.getObject().exists()) {
+      response.setStatus(SC_CREATED);
+    }
     response.setContentType("text/xml");
     response.setCharacterEncoding("UTF-8");
     response.setHeader(HEADER_LOCK_TOKEN, "<" + lock.getToken() + ">");
@@ -121,6 +125,18 @@ public class LockHandler extends WebdavHandler {
 
     XMLWriter xmlWriter = new XMLWriter(response.getWriter());
     xmlWriter.write(propDoc);
-    log(propDoc);
+
+    logXml(propDoc);
   }
+
+  private void logXml(Node element) {
+    ByteArrayOutputStream bos = new ByteArrayOutputStream();
+    try {
+      XMLWriter xmlWriter = new XMLWriter(bos, OutputFormat.createPrettyPrint());
+      xmlWriter.write(element);
+      LOG.debug(bos.toString());
+    } catch (IOException e) {
+      LOG.debug("ERROR writing XML log: " + e.getMessage());
+    }
+  }
 }
blob - c7bd42b7da7d33d12157b1d0f6dac1a96b20dfbb
blob + 26c2f2d817fea565d73ce19efee0bf802f26c328
--- src/main/java/com/thinkberg/moxo/dav/MkColHandler.java
+++ src/main/java/com/thinkberg/moxo/dav/MkColHandler.java
@@ -16,7 +16,6 @@
 
 package com.thinkberg.moxo.dav;
 
-import com.thinkberg.moxo.ResourceManager;
 import com.thinkberg.moxo.dav.lock.LockException;
 import com.thinkberg.moxo.dav.lock.LockManager;
 import org.apache.commons.vfs.FileObject;
@@ -25,6 +24,7 @@ import org.apache.commons.vfs.FileType;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
+import java.io.BufferedReader;
 import java.io.IOException;
 
 /**
@@ -34,12 +34,14 @@ import java.io.IOException;
 public class MkColHandler extends WebdavHandler {
 
   public void service(HttpServletRequest request, HttpServletResponse response) throws IOException {
-    if (request.getReader().readLine() != null) {
+    BufferedReader bufferedReader = request.getReader();
+    String line = bufferedReader.readLine();
+    if (line != null) {
       response.sendError(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);
       return;
     }
 
-    FileObject object = ResourceManager.getFileObject(request.getPathInfo());
+    FileObject object = getVFSObject(request.getPathInfo());
 
     try {
       LockManager.getInstance().checkCondition(object, getIf(request));
blob - fb5876098ccd381b67aa1aa2065a55e2ee93d7af
blob + 105b947a18e598dc34be32c8564a80e80f7fa409
--- src/main/java/com/thinkberg/moxo/dav/OptionsHandler.java
+++ src/main/java/com/thinkberg/moxo/dav/OptionsHandler.java
@@ -16,7 +16,6 @@
 
 package com.thinkberg.moxo.dav;
 
-import com.thinkberg.moxo.ResourceManager;
 import org.apache.commons.vfs.FileObject;
 import org.apache.commons.vfs.FileType;
 
@@ -35,7 +34,7 @@ public class OptionsHandler extends WebdavHandler {
 
     String path = request.getPathInfo();
     StringBuffer options = new StringBuffer();
-    FileObject object = ResourceManager.getFileObject(path);
+    FileObject object = getVFSObject(path);
     if (object.exists()) {
       options.append("OPTIONS, GET, HEAD, POST, DELETE, TRACE, COPY, MOVE, LOCK, UNLOCK, PROPFIND");
       if (FileType.FOLDER.equals(object.getType())) {
blob - 169c09204e05f26f192ab23f7ad567443352bb55
blob + 9f8b33f2d2f8c952a1dba98b56c888dad88670b3
--- src/main/java/com/thinkberg/moxo/dav/PropFindHandler.java
+++ src/main/java/com/thinkberg/moxo/dav/PropFindHandler.java
@@ -16,22 +16,21 @@
 
 package com.thinkberg.moxo.dav;
 
-import com.thinkberg.moxo.ResourceManager;
 import com.thinkberg.moxo.dav.data.DavResource;
 import com.thinkberg.moxo.dav.data.DavResourceFactory;
 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.FileSystemException;
-import org.dom4j.Document;
-import org.dom4j.DocumentException;
-import org.dom4j.DocumentHelper;
-import org.dom4j.Element;
+import org.dom4j.*;
 import org.dom4j.io.OutputFormat;
 import org.dom4j.io.SAXReader;
 import org.dom4j.io.XMLWriter;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.net.URL;
 import java.util.ArrayList;
@@ -48,12 +47,26 @@ public class PropFindHandler extends WebdavHandler {
   private static final String TAG_MULTISTATUS = "multistatus";
   private static final String TAG_HREF = "href";
   private static final String TAG_RESPONSE = "response";
+  private static final Log LOG = LogFactory.getLog(PropFindHandler.class);
 
+  void logXml(Node element) {
+    ByteArrayOutputStream bos = new ByteArrayOutputStream();
+    try {
+      XMLWriter xmlWriter = new XMLWriter(bos, OutputFormat.createPrettyPrint());
+      xmlWriter.write(element);
+      LOG.debug(bos.toString());
+    } catch (IOException e) {
+      LOG.error(e.getMessage());
+    }
+  }
+
+
   public void service(HttpServletRequest request, HttpServletResponse response) throws IOException {
     SAXReader saxReader = new SAXReader();
     try {
       Document propDoc = saxReader.read(request.getInputStream());
-      // log(propDoc);
+      logXml(propDoc);
+
       Element propFindEl = propDoc.getRootElement();
       Element propEl = (Element) propFindEl.elementIterator().next();
       String propElName = propEl.getName();
@@ -71,7 +84,7 @@ public class PropFindHandler extends WebdavHandler {
         ignoreValues = true;
       }
 
-      FileObject object = ResourceManager.getFileObject(request.getPathInfo());
+      FileObject object = getVFSObject(request.getPathInfo());
       if (object.exists()) {
         // respond as XML encoded multi status
         response.setContentType("text/xml");
@@ -84,7 +97,7 @@ public class PropFindHandler extends WebdavHandler {
                                       getBaseUrl(request),
                                       getDepth(request),
                                       ignoreValues);
-        //log(multiStatusResponse);
+        logXml(multiStatusResponse);
 
         // write the actual response
         XMLWriter writer = new XMLWriter(response.getWriter(), OutputFormat.createCompactFormat());
@@ -93,15 +106,16 @@ public class PropFindHandler extends WebdavHandler {
         writer.close();
 
       } else {
-        log("!! " + object.getName().getPath() + " NOT FOUND");
+        LOG.error(object.getName().getPath() + " NOT FOUND");
         response.sendError(HttpServletResponse.SC_NOT_FOUND);
       }
     } catch (DocumentException e) {
-      log("!! inavlid request: " + e.getMessage());
+      LOG.error("invalid request: " + e.getMessage());
       response.sendError(HttpServletResponse.SC_BAD_REQUEST);
     }
   }
 
+  @SuppressWarnings({"ConstantConditions"})
   private Document getMultiStatusRespons(FileObject object,
                                          List<String> requestedProperties,
                                          URL baseUrl,
@@ -116,7 +130,7 @@ public class PropFindHandler extends WebdavHandler {
       Element responseEl = multiStatus.addElement(TAG_RESPONSE);
       try {
         URL url = new URL(baseUrl, URLEncoder.encode(child.getName().getPath(), "UTF-8"));
-        log("!! " + url);
+        LOG.debug(url);
         responseEl.addElement(TAG_HREF).addText(url.toExternalForm());
       } catch (Exception e) {
         e.printStackTrace();
blob - 9848117271155f8ff4227471f202c991479dffba
blob + 47200c23bde07c62df6a59cebb06f1d458fe4661
--- src/main/java/com/thinkberg/moxo/dav/PropPatchHandler.java
+++ src/main/java/com/thinkberg/moxo/dav/PropPatchHandler.java
@@ -16,10 +16,11 @@
 
 package com.thinkberg.moxo.dav;
 
-import com.thinkberg.moxo.ResourceManager;
 import com.thinkberg.moxo.dav.data.DavResource;
 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 org.apache.commons.vfs.FileObject;
 import org.dom4j.Document;
 import org.dom4j.DocumentException;
@@ -42,8 +43,10 @@ import java.util.List;
  * @author Matthias L. Jugel
  */
 public class PropPatchHandler extends WebdavHandler {
+  private static final Log LOG = LogFactory.getLog(PropPatchHandler.class);
+
   public void service(HttpServletRequest request, HttpServletResponse response) throws IOException {
-    FileObject object = ResourceManager.getFileObject(request.getPathInfo());
+    FileObject object = getVFSObject(request.getPathInfo());
 
     try {
       LockManager.getInstance().checkCondition(object, getIf(request));
@@ -71,7 +74,7 @@ public class PropPatchHandler extends WebdavHandler {
         Element responseEl = multiStatusResponse.addElement("response");
         try {
           URL url = new URL(getBaseUrl(request), URLEncoder.encode(object.getName().getPath(), "UTF-8"));
-          log("!! " + url);
+          LOG.debug(url);
           responseEl.addElement("href").addText(url.toExternalForm());
         } catch (Exception e) {
           e.printStackTrace();
@@ -103,11 +106,11 @@ public class PropPatchHandler extends WebdavHandler {
         writer.flush();
         writer.close();
       } else {
-        log("!! " + object.getName().getPath() + " NOT FOUND");
+        LOG.error(object.getName().getPath() + " NOT FOUND");
         response.sendError(HttpServletResponse.SC_NOT_FOUND);
       }
     } catch (DocumentException e) {
-      log("!! inavlid request: " + e.getMessage());
+      LOG.error("invalid request: " + e.getMessage());
       response.sendError(HttpServletResponse.SC_BAD_REQUEST);
     }
   }
blob - 7a8e901869c402cbbb6c27b78bb16ebe5cec3292
blob + fc92a3d6e348a399694d0d684681d25c85f99d27
--- src/main/java/com/thinkberg/moxo/dav/PutHandler.java
+++ src/main/java/com/thinkberg/moxo/dav/PutHandler.java
@@ -16,9 +16,10 @@
 
 package com.thinkberg.moxo.dav;
 
-import com.thinkberg.moxo.ResourceManager;
 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 org.apache.commons.vfs.FileObject;
 import org.apache.commons.vfs.FileType;
 
@@ -33,8 +34,10 @@ import java.io.OutputStream;
  * @version $Id$
  */
 public class PutHandler extends WebdavHandler {
+  private static final Log LOG = LogFactory.getLog(PutHandler.class);
+
   public void service(HttpServletRequest request, HttpServletResponse response) throws IOException {
-    FileObject object = ResourceManager.getFileObject(request.getPathInfo());
+    FileObject object = getVFSObject(request.getPathInfo());
 
     try {
       LockManager.getInstance().checkCondition(object, getIf(request));
@@ -66,8 +69,8 @@ public class PutHandler extends WebdavHandler {
 
     InputStream is = request.getInputStream();
     OutputStream os = object.getContent().getOutputStream();
-    log("PUT sends " + request.getHeader("Content-length") + " bytes");
-    log("PUT copied " + Util.copyStream(is, os) + " bytes");
+    int bytesCopied = Util.copyStream(is, os);
+    LOG.debug("sending " + bytesCopied + "/" + request.getHeader("Content-length") + " bytes");
     os.flush();
     object.close();
 
blob - 7e66661da892e8d3514f76702c740496e62ced5d
blob + cdf9f6df05dd32310e05a5e0ab104c03779b15ea
--- src/main/java/com/thinkberg/moxo/dav/URLEncoder.java
+++ src/main/java/com/thinkberg/moxo/dav/URLEncoder.java
@@ -26,7 +26,7 @@ import java.util.BitSet;
  */
 class URLEncoder {
 
-  private static BitSet keepPlain;
+  private static final BitSet keepPlain;
 
   static {
     keepPlain = new BitSet(256);
@@ -50,7 +50,6 @@ class URLEncoder {
   }
 
 
-  @SuppressWarnings({"SameParameterValue"})
   public static String encode(String s, String enc) throws UnsupportedEncodingException {
     byte[] buf = s.getBytes(enc);
     StringBuffer result = new StringBuffer();
blob - c9b1dbfa79cede571c23e5fbce3af2951df4c49e
blob + e9810818ac9e79873202dae184ec9ad09b813804
--- src/main/java/com/thinkberg/moxo/dav/UnlockHandler.java
+++ src/main/java/com/thinkberg/moxo/dav/UnlockHandler.java
@@ -16,8 +16,9 @@
 
 package com.thinkberg.moxo.dav;
 
-import com.thinkberg.moxo.ResourceManager;
 import com.thinkberg.moxo.dav.lock.LockManager;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 import org.apache.commons.vfs.FileObject;
 
 import javax.servlet.http.HttpServletRequest;
@@ -29,12 +30,13 @@ import java.io.IOException;
  * @version $Id$
  */
 public class UnlockHandler extends WebdavHandler {
+  private static final Log LOG = LogFactory.getLog(UnlockHandler.class);
 
   public void service(HttpServletRequest request, HttpServletResponse response) throws IOException {
-    FileObject object = ResourceManager.getFileObject(request.getPathInfo());
+    FileObject object = getVFSObject(request.getPathInfo());
     String lockTokenHeader = request.getHeader("Lock-Token");
     String lockToken = lockTokenHeader.substring(1, lockTokenHeader.length() - 1);
-    log("UNLOCK(" + lockToken + ")");
+    LOG.debug("UNLOCK(" + lockToken + ")");
 
     if (LockManager.getInstance().releaseLock(object, lockToken)) {
       response.setStatus(HttpServletResponse.SC_NO_CONTENT);
blob - 635e944e7bab4630c048790a9115f02f07481a7c
blob + 6cc9911ce6b996e0c0610c4987c090772f356817
--- src/main/java/com/thinkberg/moxo/dav/Util.java
+++ src/main/java/com/thinkberg/moxo/dav/Util.java
@@ -36,7 +36,7 @@ public class Util {
   }
 
 //  public static String getISODateString(long time) {
-//    return "";
+//    return "";    4
 //  }
 
 
blob - a74e1a9ff4bbc10d2647532ca2252c2a48acd7c8
blob + bb559777fd0928f6c9ee3f7bb12b897d76ffc0d7
--- src/main/java/com/thinkberg/moxo/dav/WebdavHandler.java
+++ src/main/java/com/thinkberg/moxo/dav/WebdavHandler.java
@@ -16,17 +16,16 @@
 
 package com.thinkberg.moxo.dav;
 
-import com.thinkberg.moxo.ResourceManager;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 import org.apache.commons.vfs.FileObject;
 import org.apache.commons.vfs.FileSystemException;
-import org.dom4j.Node;
-import org.dom4j.io.OutputFormat;
-import org.dom4j.io.XMLWriter;
+import org.apache.commons.vfs.FileSystemManager;
+import org.apache.commons.vfs.VFS;
+import org.jets3t.service.Jets3tProperties;
 
-import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.net.MalformedURLException;
 import java.net.URL;
@@ -37,34 +36,35 @@ import java.util.Arrays;
  * @version $Id$
  */
 public abstract class WebdavHandler {
+  private static final Log LOG = LogFactory.getLog(WebdavHandler.class);
+
+  static final int SC_CREATED = 201;
   static final int SC_LOCKED = 423;
   static final int SC_MULTI_STATUS = 207;
 
-  private HttpServlet servlet;
+  private static FileObject fileSystemRoot;
 
-  public void setServlet(HttpServlet servlet) {
-    this.servlet = servlet;
-  }
-
-  public abstract void service(HttpServletRequest request, HttpServletResponse response) throws IOException;
-
-  void log(Node element) {
-    ByteArrayOutputStream bos = new ByteArrayOutputStream();
+  static {
     try {
-      XMLWriter xmlWriter = new XMLWriter(bos, OutputFormat.createPrettyPrint());
-      xmlWriter.write(element);
-      System.out.print(bos.toString());
-    } catch (IOException e) {
-      servlet.log("!! " + e.getMessage());
+      String propertiesFileName = System.getProperty("moxo.properties", "moxo.properties");
+      Jets3tProperties properties = Jets3tProperties.getInstance(propertiesFileName);
+      FileSystemManager fsm = VFS.getManager();
+
+      // create a virtual filesystemusing the url provided or fall back to RAM
+      fileSystemRoot = fsm.resolveFile(properties.getStringProperty("vfs.url", "ram:/"));
+
+      LOG.info("created virtual file system: " + fileSystemRoot);
+    } catch (FileSystemException e) {
+      LOG.error("can't create virtual file system: " + e.getMessage());
+      e.printStackTrace();
     }
   }
 
-  void log(String message) {
-    servlet.log("## " + message);
+  protected static FileObject getVFSObject(String path) throws FileSystemException {
+    return fileSystemRoot.resolveFile(path);
   }
 
-
-  static URL getBaseUrl(HttpServletRequest request) {
+  protected static URL getBaseUrl(HttpServletRequest request) {
     try {
       String requestUrl = request.getRequestURL().toString();
       String requestUri = request.getRequestURI();
@@ -76,6 +76,7 @@ public abstract class WebdavHandler {
     return null;
   }
 
+  public abstract void service(HttpServletRequest request, HttpServletResponse response) throws IOException;
 
   /**
    * Get the depth header value. This value defines how operations
@@ -96,7 +97,7 @@ public abstract class WebdavHandler {
       depthValue = Integer.parseInt(depth);
     }
 
-    log("Depth: " + depthValue);
+    LOG.debug("request header: Depth: " + (depthValue == Integer.MAX_VALUE ? "infinity" : depthValue));
     return depthValue;
   }
 
@@ -110,7 +111,8 @@ public abstract class WebdavHandler {
   boolean getOverwrite(HttpServletRequest request) {
     String overwrite = request.getHeader("Overwrite");
     boolean overwriteValue = overwrite == null || "T".equals(overwrite);
-    log("Overwrite: " + overwriteValue);
+
+    LOG.debug("request header: Overwrite: " + overwriteValue);
     return overwriteValue;
   }
 
@@ -128,8 +130,8 @@ public abstract class WebdavHandler {
     FileObject targetObject = null;
     if (null != targetUrlStr) {
       URL target = new URL(targetUrlStr);
-      targetObject = ResourceManager.getFileObject(target.getPath());
-      log("Destination: " + targetObject.getName().getPath());
+      targetObject = getVFSObject(target.getPath());
+      LOG.debug("request header: Destination: " + targetObject.getName().getPath());
     }
 
     return targetObject;
@@ -142,7 +144,12 @@ public abstract class WebdavHandler {
    * @return the value if the If: header.
    */
   String getIf(HttpServletRequest request) {
-    return request.getHeader("If");
+    String getIfHeader = request.getHeader("If");
+
+    if (null != getIfHeader) {
+      LOG.debug("request header: If: " + getIfHeader);
+    }
+    return getIfHeader;
   }
 
   /**
@@ -155,7 +162,7 @@ public abstract class WebdavHandler {
     String timeout = request.getHeader("Timeout");
     if (null != timeout) {
       String[] timeoutValues = timeout.split(",[ ]*");
-      log(Arrays.asList(timeoutValues).toString());
+      LOG.debug("request header: Timeout: " + Arrays.asList(timeoutValues).toString());
       if ("infinity".equalsIgnoreCase(timeoutValues[0])) {
         return -1;
       } else {
blob - 4356ce41848ea62de23b8a036e7aea950df89046
blob + 82ec809f38ec9a22f32b81ba769787173ef5df2d
--- src/main/java/com/thinkberg/moxo/dav/data/AbstractDavResource.java
+++ src/main/java/com/thinkberg/moxo/dav/data/AbstractDavResource.java
@@ -35,7 +35,6 @@ public abstract class AbstractDavResource {
   private static final String TAG_PROP = "prop";
   private static final String TAG_STATUS = "status";
 
-  @SuppressWarnings({"UnusedReturnValue"})
   public Element serializeToXml(Element root, List<String> requestedProperties) {
     Element propStatEl = root.addElement(TAG_PROPSTAT);
     Element propEl = propStatEl.addElement(TAG_PROP);
@@ -61,5 +60,6 @@ public abstract class AbstractDavResource {
     return root;
   }
 
+  @SuppressWarnings({"BooleanMethodIsAlwaysInverted"})
   protected abstract boolean addPropertyValue(Element root, String propertyName);
 }
blob - 918da1c04a6de60ad3be17615e2e920da8a2975b
blob + fcd185c553c3e877a601f195e39e4d1687e16c32
--- src/main/java/com/thinkberg/moxo/dav/data/DavCollection.java
+++ src/main/java/com/thinkberg/moxo/dav/data/DavCollection.java
@@ -71,7 +71,7 @@ public class DavCollection extends DavResource {
   }
 
   protected boolean addQuotaProperty(Element root) {
-    root.addElement(PROP_QUOTA).addText("1000000");
+    root.addElement(PROP_QUOTA).addText("" + Long.MAX_VALUE);
     return true;
   }
 
@@ -82,7 +82,7 @@ public class DavCollection extends DavResource {
   }
 
   protected boolean addQuotaAvailableBytesProperty(Element root) {
-    root.addElement(PROP_QUOTA_AVAILABLE_BYTES).addText("1000000");
+    root.addElement(PROP_QUOTA_AVAILABLE_BYTES).addText("" + Long.MAX_VALUE);
     return true;
   }
 
blob - 220a25349e7c8e941c9ae4944459052216833000
blob + 60ddbc7d9240cdb82e2d97eba5c93761a077ca9d
--- src/main/java/com/thinkberg/moxo/dav/data/DavResource.java
+++ src/main/java/com/thinkberg/moxo/dav/data/DavResource.java
@@ -30,7 +30,6 @@ import java.util.List;
  * @author Matthias L. Jugel
  * @version $Id$
  */
-@SuppressWarnings({"SameReturnValue"})
 public class DavResource extends AbstractDavResource {
 
   // @see http://www.webdav.org/specs/rfc2518.html#dav.properties
@@ -64,11 +63,12 @@ public class DavResource extends AbstractDavResource {
           PROP_LOCK_DISCOVERY,
           PROP_RESOURCETYPE,
           PROP_SOURCE,
-          PROP_SUPPORTED_LOCK
+          PROP_SUPPORTED_LOCK,
+          PROP_QUOTA_AVAILABLE_BYTES
   );
 
-  private final FileObject object;
-  private boolean ignoreValues = false;
+  protected final FileObject object;
+  protected boolean ignoreValues = false;
 
   public DavResource(FileObject object) {
     this(object, false);
@@ -138,12 +138,10 @@ public class DavResource extends AbstractDavResource {
     return false;
   }
 
-  @SuppressWarnings({"WeakerAccess", "UnusedParameters"})
   protected boolean addCreationDateProperty(Element root) {
     return false;
   }
 
-  @SuppressWarnings({"WeakerAccess"})
   protected boolean addGetDisplayNameProperty(Element root) {
     Element el = root.addElement(PROP_DISPLAY_NAME);
     if (!ignoreValues) {
@@ -152,12 +150,10 @@ public class DavResource extends AbstractDavResource {
     return true;
   }
 
-  @SuppressWarnings({"WeakerAccess", "UnusedParameters"})
   protected boolean addGetContentLanguageProperty(Element root) {
     return false;
   }
 
-  @SuppressWarnings({"WeakerAccess"})
   protected boolean addGetContentLengthProperty(Element root) {
     try {
       Element el = root.addElement(PROP_GET_CONTENT_LENGTH);
@@ -171,7 +167,6 @@ public class DavResource extends AbstractDavResource {
     }
   }
 
-  @SuppressWarnings({"WeakerAccess"})
   protected boolean addGetContentTypeProperty(Element root) {
     try {
       String contentType = object.getContent().getContentInfo().getContentType();
@@ -190,12 +185,10 @@ public class DavResource extends AbstractDavResource {
     }
   }
 
-  @SuppressWarnings({"WeakerAccess", "UnusedParameters"})
   protected boolean addGetETagProperty(Element root) {
     return false;
   }
 
-  @SuppressWarnings({"WeakerAccess"})
   protected boolean addGetLastModifiedProperty(Element root) {
     try {
       Element el = root.addElement(PROP_GET_LAST_MODIFIED);
@@ -209,7 +202,6 @@ public class DavResource extends AbstractDavResource {
     }
   }
 
-  @SuppressWarnings({"WeakerAccess"})
   protected boolean addLockDiscoveryProperty(Element root) {
     Element lockdiscoveryEl = root.addElement(PROP_LOCK_DISCOVERY);
     try {
@@ -229,18 +221,15 @@ public class DavResource extends AbstractDavResource {
     }
   }
 
-  @SuppressWarnings({"WeakerAccess"})
   protected boolean addResourceTypeProperty(Element root) {
     root.addElement(PROP_RESOURCETYPE);
     return true;
   }
 
-  @SuppressWarnings({"WeakerAccess", "UnusedParameters"})
   protected boolean addSourceProperty(Element root) {
     return false;
   }
 
-  @SuppressWarnings({"WeakerAccess"})
   protected boolean addSupportedLockProperty(Element root) {
     Element supportedlockEl = root.addElement(PROP_SUPPORTED_LOCK);
     if (!ignoreValues) {
@@ -255,22 +244,18 @@ public class DavResource extends AbstractDavResource {
     return true;
   }
 
-  @SuppressWarnings({"WeakerAccess"})
   protected boolean addQuotaProperty(Element root) {
     return false;
   }
 
-  @SuppressWarnings({"WeakerAccess"})
   protected boolean addQuotaUsedProperty(Element root) {
     return false;
   }
 
-  @SuppressWarnings({"WeakerAccess"})
   protected boolean addQuotaAvailableBytesProperty(Element root) {
     return false;
   }
 
-  @SuppressWarnings({"WeakerAccess"})
   protected boolean addQuotaUsedBytesProperty(Element root) {
     return false;
   }
blob - 44f4c9aaa4abefb4b0565650ccaa2960ed875225
blob + 28d71a7844d3ecca4d5bc448fb9e7c6e47913291
--- src/main/java/com/thinkberg/moxo/dav/lock/Lock.java
+++ src/main/java/com/thinkberg/moxo/dav/lock/Lock.java
@@ -72,7 +72,6 @@ public class Lock {
     return depth;
   }
 
-  @SuppressWarnings({"WeakerAccess"})
   public String getDepthValue() {
     switch (depth) {
       case 0:
@@ -84,7 +83,6 @@ public class Lock {
     }
   }
 
-  @SuppressWarnings({"WeakerAccess"})
   public String getTimeout() {
     if (timeout == -1) {
       return "Infinity";
@@ -103,7 +101,6 @@ public class Lock {
    * @param root the root element to add the activelock to
    * @return the root element
    */
-  @SuppressWarnings({"UnusedReturnValue"})
   public Element serializeToXml(Element root) {
     Element activelockEl = root.addElement("activelock");
     activelockEl.addElement("locktype").addElement(getType());
@@ -130,7 +127,7 @@ public class Lock {
    * @return whether this lock is for the same file object
    */
   public boolean equals(Object other) {
-    return object.equals(((Lock) other).object);
+    return other instanceof Lock && object.equals(((Lock) other).object);
   }
 
 
blob - ee85960287fcb8693e420e89092915792c5c13e3 (mode 644)
blob + /dev/null
--- src/main/java/com/thinkberg/moxo/servlet/MoxoS3WebdavServlet.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright 2007 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.servlet;
-
-import com.thinkberg.moxo.dav.CopyHandler;
-import com.thinkberg.moxo.dav.DeleteHandler;
-import com.thinkberg.moxo.dav.GetHandler;
-import com.thinkberg.moxo.dav.HeadHandler;
-import com.thinkberg.moxo.dav.LockHandler;
-import com.thinkberg.moxo.dav.MkColHandler;
-import com.thinkberg.moxo.dav.MoveHandler;
-import com.thinkberg.moxo.dav.OptionsHandler;
-import com.thinkberg.moxo.dav.PostHandler;
-import com.thinkberg.moxo.dav.PropFindHandler;
-import com.thinkberg.moxo.dav.PropPatchHandler;
-import com.thinkberg.moxo.dav.PutHandler;
-import com.thinkberg.moxo.dav.UnlockHandler;
-import com.thinkberg.moxo.dav.WebdavHandler;
-import org.mortbay.jetty.Response;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * @author Matthias L. Jugel
- * @version $Id$
- */
-public class MoxoS3WebdavServlet extends HttpServlet {
-  private final Map<String, WebdavHandler> handlers = new HashMap<String, WebdavHandler>();
-
-  public MoxoS3WebdavServlet() {
-    handlers.put("COPY", new CopyHandler());
-    handlers.put("DELETE", new DeleteHandler());
-    handlers.put("GET", new GetHandler());
-    handlers.put("HEAD", new HeadHandler());
-    handlers.put("LOCK", new LockHandler());
-    handlers.put("MKCOL", new MkColHandler());
-    handlers.put("MOVE", new MoveHandler());
-    handlers.put("OPTIONS", new OptionsHandler());
-    handlers.put("POST", new PostHandler());
-    handlers.put("PROPFIND", new PropFindHandler());
-    handlers.put("PROPPATCH", new PropPatchHandler());
-    handlers.put("PUT", new PutHandler());
-    handlers.put("UNLOCK", new UnlockHandler());
-
-    for (WebdavHandler handler : handlers.values()) {
-      handler.setServlet(this);
-    }
-  }
-
-  public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
-    String method = request.getMethod();
-    log(">> " + request.getMethod() + " " + request.getPathInfo());
-    if (request.getHeader("X-Litmus") != null) {
-      log("!! " + request.getHeader("X-Litmus"));
-    }
-    if (handlers.containsKey(method)) {
-      handlers.get(method).service(request, response);
-    } else {
-      response.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
-    }
-    Response jettyResponse = ((Response) response);
-    String reason = jettyResponse.getReason();
-    log("<< " + jettyResponse.getStatus() + (reason != null ? ": " + reason : ""));
-  }
-}
blob - /dev/null
blob + c8a306a053d53a8a29bdcfae8a10e3c852f7f3b0 (mode 644)
--- /dev/null
+++ src/main/java/com/thinkberg/moxo/servlet/MoxoWebDAVServlet.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2007 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.servlet;
+
+import com.thinkberg.moxo.dav.*;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.vfs.FileObject;
+import org.mortbay.jetty.Response;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author Matthias L. Jugel
+ * @version $Id$
+ */
+public class MoxoWebDAVServlet extends HttpServlet {
+  private static final Log LOG = LogFactory.getLog(MoxoWebDAVServlet.class);
+
+  private final Map<String, WebdavHandler> handlers = new HashMap<String, WebdavHandler>();
+  private FileObject fileSystemRoot = null;
+
+  public MoxoWebDAVServlet() {
+    handlers.put("COPY", new CopyHandler());
+    handlers.put("DELETE", new DeleteHandler());
+    handlers.put("GET", new GetHandler());
+    handlers.put("HEAD", new HeadHandler());
+    handlers.put("LOCK", new LockHandler());
+    handlers.put("MKCOL", new MkColHandler());
+    handlers.put("MOVE", new MoveHandler());
+    handlers.put("OPTIONS", new OptionsHandler());
+    handlers.put("POST", new PostHandler());
+    handlers.put("PROPFIND", new PropFindHandler());
+    handlers.put("PROPPATCH", new PropPatchHandler());
+    handlers.put("PUT", new PutHandler());
+    handlers.put("UNLOCK", new UnlockHandler());
+  }
+
+  protected FileObject getFileSystemRoot() {
+    return fileSystemRoot;
+  }
+
+  public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+//    String auth = request.getHeader("Authorization");
+//    String login = "", password = "";
+//
+//    if (auth != null) {
+//      auth = new String(Base64.decodeBase64(auth.substring(auth.indexOf(' ') + 1).getBytes()));
+//      login = auth.substring(0, auth.indexOf(':'));
+//      password = auth.substring(auth.indexOf(':') + 1);
+//    }
+//
+//    AWSCredentials credentials = AWSCredentials.load(password,  ))
+//    if (user == null) {
+//      response.setHeader("WWW-Authenticate", "Basic realm=\"Moxo\"");
+//      response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+//      return;
+//    }
+
+
+    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);
+    } else {
+      response.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
+    }
+    Response jettyResponse = ((Response) response);
+    String reason = jettyResponse.getReason();
+    LOG.debug(String.format("<< %s (%b%s)", request.getMethod(), jettyResponse.getStatus(), reason != null ? ": " + reason : ""));
+  }
+}
blob - c955d451826291a42e1b6aac6197ae8efee01db4
blob + 6e622e49ceb530d686040b60ba8b310b252b7d62
--- src/main/java/com/thinkberg/moxo/vfs/DepthFileSelector.java
+++ src/main/java/com/thinkberg/moxo/vfs/DepthFileSelector.java
@@ -47,7 +47,6 @@ public class DepthFileSelector implements FileSelector
     this(0, depth);
   }
 
-  @SuppressWarnings({"SameParameterValue"})
   public DepthFileSelector(int min, int max) {
     minDepth = min;
     maxDepth = max;
blob - 5768fc6965485ff468abf9e84001279acbb70358
blob + 1dd702d68b6fbe36e2acde95debb98faa1b29a0f
--- src/main/java/com/thinkberg/moxo/vfs/S3FileName.java
+++ src/main/java/com/thinkberg/moxo/vfs/S3FileName.java
@@ -23,7 +23,6 @@ import org.apache.commons.vfs.provider.local.LocalFile
  * @author Matthias L. Jugel
  */
 public class S3FileName extends LocalFileName {
-  @SuppressWarnings({"WeakerAccess"})
   protected S3FileName(final String scheme, final String rootFile, final String path, final FileType type) {
     super(scheme, rootFile, path, type);
   }
blob - 9c5d272ffd3f18b369af876efa3fa7762060d730
blob + 34a5d56d0239c568932ce009f30c10cda2b451c7
--- src/main/java/com/thinkberg/moxo/vfs/S3FileNameParser.java
+++ src/main/java/com/thinkberg/moxo/vfs/S3FileNameParser.java
@@ -37,7 +37,6 @@ public class S3FileNameParser extends AbstractFileName
 
   }
 
-
   public FileName parseUri(final VfsComponentContext context, final FileName base, final String filename) throws FileSystemException {
     StringBuffer name = new StringBuffer();
 
blob - 1f34a86342e7cb4329618df20eb9bd5c024e96ca
blob + 8caafbfd75cd2ba1fa4dbd73d893b8cd3df66fd1
--- src/main/java/com/thinkberg/moxo/vfs/S3FileProvider.java
+++ src/main/java/com/thinkberg/moxo/vfs/S3FileProvider.java
@@ -16,11 +16,7 @@
 
 package com.thinkberg.moxo.vfs;
 
-import org.apache.commons.vfs.Capability;
-import org.apache.commons.vfs.FileName;
-import org.apache.commons.vfs.FileSystem;
-import org.apache.commons.vfs.FileSystemException;
-import org.apache.commons.vfs.FileSystemOptions;
+import org.apache.commons.vfs.*;
 import org.apache.commons.vfs.provider.AbstractOriginatingFileProvider;
 
 import java.util.Arrays;
@@ -35,7 +31,7 @@ import java.util.Collections;
  */
 public class S3FileProvider extends AbstractOriginatingFileProvider {
 
-  public final static Collection capabilities = Collections.unmodifiableCollection(Arrays.asList(
+  public final static Collection<Capability> capabilities = Collections.unmodifiableCollection(Arrays.asList(
           Capability.CREATE,
           Capability.DELETE,
           Capability.RENAME,
@@ -53,7 +49,6 @@ public class S3FileProvider extends AbstractOriginatin
 
   ));
 
-
   public S3FileProvider() {
     super();
     setFileNameParser(S3FileNameParser.getInstance());
blob - 8352c585cb21de0f0c73b65170f9e14120ae23b0
blob + 1f080072887e8f54191f85b14ae1e3d922de11bc
--- src/main/java/com/thinkberg/moxo/vfs/jets3t/Jets3tConnector.java
+++ src/main/java/com/thinkberg/moxo/vfs/jets3t/Jets3tConnector.java
@@ -16,6 +16,8 @@
 
 package com.thinkberg.moxo.vfs.jets3t;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 import org.jets3t.service.Jets3tProperties;
 import org.jets3t.service.S3Service;
 import org.jets3t.service.S3ServiceException;
@@ -26,6 +28,8 @@ import org.jets3t.service.security.AWSCredentials;
  * @author Matthias L. Jugel
  */
 public class Jets3tConnector {
+  private static final Log LOG = LogFactory.getLog(Jets3tConnector.class);
+
   private static final String APPLICATION_DESCRIPTION = "S3 VFS Connector/1.0";
 
   private static Jets3tConnector instance;
@@ -54,7 +58,6 @@ public class Jets3tConnector {
    *          if the service cannot be accessed
    */
   private Jets3tConnector() throws S3ServiceException {
-    System.err.print("Authenticated to Amazon S3: ");
     String propertiesFileName = System.getProperty("moxo.properties", "moxo.properties");
     Jets3tProperties properties = Jets3tProperties.getInstance(propertiesFileName);
 
@@ -62,13 +65,14 @@ public class Jets3tConnector {
       throw new S3ServiceException("can't find S3 configuration: " + propertiesFileName);
     }
 
+
     AWSCredentials awsCredentials = new AWSCredentials(
             properties.getStringProperty("accesskey", null),
             properties.getStringProperty("secretkey", null));
 
 
     service = new RestS3Service(awsCredentials, APPLICATION_DESCRIPTION, null);
-    System.err.println("OK");
+    LOG.info("S3 authentication succeeded");
   }
 
   public S3Service getService() {
blob - f5fd3a29646ca8af31028d47e7e86e0dec740e6d
blob + e5e4ff824521e982ee015ee38c6ab14d746cbc0a
--- src/main/java/com/thinkberg/moxo/vfs/jets3t/Jets3tFileObject.java
+++ src/main/java/com/thinkberg/moxo/vfs/jets3t/Jets3tFileObject.java
@@ -16,91 +16,116 @@
 
 package com.thinkberg.moxo.vfs.jets3t;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 import org.apache.commons.vfs.FileName;
 import org.apache.commons.vfs.FileObject;
-import org.apache.commons.vfs.FileSystemException;
 import org.apache.commons.vfs.FileType;
 import org.apache.commons.vfs.provider.AbstractFileObject;
+import org.apache.commons.vfs.util.MonitorOutputStream;
+import org.jets3t.service.Constants;
 import org.jets3t.service.S3Service;
 import org.jets3t.service.S3ServiceException;
 import org.jets3t.service.model.S3Bucket;
 import org.jets3t.service.model.S3Object;
 import org.jets3t.service.utils.Mimetypes;
 
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.RandomAccessFile;
+import java.io.*;
 import java.nio.channels.Channels;
 import java.nio.channels.FileChannel;
 import java.nio.channels.ReadableByteChannel;
 import java.util.Date;
 
+
 /**
  * Implementation of the virtual S3 file system object using the Jets3t library.
  *
  * @author Matthias L. Jugel
  */
 public class Jets3tFileObject extends AbstractFileObject {
+  private static final Log LOG = LogFactory.getLog(Jets3tFileObject.class);
+
+  private static final String VFS_LAST_MODIFIED_TIME = "vfs-last-modified-time";
+
   private final S3Service service;
   private final S3Bucket bucket;
 
   private boolean attached = false;
-  private boolean changed = false;
+  private boolean dirty = false;
+  private boolean contentCached = false;
+
   private S3Object object;
   private File cacheFile;
 
   public Jets3tFileObject(FileName fileName,
                           Jets3tFileSystem fileSystem,
-                          S3Service service, S3Bucket bucket)
-          throws FileSystemException {
+                          S3Service service, S3Bucket bucket) {
     super(fileName, fileSystem);
     this.service = service;
     this.bucket = bucket;
   }
 
+  /**
+   * Attach S3 Object to VFS object.
+   * This method only downloads the meta-data without the actual content.
+   * If the object does not exist, it will be created locally.
+   *
+   * @throws Exception if the S3 access fails for some reason
+   */
   protected void doAttach() throws Exception {
     if (!attached) {
       try {
-        object = service.getObject(bucket, getS3Key());
-        System.err.println("Attached file to S3 Object: " + object);
-        InputStream is = object.getDataInputStream();
-        if (object.getContentLength() > 0) {
-          ReadableByteChannel rbc = Channels.newChannel(is);
-          FileChannel cacheFc = getCacheFileChannel();
-          cacheFc.transferFrom(rbc, 0, object.getContentLength());
-          cacheFc.close();
-          rbc.close();
-        } else {
-          is.close();
+        object = service.getObjectDetails(bucket, getS3Key());
+        if (object.getMetadata(VFS_LAST_MODIFIED_TIME) == null) {
+          // it is possible the bucket has no last-modified data, use the S3 data then
+          object.addMetadata(Constants.REST_METADATA_PREFIX + VFS_LAST_MODIFIED_TIME, "" + object.getLastModifiedDate().getTime());
         }
+        contentCached = false;
+        dirty = false;
+
+        LOG.debug(String.format("attaching (existing) '%s'", object.getKey()));
       } catch (S3ServiceException e) {
         object = new S3Object(bucket, getS3Key());
-        object.setLastModifiedDate(new Date());
-        System.err.println("Attached file to new S3 Object: " + object);
+        object.addMetadata(Constants.REST_METADATA_PREFIX + VFS_LAST_MODIFIED_TIME, "" + new Date().getTime());
+        contentCached = true;
+        dirty = true;
+
+        LOG.debug(String.format("attaching (new) '%s'", object.getKey()));
       }
+
       attached = true;
     }
   }
 
   protected void doDetach() throws Exception {
-    // TODO do not send immediately but put in some kind of upload queue
-    if (attached && changed) {
-      System.err.println("Detaching changed object: " + object);
+    if (attached) {
+      LOG.debug(String.format("detaching '%s' (dirty=%b, cached=%b)", object.getKey(), dirty, (cacheFile != null)));
+      object = null;
       if (cacheFile != null) {
-        FileChannel cacheFc = getCacheFileChannel();
-        object.setContentLength(cacheFc.size());
-        object.setDataInputStream(getInputStream());
+        cacheFile.delete();
+        cacheFile = null;
+        contentCached = false;
       }
-      System.err.println(object);
-      service.putObject(bucket, object);
+      dirty = false;
       attached = false;
     }
   }
 
   protected void doDelete() throws Exception {
+    // do not delete the root folder
+    if ("".equals(object.getKey())) {
+      LOG.warn(String.format("ignored attempt to delete root folder '%s' ", bucket.getName()));
+      return;
+    }
+    LOG.debug(String.format("deleting '%s'", object.getKey()));
     service.deleteObject(bucket, object.getKey());
+    if (cacheFile != null) {
+      cacheFile.delete();
+      cacheFile = null;
+      contentCached = false;
+    }
+    dirty = false;
+    attached = false;
   }
 
   protected void doRename(FileObject newfile) throws Exception {
@@ -110,27 +135,63 @@ public class Jets3tFileObject extends AbstractFileObje
   protected void doCreateFolder() throws Exception {
     if (!Mimetypes.MIMETYPE_JETS3T_DIRECTORY.equals(object.getContentType())) {
       object.setContentType(Mimetypes.MIMETYPE_JETS3T_DIRECTORY);
+
+      LOG.debug(String.format("creating folder '%s'", object.getKey()));
       service.putObject(bucket, object);
-      changed = false;
     }
   }
 
   protected long doGetLastModifiedTime() throws Exception {
-    return object.getLastModifiedDate().getTime();
+    String timeStamp = (String) object.getMetadata(VFS_LAST_MODIFIED_TIME);
+    if (null != timeStamp) {
+      return Long.parseLong(timeStamp);
+    }
+    return 0;
   }
 
   protected void doSetLastModifiedTime(final long modtime) throws Exception {
-    changed = true;
-    object.setLastModifiedDate(new Date(modtime));
+    object.addMetadata(Constants.REST_METADATA_PREFIX + VFS_LAST_MODIFIED_TIME, modtime);
+    dirty = true;
   }
 
   protected InputStream doGetInputStream() throws Exception {
-    return Channels.newInputStream(getCacheFileChannel());
+    if (!contentCached) {
+      object = service.getObject(bucket, getS3Key());
+      LOG.debug(String.format("caching content of '%s'", object.getKey()));
+
+      InputStream objectInputStream = object.getDataInputStream();
+      if (object.getContentLength() > 0) {
+        ReadableByteChannel rbc = Channels.newChannel(objectInputStream);
+        FileChannel cacheFc = getCacheFile().getChannel();
+        cacheFc.transferFrom(rbc, 0, object.getContentLength());
+        cacheFc.close();
+        rbc.close();
+      } else {
+        objectInputStream.close();
+      }
+      contentCached = true;
+    }
+
+    return Channels.newInputStream(getCacheFile().getChannel());
   }
 
   protected OutputStream doGetOutputStream(boolean bAppend) throws Exception {
-    changed = true;
-    return Channels.newOutputStream(getCacheFileChannel());
+    dirty = true;
+    return new MonitorOutputStream(Channels.newOutputStream(getCacheFile().getChannel())) {
+      protected void onClose() throws IOException {
+        try {
+          LOG.debug(String.format("sending '%s' to storage (dirty=%b, cached=%b)", object.getKey(), dirty, cacheFile));
+          if (cacheFile != null) {
+            FileChannel cacheFc = getCacheFile().getChannel();
+            object.setContentLength(cacheFc.size());
+            object.setDataInputStream(Channels.newInputStream(cacheFc));
+          }
+          service.putObject(bucket, object);
+        } catch (S3ServiceException e) {
+          LOG.error(String.format("can't send object '%s' to storage", object.getKey()), e);
+        }
+      }
+    };
   }
 
   protected FileType doGetType() throws Exception {
@@ -161,6 +222,7 @@ public class Jets3tFileObject extends AbstractFileObje
         childrenNames[i] = children[i].getKey().replaceAll("[^/]*//*", "");
       }
     }
+
     return childrenNames;
   }
 
@@ -184,10 +246,10 @@ public class Jets3tFileObject extends AbstractFileObje
     }
   }
 
-  private FileChannel getCacheFileChannel() throws IOException {
+  private RandomAccessFile getCacheFile() throws IOException, S3ServiceException {
     if (cacheFile == null) {
       cacheFile = File.createTempFile("moxo.", ".s3");
     }
-    return new RandomAccessFile(cacheFile, "rw").getChannel();
+    return new RandomAccessFile(cacheFile, "rw");
   }
 }
blob - e78aada5f0071545a49f52fb1f86aa003f038a87
blob + 5c2181514077735de9eaee6f6f59f9db52ebc405
--- src/main/java/com/thinkberg/moxo/vfs/jets3t/Jets3tFileSystem.java
+++ src/main/java/com/thinkberg/moxo/vfs/jets3t/Jets3tFileSystem.java
@@ -18,6 +18,8 @@ package com.thinkberg.moxo.vfs.jets3t;
 
 import com.thinkberg.moxo.vfs.S3FileName;
 import com.thinkberg.moxo.vfs.S3FileProvider;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 import org.apache.commons.vfs.FileName;
 import org.apache.commons.vfs.FileObject;
 import org.apache.commons.vfs.FileSystemException;
@@ -35,6 +37,8 @@ import java.util.Collection;
  * @author Matthias L. Jugel
  */
 public class Jets3tFileSystem extends AbstractFileSystem {
+  private static final Log LOG = LogFactory.getLog(Jets3tFileSystem.class);
+
   private S3Service service;
   private S3Bucket bucket;
 
@@ -43,11 +47,13 @@ public class Jets3tFileSystem extends AbstractFileSyst
     String bucketId = fileName.getRootFile();
     try {
       service = Jets3tConnector.getInstance().getService();
-      bucket = new S3Bucket(bucketId);
       if (!service.isBucketAccessible(bucketId)) {
+        LOG.info("creating new S3 bucket (" + bucketId + ") for file system");
         bucket = service.createBucket(bucketId);
+      } else {
+        LOG.info("using existing S3 bucket: " + bucketId);
+        bucket = new S3Bucket(bucketId);
       }
-      System.err.println("Created new S3 FileSystem: " + bucketId);
     } catch (S3ServiceException e) {
       throw new FileSystemException(e);
     }
blob - ae39e881510681e8eb8b08d6c28510f4eec11b1d
blob + 3067e4fa327fbd14e218e7adccae96656857cc6b
--- src/main/resources/jetty.xml
+++ src/main/resources/jetty.xml
@@ -12,163 +12,178 @@
 
 <Configure id="Server" class="org.mortbay.jetty.Server">
 
-  <!-- =========================================================== -->
-  <!-- Server Thread Pool                                          -->
-  <!-- =========================================================== -->
-  <Set name="ThreadPool">
-    <!-- Default bounded blocking threadpool
-    -->
-    <New class="org.mortbay.thread.BoundedThreadPool">
-      <Set name="minThreads">10</Set>
-      <Set name="lowThreads">50</Set>
-      <Set name="maxThreads">250</Set>
-    </New>
+    <!-- =========================================================== -->
+    <!-- Server Thread Pool                                          -->
+    <!-- =========================================================== -->
+    <Set name="ThreadPool">
+        <!-- Default bounded blocking threadpool
+        -->
+        <New class="org.mortbay.thread.BoundedThreadPool">
+            <Set name="minThreads">10</Set>
+            <Set name="lowThreads">50</Set>
+            <Set name="maxThreads">250</Set>
+        </New>
 
-    <!-- Optional Java 5 bounded threadpool with job queue
-    <New class="org.mortbay.thread.concurrent.ThreadPool">
-      <Arg type="int">0</Arg>
-      <Set name="corePoolSize">10</Set>
-      <Set name="maximumPoolSize">250</Set>
-    </New>
+        <!-- Optional Java 5 bounded threadpool with job queue
+        <New class="org.mortbay.thread.concurrent.ThreadPool">
+          <Arg type="int">0</Arg>
+          <Set name="corePoolSize">10</Set>
+          <Set name="maximumPoolSize">250</Set>
+        </New>
+        -->
+    </Set>
+
+
+    <!-- =========================================================== -->
+    <!-- Set connectors                                              -->
+    <!-- =========================================================== -->
+    <!-- One of each type!                                           -->
+    <!-- =========================================================== -->
+
+    <!-- Use this connector for many frequently idle connections
+         and for threadless continuations.
     -->
-  </Set>
+    <Call name="addConnector">
+        <Arg>
+            <New class="org.mortbay.jetty.nio.SelectChannelConnector">
+                <Set name="port">
+                    <SystemProperty name="jetty.port" default="8080"/>
+                </Set>
+                <Set name="maxIdleTime">30000</Set>
+                <Set name="Acceptors">2</Set>
+                <Set name="confidentialPort">8443</Set>
+            </New>
+        </Arg>
+    </Call>
 
+    <Call name="addConnector">
+        <Arg>
+            <New class="org.mortbay.jetty.security.SslSocketConnector">
+                <Set name="Port">8443</Set>
+                <Set name="maxIdleTime">30000</Set>
+                <Set name="keystore"><SystemProperty name="user.home" default="."/>/.keystore
+                </Set>
+                <Set name="password">OBF:1fvu20731x191vul1vup1x1d20731ftg</Set>
+                <Set name="keyPassword">OBF:1fvu20731x191vul1vup1x1d20731ftg</Set>
+                <Set name="truststore"><SystemProperty name="user.home" default="."/>/.keystore
+                </Set>
+                <Set name="trustPassword">OBF:1fvu20731x191vul1vup1x1d20731ftg</Set>
+            </New>
+        </Arg>
+    </Call>
 
-  <!-- =========================================================== -->
-  <!-- Set connectors                                              -->
-  <!-- =========================================================== -->
-  <!-- One of each type!                                           -->
-  <!-- =========================================================== -->
+    <!-- Use this connector if NIO is not available.
+    <Call name="addConnector">
+      <Arg>
+          <New class="org.mortbay.jetty.bio.SocketConnector">
+            <Set name="port">8081</Set>
+            <Set name="maxIdleTime">50000</Set>
+            <Set name="lowResourceMaxIdleTime">1500</Set>
+          </New>
+      </Arg>
+    </Call>
+    -->
 
-  <!-- Use this connector for many frequently idle connections
-       and for threadless continuations.
-  -->
-  <Call name="addConnector">
-    <Arg>
-      <New class="org.mortbay.jetty.nio.SelectChannelConnector">
-        <Set name="port">
-          <SystemProperty name="jetty.port" default="8080"/>
-        </Set>
-        <Set name="maxIdleTime">30000</Set>
-        <Set name="Acceptors">2</Set>
-        <Set name="confidentialPort">8443</Set>
+    <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+    <!-- To add a HTTPS SSL listener                                     -->
+    <!-- see jetty-ssl.xml to add an ssl connector. use                  -->
+    <!-- java -jar start.jar etc/jetty.xml etc/jetty-ssl.xml             -->
+    <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+    <!-- =========================================================== -->
+    <!-- Set up global session ID manager                            -->
+    <!-- =========================================================== -->
+    <!--
+    <Set name="sessionIdManager">
+      <New class="org.mortbay.jetty.servlet.HashSessionIdManager">
+        <Set name="workerName">node1</Set>
       </New>
-    </Arg>
-  </Call>
+    </Set>
+    -->
 
+    <!-- =========================================================== -->
+    <!-- Set handler Collection Structure                            -->
+    <!-- =========================================================== -->
+    <Set name="handler">
+        <New id="Handlers" class="org.mortbay.jetty.handler.HandlerCollection">
+            <Set name="handlers">
+                <Array type="org.mortbay.jetty.Handler">
+                    <Item>
+                        <New id="Contexts" class="org.mortbay.jetty.handler.ContextHandlerCollection"/>
+                    </Item>
+                    <Item>
+                        <New id="DefaultHandler" class="org.mortbay.jetty.handler.DefaultHandler"/>
+                    </Item>
+                    <Item>
+                        <New id="RequestLog" class="org.mortbay.jetty.handler.RequestLogHandler"/>
+                    </Item>
+                </Array>
+            </Set>
+        </New>
+    </Set>
 
-  <!-- Use this connector if NIO is not available.
-  <Call name="addConnector">
-    <Arg>
-        <New class="org.mortbay.jetty.bio.SocketConnector">
-          <Set name="port">8081</Set>
-          <Set name="maxIdleTime">50000</Set>
-          <Set name="lowResourceMaxIdleTime">1500</Set>
+    <!-- Set the handler for our web dav servlet -->
+    <Set name="handler">
+        <New id="WebDAVServletContext" class="org.mortbay.jetty.servlet.Context">
+            <Set name="contextPath">/</Set>
+            <Set name="resourceBase">
+                <SystemProperty name="jetty.docroot" default="."/>
+            </Set>
+            <Call name="addServlet">
+                <Arg>com.thinkberg.moxo.servlet.MoxoWebDAVServlet</Arg>
+                <Arg>/*</Arg>
+            </Call>
         </New>
-    </Arg>
-  </Call>
-  -->
+    </Set>
 
-  <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
-  <!-- To add a HTTPS SSL listener                                     -->
-  <!-- see jetty-ssl.xml to add an ssl connector. use                  -->
-  <!-- java -jar start.jar etc/jetty.xml etc/jetty-ssl.xml             -->
-  <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
 
-  <!-- =========================================================== -->
-  <!-- Set up global session ID manager                            -->
-  <!-- =========================================================== -->
-  <!--
-  <Set name="sessionIdManager">
-    <New class="org.mortbay.jetty.servlet.HashSessionIdManager">
-      <Set name="workerName">node1</Set>
-    </New>
-  </Set>
-  -->
-
-  <!-- =========================================================== -->
-  <!-- Set handler Collection Structure                            -->
-  <!-- =========================================================== -->
-  <Set name="handler">
-    <New id="Handlers" class="org.mortbay.jetty.handler.HandlerCollection">
-      <Set name="handlers">
-        <Array type="org.mortbay.jetty.Handler">
-          <Item>
-            <New id="Contexts" class="org.mortbay.jetty.handler.ContextHandlerCollection"/>
-          </Item>
-          <Item>
-            <New id="DefaultHandler" class="org.mortbay.jetty.handler.DefaultHandler"/>
-          </Item>
-          <Item>
-            <New id="RequestLog" class="org.mortbay.jetty.handler.RequestLogHandler"/>
-          </Item>
+    <!-- =========================================================== -->
+    <!-- Configure Authentication Realms                             -->
+    <!-- Realms may be configured for the entire server here, or     -->
+    <!-- they can be configured for a specific web app in a context  -->
+    <!-- configuration (see $(jetty.home)/contexts/test.xml for an   -->
+    <!-- example).                                                   -->
+    <!-- =========================================================== -->
+    <Set name="UserRealms">
+        <Array type="org.mortbay.jetty.security.UserRealm">
+            <!--
+                  <Item>
+                    <New class="org.mortbay.jetty.security.HashUserRealm">
+                      <Set name="name">Test Realm</Set>
+                      <Set name="config"><SystemProperty name="jetty.home" default="."/>/etc/realm.properties</Set>
+                    </New>
+                  </Item>
+            -->
         </Array>
-      </Set>
-    </New>
-  </Set>
-
-  <!-- Set the handler for our web dav servlet -->
-  <Set name="handler">
-    <New id="WebDAVServletContext" class="org.mortbay.jetty.servlet.Context">
-      <Set name="contextPath">/</Set>
-      <Set name="resourceBase">
-        <SystemProperty name="jetty.docroot" default="."/>
-      </Set>
-      <Call name="addServlet">
-        <Arg>com.thinkberg.moxo.servlet.MoxoS3WebdavServlet</Arg>
-        <Arg>/*</Arg>
-      </Call>
-    </New>
-  </Set>
-
-
-  <!-- =========================================================== -->
-  <!-- Configure Authentication Realms                             -->
-  <!-- Realms may be configured for the entire server here, or     -->
-  <!-- they can be configured for a specific web app in a context  -->
-  <!-- configuration (see $(jetty.home)/contexts/test.xml for an   -->
-  <!-- example).                                                   -->
-  <!-- =========================================================== -->
-  <Set name="UserRealms">
-    <Array type="org.mortbay.jetty.security.UserRealm">
-      <!--
-           <Item>
-             <New class="org.mortbay.jetty.security.HashUserRealm">
-               <Set name="name">Test Realm</Set>
-               <Set name="config"><SystemProperty name="jetty.home" default="."/>/etc/realm.properties</Set>
-             </New>
-           </Item>
-     -->
-    </Array>
-  </Set>
-
-  <!-- =========================================================== -->
-  <!-- Configure Request Log                                       -->
-  <!-- Request logs  may be configured for the entire server here, -->
-  <!-- or they can be configured for a specific web app in a       -->
-  <!-- contexts configuration (see $(jetty.home)/contexts/test.xml -->
-  <!-- for an example).                                            -->
-  <!-- =========================================================== -->
-  <Ref id="RequestLog">
-    <Set name="requestLog">
-      <New id="RequestLogImpl" class="org.mortbay.jetty.NCSARequestLog">
-        <Arg>
-          <SystemProperty name="jetty.logs" default="./logs"/>
-          /yyyy_mm_dd.request.log
-        </Arg>
-        <Set name="retainDays">90</Set>
-        <Set name="append">true</Set>
-        <Set name="extended">false</Set>
-        <Set name="LogTimeZone">GMT</Set>
-      </New>
     </Set>
-  </Ref>
 
-  <!-- =========================================================== -->
-  <!-- extra options                                               -->
-  <!-- =========================================================== -->
-  <Set name="stopAtShutdown">true</Set>
-  <!-- ensure/prevent Server: header being sent to browsers        -->
-  <Set name="sendServerVersion">true</Set>
+    <!-- =========================================================== -->
+    <!-- Configure Request Log                                       -->
+    <!-- Request logs  may be configured for the entire server here, -->
+    <!-- or they can be configured for a specific web app in a       -->
+    <!-- contexts configuration (see $(jetty.home)/contexts/test.xml -->
+    <!-- for an example).                                            -->
+    <!-- =========================================================== -->
+    <Ref id="RequestLog">
+        <Set name="requestLog">
+            <New id="RequestLogImpl" class="org.mortbay.jetty.NCSARequestLog">
+                <Arg>
+                    <SystemProperty name="jetty.logs" default="./logs"/>
+                    /yyyy_mm_dd.request.log
+                </Arg>
+                <Set name="retainDays">90</Set>
+                <Set name="append">true</Set>
+                <Set name="extended">false</Set>
+                <Set name="LogTimeZone">GMT</Set>
+            </New>
+        </Set>
+    </Ref>
 
+    <!-- =========================================================== -->
+    <!-- extra options                                               -->
+    <!-- =========================================================== -->
+    <Set name="stopAtShutdown">true</Set>
+    <!-- ensure/prevent Server: header being sent to browsers        -->
+    <Set name="sendServerVersion">true</Set>
+
 </Configure>
blob - cf3d423ee160e82b17ab56a44e55faddac0f384d
blob + 2431b8fe9d36132a3fa0f91baef89bc9df06ccc6
--- src/main/resources/moxo.template.properties
+++ src/main/resources/moxo.template.properties
@@ -35,4 +35,5 @@ acl=PRIVATE
 password=<some encryption password here>
 
 # the bucket that contains the file system
-bucket=<the file system bucket>
\ No newline at end of file
+s3.url=s3://TESTBUCKET/
+#s3.url=ram:/
\ No newline at end of file
blob - 253190f1ea5939f131d17688ef3a6e947791f8eb
blob + 5b43b0aee4de7daf1be61d57c1bd4be286fef0fd
--- src/main/resources/simplelog.properties
+++ src/main/resources/simplelog.properties
@@ -14,4 +14,5 @@
 # limitations under the License.
 #
 
-org.apache.commons.logging.simplelog.defaultlog=info
\ No newline at end of file
+org.apache.commons.logging.simplelog.defaultlog=error
+org.apache.commons.logging.simplelog.log.com.thinkberg.moxo=debug
\ No newline at end of file
blob - 75d3440d7341f1159ea710440ef5a4d9ab1c7e4a
blob + 9ac36182f7b6d695e49901c76968ce5efc0a39bf
--- src/test/java/com/thinkberg/moxo/MoxoTest.java
+++ src/test/java/com/thinkberg/moxo/MoxoTest.java
@@ -38,8 +38,8 @@ public class MoxoTest extends TestCase {
     String propertiesFileName = System.getProperty("moxo.properties", "moxo.properties");
     Jets3tProperties properties = Jets3tProperties.getInstance(propertiesFileName);
 
-    String bucketId = properties.getStringProperty("bucket", null);
-    if (null != bucketId) {
+    String vfsUrl = properties.getStringProperty("vfs.url", null);
+    if (null != vfsUrl && vfsUrl.startsWith("s3:")) {
       s.addTestSuite(S3FileNameTest.class);
       s.addTestSuite(S3FileProviderTest.class);
     }
blob - 111a1dbd66bf028541af96c6cc04d9feef45c600 (mode 644)
blob + /dev/null
--- src/test/java/com/thinkberg/moxo/S3TestCase.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright 2007 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;
-
-import junit.framework.TestCase;
-import org.jets3t.service.Jets3tProperties;
-
-/**
- * @author Matthias L. Jugel
- */
-public class S3TestCase extends TestCase {
-  protected final static String BUCKETID;
-  protected static final String ROOT;
-
-  static {
-    String propertiesFileName = System.getProperty("moxo.properties", "moxo.properties");
-    Jets3tProperties properties = Jets3tProperties.getInstance(propertiesFileName);
-
-    BUCKETID = properties.getStringProperty("bucket", null);
-    ROOT = "s3://" + BUCKETID;
-
-  }
-}
blob - 0a640610fa6e6e3de857f625d04981a4eaa3f1d3
blob + c348a78e06fa4feda0222993ce6a971a7d0994fa
--- src/test/java/com/thinkberg/moxo/dav/DavTestCase.java
+++ src/test/java/com/thinkberg/moxo/dav/DavTestCase.java
@@ -39,7 +39,6 @@ public class DavTestCase extends TestCase {
     aDirectory.createFolder();
   }
 
-  @SuppressWarnings({"SameParameterValue"})
   protected void testPropertyValue(FileObject object, String propertyName, String propertyValue) throws FileSystemException {
     Element root = serializeDavResource(object, propertyName);
     assertEquals(propertyValue, selectExistingPropertyValue(root, propertyName));
@@ -58,7 +57,6 @@ public class DavTestCase extends TestCase {
     return root.selectSingleNode(PROP_MISSING + propertyName);
   }
 
-  @SuppressWarnings({"SameParameterValue"})
   String selectMissingPropertyName(Element root, String propertyName) {
     return selectMissingProperty(root, propertyName).getName();
   }
blob - 91cc77a793bd5a99291d9625ec74622b90b9102d
blob + cad1af21c05c18a65efeb78b403be26e71fca272
--- src/test/java/com/thinkberg/moxo/vfs/S3FileNameTest.java
+++ src/test/java/com/thinkberg/moxo/vfs/S3FileNameTest.java
@@ -16,20 +16,20 @@
 
 package com.thinkberg.moxo.vfs;
 
-import junit.framework.TestCase;
-import org.apache.commons.vfs.FileSystemException;
 import org.apache.commons.vfs.FileName;
-import org.jets3t.service.Jets3tProperties;
-import com.thinkberg.moxo.S3TestCase;
+import org.apache.commons.vfs.FileSystemException;
 
+import java.net.URI;
+
 /**
  * @author Matthias L. Jugel
  */
 public class S3FileNameTest extends S3TestCase {
   public void testGetBucketFromUri() throws FileSystemException {
-    String uri = ROOT + "/junk.txt";
-    FileName fileName = S3FileNameParser.getInstance().parseUri(null, null, uri);
-    assertEquals(BUCKETID, ((S3FileName)fileName).getRootFile());
+    String uriString = ROOT + "/junk.txt";
+    String bucketId = URI.create(uriString).getHost();
+    FileName fileName = S3FileNameParser.getInstance().parseUri(null, null, uriString);
+    assertEquals(bucketId, ((S3FileName) fileName).getRootFile());
   }
 
   public void testGetRootFolderFromUri() throws FileSystemException {
blob - 9f9b360ec733337b7a07da2071e596cd97c791fb
blob + 38485aeefa013dff633c31b5e00104bd1efcdd46
--- src/test/java/com/thinkberg/moxo/vfs/S3FileProviderTest.java
+++ src/test/java/com/thinkberg/moxo/vfs/S3FileProviderTest.java
@@ -16,43 +16,97 @@
 
 package com.thinkberg.moxo.vfs;
 
-import com.thinkberg.moxo.S3TestCase;
-import org.apache.commons.vfs.FileObject;
-import org.apache.commons.vfs.FileSelectInfo;
-import org.apache.commons.vfs.FileSelector;
-import org.apache.commons.vfs.FileSystem;
-import org.apache.commons.vfs.FileSystemException;
-import org.apache.commons.vfs.FileType;
-import org.apache.commons.vfs.VFS;
+import org.apache.commons.vfs.*;
 
 import java.io.IOException;
+import java.io.OutputStream;
+import java.net.URI;
+import java.util.Arrays;
 
 /**
  * @author Matthias L. Jugel
  */
 public class S3FileProviderTest extends S3TestCase {
+  private static final FileSelector ALL_FILE_SELECTOR = new FileSelector() {
+
+    public boolean includeFile(FileSelectInfo fileInfo) throws Exception {
+      return true;
+    }
+
+    public boolean traverseDescendents(FileSelectInfo fileInfo) throws Exception {
+      return true;
+    }
+  };
+  private static final String FOLDER = "/directory";
+  private static final String FILE = FOLDER + "/newfile.txt";
+
+  static {
+    try {
+      FileObject rootFs = VFS.getManager().resolveFile(ROOT);
+      rootFs.delete(ALL_FILE_SELECTOR);
+    } catch (FileSystemException e) {
+      // just delete, ignore the rest
+    }
+  }
+
   public void testDoCreateFileSystem() throws FileSystemException {
     FileObject object = VFS.getManager().resolveFile(ROOT);
-    assertEquals(BUCKETID, ((S3FileName) object.getName()).getRootFile());
+    String bucketId = URI.create(ROOT).getHost();
+    assertEquals(bucketId, ((S3FileName) object.getName()).getRootFile());
   }
 
+
+  public void testFileSystemIsEmpty() throws FileSystemException {
+    FileObject object = VFS.getManager().resolveFile(ROOT);
+    assertEquals(1, object.findFiles(ALL_FILE_SELECTOR).length);
+  }
+
   public void testRootDirectoryIsFolder() throws FileSystemException {
     FileObject object = VFS.getManager().resolveFile(ROOT);
     assertEquals(FileType.FOLDER, object.getType());
   }
 
-  public void testGetDirectory() throws FileSystemException {
-    FileObject object = VFS.getManager().resolveFile(ROOT + "/Sites");
+  public void testCreateFolder() throws FileSystemException {
+    FileObject object = VFS.getManager().resolveFile(ROOT + FOLDER);
+    assertFalse(object.exists());
+    object.createFolder();
+    assertTrue(object.exists());
     assertEquals(FileType.FOLDER, object.getType());
   }
 
-  public void testGetDirectoryListing() throws FileSystemException {
-    FileObject object = VFS.getManager().resolveFile(ROOT + "/Sites/Sites/images");
+  public void testGetFolder() throws FileSystemException {
+    FileObject object = VFS.getManager().resolveFile(ROOT + FOLDER);
+    assertTrue(object.exists());
+    assertEquals(FileType.FOLDER, object.getType());
+  }
+
+  public void testCreateFile() throws IOException {
+    FileObject object = VFS.getManager().resolveFile(ROOT + FILE);
+    assertFalse(object.exists());
+    OutputStream os = object.getContent().getOutputStream();
+    os.write(0xfc);
+    os.close();
+    assertTrue(object.exists());
+    assertEquals(FileType.FILE, object.getType());
+  }
+
+  public void testCreateEmptyFile() throws IOException {
+    FileObject object = VFS.getManager().resolveFile(ROOT + FILE + ".empty");
+    assertFalse(object.exists());
+    object.createFile();
+    assertTrue(object.exists());
+    assertEquals(FileType.FILE, object.getType());
+  }
+
+  public void testFileHasLastModifiedTimestamp() throws FileSystemException {
+    FileObject object = VFS.getManager().resolveFile(ROOT + FILE);
+    object.getContent().getLastModifiedTime();
+  }
+
+  public void testGetFolderListing() throws FileSystemException {
+    FileObject object = VFS.getManager().resolveFile(ROOT + FOLDER);
     FileObject[] files = object.findFiles(new DepthFileSelector(1));
-    for (FileObject file : files) {
-      System.out.println("Found file: " + file.getName().getPath());
-    }
-    assertEquals(4, files.length);
+    assertEquals(3, files.length);
   }
 
   public void testMissingFile() throws FileSystemException {
@@ -60,38 +114,67 @@ public class S3FileProviderTest extends S3TestCase {
     assertFalse(object.exists());
   }
 
+  public void testGetLastModifiedTimeFolder() throws FileSystemException {
+    FileObject object = VFS.getManager().resolveFile(ROOT + FOLDER);
+    object.getContent().getLastModifiedTime();
+  }
+
+  public void testGetLastModifiedTimeFile() throws FileSystemException {
+    FileObject object = VFS.getManager().resolveFile(ROOT + FILE);
+    object.getContent().getLastModifiedTime();
+  }
+
   public void testDeleteFile() throws FileSystemException {
-    FileObject object = VFS.getManager().resolveFile(ROOT + "/newfile.txt");
+    FileObject object = VFS.getManager().resolveFile(ROOT + FILE);
     object.delete();
+    assertFalse(object.exists());
   }
 
   public void testDeleteFolder() throws FileSystemException {
-    FileObject object = VFS.getManager().resolveFile(ROOT + "/newfolder");
-    object.delete(new FileSelector() {
+    FileObject object = VFS.getManager().resolveFile(ROOT + FOLDER);
+    object.delete(ALL_FILE_SELECTOR);
+    assertFalse(object.exists());
+  }
 
-      public boolean includeFile(FileSelectInfo fileInfo) throws Exception {
-        return true;
-      }
+  public void testCopyFolder() throws FileSystemException {
+    FileObject origFolder = VFS.getManager().resolveFile(ROOT + FOLDER);
+    origFolder.createFolder();
 
-      public boolean traverseDescendents(FileSelectInfo fileInfo) throws Exception {
-        return true;
-      }
-    });
-  }
+    origFolder.resolveFile("file.0").createFile();
+    origFolder.resolveFile("file.1").createFile();
+    origFolder.resolveFile("file.2").createFile();
 
-  public void testCreateFolder() throws FileSystemException {
-    FileObject object = VFS.getManager().resolveFile(ROOT + "/newfolder");
-    object.createFolder();
+    FileObject[] origFiles = origFolder.findFiles(new DepthFileSelector(1));
+    assertEquals(4, origFiles.length);
+
+    FileObject destFolder = VFS.getManager().resolveFile(ROOT + FOLDER + "_dest");
+    assertFalse(destFolder.exists());
+    destFolder.copyFrom(origFolder, new DepthFileSelector(1));
+    assertTrue(destFolder.exists());
+
+    FileObject[] destFiles = destFolder.findFiles(new DepthFileSelector(1));
+    System.err.println(Arrays.asList(destFiles));
+    assertEquals(4, destFiles.length);
+
+    for (int i = 0; i < origFiles.length; i++) {
+      assertEquals(origFiles[i].getName().getRelativeName(origFolder.getName()),
+                   destFiles[i].getName().getRelativeName(destFolder.getName()));
+    }
+
+    origFolder.delete(ALL_FILE_SELECTOR);
+    destFolder.delete(ALL_FILE_SELECTOR);
+
+    assertFalse(origFolder.exists());
+    assertFalse(destFolder.exists());
   }
 
-  public void testCreateFile() throws IOException {
-    FileObject object = VFS.getManager().resolveFile(ROOT + "/newfile.txt");
-    object.getContent().getOutputStream().write(0xfc);
-    object.close();
+  public void testMoveFolder() throws FileSystemException {
+
   }
 
   public void testCloseFileSystem() throws FileSystemException {
     FileSystem fs = VFS.getManager().resolveFile(ROOT).getFileSystem();
     VFS.getManager().closeFileSystem(fs);
   }
+
 }
blob - /dev/null
blob + 790d3007daff25069746237120ced9b14239a05a (mode 644)
--- /dev/null
+++ src/test/java/com/thinkberg/moxo/vfs/S3TestCase.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2007 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.vfs;
+
+import junit.framework.TestCase;
+import org.jets3t.service.Jets3tProperties;
+
+/**
+ * @author Matthias L. Jugel
+ */
+public class S3TestCase extends TestCase {
+  protected static final String ROOT;
+
+  static {
+    String propertiesFileName = System.getProperty("moxo.properties", "moxo.properties");
+    Jets3tProperties properties = Jets3tProperties.getInstance(propertiesFileName);
+    ROOT = properties.getStringProperty("vfs.url", "ram:/");
+  }
+}