Commits

Anonymous committed 5477781

Released 1.0.0, poking around with the Filesystem code to see if uploading / locking can be supported

  • Participants
  • Parent commits 4e63af6

Comments (0)

Files changed (16)

Source/org/xnat/xnatfs/webdav/Bundle.java

      * Exception e ) { logger.error ( "Failed to get child element list: " + e
      * ); e.printStackTrace (); }
      */
-    if ( childName.equals ( "Upload" ) ) {
+    if ( false && childName.equals ( "Upload" ) ) {
       UploadDirectory upload = new UploadDirectory ( xnatfs, mAbsolutePath, childName, mURL + "files/" );
       setChildAuthorization ( upload );
       return upload;
   @Override
   public List<? extends Resource> getChildren () {
     List<Resource> children = (List<Resource>) super.getChildren ();
-    children.add ( child ( "Upload" ) );
+    // children.add ( child ( "Upload" ) );
     return children;
   }
 

Source/org/xnat/xnatfs/webdav/DummyFile.java

    * @see com.bradmcevoy.http.GetableResource#getContentLength()
    */
   public Long getContentLength () {
-    return new Long ( 1024 * 1024 );
+    return new Long ( sContents.length () );
   }
 
   /*
    */
   public String getContentType ( String accepts ) {
     logger.debug ( "getContentType: " + accepts );
-    return null;
+    return accepts;
   }
 
   /*
    * com.bradmcevoy.http.Range, java.util.Map, java.lang.String)
    */
   public void sendContent ( OutputStream out, Range range, Map<String, String> params, String contentType ) throws IOException, NotAuthorizedException {
-    byte[] c = new byte[1];
-    c[0] = 0;
-    if ( range != null ) {
-      // Just want a portion of the file
-      logger.debug ( "sendContent start: " + range.getStart () + " finish: " + range.getFinish () + " response: " + sContents.substring ( (int) range.getStart (), (int) range.getFinish () ) );
-      for ( long start = range.getStart (); start < range.getFinish (); start++ ) {
-        out.write ( c );
-      }
-      // out.write ( sContents.substring ( (int) range.getStart (), (int)
-      // range.getFinish () ).getBytes () );
-    } else {
-      logger.debug ( "setContent request for entire file" );
-      // Write it all out
-      for ( long i = 0; i < 1024 * 1024; i++ ) {
-        out.write ( c );
-      }
-      logger.debug ( "Finished sending entire file" );
-      // out.write ( sContents.getBytes () );
-    }
+    logger.debug ( "Finished sending entire file" );
+    out.write ( sContents.getBytes () );
   }
 
   /*

Source/org/xnat/xnatfs/webdav/FilesystemServlet.java

+/**
+ * 
+ */
+package org.xnat.xnatfs.webdav;
+
+import java.io.IOException;
+
+import javax.servlet.Servlet;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.bradmcevoy.http.AbstractMiltonEndPoint;
+import com.bradmcevoy.http.ApplicationConfig;
+import com.bradmcevoy.http.MiltonServlet;
+import com.bradmcevoy.http.Request;
+import com.bradmcevoy.http.ResourceFactory;
+import com.bradmcevoy.http.Response;
+import com.bradmcevoy.http.ServletRequest;
+import com.bradmcevoy.http.ServletResponse;
+import com.ettrema.http.fs.FileSystemResourceFactory;
+import com.ettrema.http.fs.MemoryLockManager;
+
+/**
+ * @author blezek
+ * 
+ */
+public class FilesystemServlet extends AbstractMiltonEndPoint implements Servlet {
+
+  private Logger log = LoggerFactory.getLogger ( MiltonServlet.class );
+
+  private ServletConfig config;
+
+  private static final ThreadLocal<HttpServletRequest> originalRequest = new ThreadLocal<HttpServletRequest> ();
+  private static final ThreadLocal<HttpServletResponse> originalResponse = new ThreadLocal<HttpServletResponse> ();
+
+  public void init ( ServletConfig config ) throws ServletException {
+
+    try {
+      this.config = config;
+      String notFoundPath = config.getInitParameter ( "not.found.path" );
+      String resourceFactoryFactoryClassName = config.getInitParameter ( "resource.factory.factory.class" );
+      if ( resourceFactoryFactoryClassName != null && resourceFactoryFactoryClassName.length () > 0 ) {
+        initFromFactoryFactory ( resourceFactoryFactoryClassName, notFoundPath );
+      } else {
+        String resourceFactoryClassName = config.getInitParameter ( "resource.factory.class" );
+        String responseHandlerClassName = config.getInitParameter ( "response.handler.class" );
+        init ( resourceFactoryClassName, notFoundPath, responseHandlerClassName );
+      }
+      httpManager.init ( new ApplicationConfig ( config ), httpManager );
+    } catch ( ServletException ex ) {
+      log.error ( "Exception starting milton servlet", ex );
+      throw ex;
+    } catch ( Throwable ex ) {
+      log.error ( "Exception starting milton servlet", ex );
+      throw new RuntimeException ( ex );
+    }
+    ResourceFactory f = httpManager.getResourceFactory ();
+    if ( f instanceof FileSystemResourceFactory ) {
+      FileSystemResourceFactory r = (FileSystemResourceFactory) f;
+      r.setLockManager ( new MemoryLockManager () );
+    }
+  }
+
+  public static HttpServletRequest request () {
+    return originalRequest.get ();
+  }
+
+  public static HttpServletResponse response () {
+    return originalResponse.get ();
+  }
+
+  public static void forward ( String url ) {
+    try {
+      request ().getRequestDispatcher ( url ).forward ( originalRequest.get (), originalResponse.get () );
+    } catch ( IOException ex ) {
+      throw new RuntimeException ( ex );
+    } catch ( ServletException ex ) {
+      throw new RuntimeException ( ex );
+    }
+  }
+
+  public void service ( javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse ) throws ServletException, IOException {
+    HttpServletRequest req = (HttpServletRequest) servletRequest;
+    HttpServletResponse resp = (HttpServletResponse) servletResponse;
+    try {
+      originalRequest.set ( req );
+      originalResponse.set ( resp );
+      Request request = new ServletRequest ( req );
+      Response response = new ServletResponse ( resp );
+      httpManager.process ( request, response );
+    } finally {
+      originalRequest.remove ();
+      originalResponse.remove ();
+      servletResponse.getOutputStream ().flush ();
+      servletResponse.flushBuffer ();
+    }
+  }
+
+  public String getServletInfo () {
+    return "xnatfs";
+  }
+
+  public ServletConfig getServletConfig () {
+    return config;
+  }
+}

Source/org/xnat/xnatfs/webdav/LockManager.java

+package org.xnat.xnatfs.webdav;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.bradmcevoy.http.LockInfo;
+import com.bradmcevoy.http.LockResult;
+import com.bradmcevoy.http.LockTimeout;
+import com.bradmcevoy.http.LockToken;
+
+public class LockManager {
+
+  /**
+   * maps current locks by the file associated with the resource
+   */
+
+  Map<String, LockToken> mLocks;
+
+  public LockManager () {
+    mLocks = new HashMap<String, LockToken> ();
+  }
+
+  public synchronized LockResult lock ( LockTimeout timeout, LockInfo lockInfo, VirtualResource resource ) {
+    LockToken currentLock = mLocks.get ( resource );
+    if ( currentLock != null ) {
+      return LockResult.failed ( LockResult.FailureReason.ALREADY_LOCKED );
+    }
+    LockToken newToken = new LockToken ( resource.getAbsoluteName (), lockInfo, timeout );
+    mLocks.put ( newToken.tokenId, newToken );
+    return LockResult.success ( newToken );
+  }
+
+  public synchronized LockResult refresh ( String tokenId, VirtualResource resource ) {
+    LockToken currentLock = mLocks.get ( resource );
+    currentLock.setFrom ( new Date () );
+    return LockResult.success ( currentLock );
+  }
+
+  public synchronized void unlock ( String tokenId, VirtualResource resource ) {
+    LockToken token = mLocks.get ( resource );
+    if ( token != null ) {
+      mLocks.remove ( token.tokenId );
+    }
+  }
+
+}

Source/org/xnat/xnatfs/webdav/StartJettyServer.java

     server.setHandler ( context );
     ServletHolder holder = new ServletHolder ( new xnatfsServlet () );
     holder.setInitParameter ( "resource.factory.class", "org.xnat.xnatfs.webdav.XNATFS" );
+    // ServletHolder holder = new ServletHolder ( new FilesystemServlet () );
+    // holder.setInitParameter ( "resource.factory.class",
+    // "com.ettrema.http.fs.FileSystemResourceFactory" );
     holder.setInitParameter ( "response.handler.class", "com.bradmcevoy.http.MsOfficeResponseHandler" );
     context.addServlet ( holder, "/*" );
 

Source/org/xnat/xnatfs/webdav/UploadDirectory.java

 
 import org.apache.log4j.Logger;
 
+import com.bradmcevoy.http.LockInfo;
+import com.bradmcevoy.http.LockTimeout;
+import com.bradmcevoy.http.LockToken;
+import com.bradmcevoy.http.LockingCollectionResource;
 import com.bradmcevoy.http.PutableResource;
 import com.bradmcevoy.http.Resource;
 
  * @author blezek
  * 
  */
-public class UploadDirectory extends VirtualDirectory implements PutableResource {
+public class UploadDirectory extends VirtualDirectory implements PutableResource, LockingCollectionResource {
   static final Logger logger = Logger.getLogger ( UploadDirectory.class );
 
   /**
    * @see org.xnat.xnatfs.webdav.VirtualDirectory#child(java.lang.String)
    */
   @Override
-  public Resource child ( String arg0 ) {
+  public Resource child ( String name ) {
     // UploadDirectory never has any children
+    // return new DummyFile ( xnatfs, mAbsolutePath, name );
     return null;
   }
 
     return null;
   }
 
+  public LockToken createAndLock ( String arg0, LockTimeout arg1, LockInfo arg2 ) {
+    // TODO Auto-generated method stub
+    return null;
+  }
+
 }

Source/org/xnat/xnatfs/webdav/VirtualResource.java

 import org.apache.log4j.Logger;
 
 import com.bradmcevoy.http.Auth;
+import com.bradmcevoy.http.LockInfo;
+import com.bradmcevoy.http.LockResult;
+import com.bradmcevoy.http.LockTimeout;
+import com.bradmcevoy.http.LockToken;
+import com.bradmcevoy.http.LockableResource;
 import com.bradmcevoy.http.PropFindableResource;
 import com.bradmcevoy.http.Request;
 import com.bradmcevoy.http.Resource;
 import com.bradmcevoy.http.Request.Method;
 
-public abstract class VirtualResource implements Resource, PropFindableResource {
+public abstract class VirtualResource implements Resource, PropFindableResource, LockableResource {
   final static Logger logger = Logger.getLogger ( VirtualResource.class );
 
   public static final Date mDate = new Date ();
   /*
    * (non-Javadoc)
    * 
+   * @see com.bradmcevoy.http.LockableResource#getCurrentLock()
+   */
+  public LockToken getCurrentLock () {
+    // TODO Auto-generated method stub
+    return null;
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see
+   * com.bradmcevoy.http.LockableResource#lock(com.bradmcevoy.http.LockTimeout,
+   * com.bradmcevoy.http.LockInfo)
+   */
+  public LockResult lock ( LockTimeout timeout, LockInfo lockInfo ) {
+    return xnatfs.getLockManager ().lock ( timeout, lockInfo, this );
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see com.bradmcevoy.http.LockableResource#refreshLock(java.lang.String)
+   */
+  public LockResult refreshLock ( String token ) {
+    return xnatfs.getLockManager ().refresh ( token, this );
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see com.bradmcevoy.http.LockableResource#unlock(java.lang.String)
+   */
+  public void unlock ( String tokenId ) {
+    xnatfs.getLockManager ().unlock ( tokenId, this );
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
    * @see
    * com.bradmcevoy.http.Resource#checkRedirect(com.bradmcevoy.http.Request)
    */
     return mAbsolutePath;
   }
 
+  public String getAbsoluteName () {
+    if ( mAbsolutePath != null ) {
+      return mAbsolutePath + "/" + mName;
+    } else {
+      return mName;
+    }
+  }
+
   /*
    * (non-Javadoc)
    * 

Source/org/xnat/xnatfs/webdav/XNATFS.java

   public static File sTemporaryDirectory;
 
   SecurityManager securityManager;
+  LockManager mLockManager;
 
   public XNATFS () {
     // Configure log4j
     Logger.getLogger ( "org.apache.commons" ).setLevel ( Level.WARN );
     Logger.getLogger ( "httpclient.wire" ).setLevel ( Level.WARN );
     Logger.getLogger ( "org.xnat.xnatfs.webdav." ).setLevel ( Level.DEBUG );
+    Logger.getLogger ( "com.bradmcevoy.http" ).setLevel ( Level.DEBUG );
     configureCache ();
     configureConnection ();
     sTemporaryDirectory = new File ( System.getProperty ( "java.io.tmpdir" ) );
     sTemporaryDirectory.mkdirs ();
 
     securityManager = this;
+    mLockManager = new LockManager ();
   }
 
   /*
     if ( path.startsWith ( "/xnatfs" ) ) {
       path = path.replaceFirst ( "/xnatfs", "" );
     }
-    logger.debug ( "Couldn't find node '" + path + "', trying to create the file in the parent" );
+    // logger.debug ( "Couldn't find node '" + path +
+    // "', trying to create the file in the parent" );
     return createChild ( path );
   }
 
    * @return The new Node. Should never return null.
    */
   synchronized Resource createChild ( String path ) {
-    logger.debug ( "createChild: " + path );
+    // logger.debug ( "createChild: " + path );
     if ( path == null || path.equals ( "" ) ) {
       logger.error ( "createChild: Null path" );
       return null;
     }
     // End the recursion
     if ( path.equals ( "/" ) ) {
-      logger.error ( "createChild: Should have found root, creating new" );
+      // logger.error ( "createChild: Should have found root, creating new" );
+      // logger.warn ( "Reminder: returning UploadDirectory" );
+      // UploadDirectory d = new UploadDirectory ( this, null, "/", "/" );
+      // return d;
+
       Root root = new Root ( this, null, "/", "/" );
       return root;
     }
       return null;
     }
     try {
-      logger.debug ( "createChild: having parent " + VirtualResource.dirname ( path ) + " create child " + VirtualResource.tail ( path ) );
+      // logger.debug ( "createChild: having parent " + VirtualResource.dirname
+      // ( path ) + " create child " + VirtualResource.tail ( path ) );
       return parent.child ( VirtualResource.tail ( path ) );
     } catch ( Exception e ) {
       logger.error ( "Falied to create child: " + VirtualResource.tail ( path ), e );
    * 
    * @see com.bradmcevoy.http.ResourceFactory#getSupportedLevels()
    */
+  // Note that supported levels of 1,2 is required for mac os write operations,
+  // but might interfere with MS office editing operations
   public String getSupportedLevels () {
     return "1,2";
   }
   }
 
   public Object authenticate ( String user, String password ) {
-    logger.debug ( "authenticate: " + user + "/" + password );
+    logger.debug ( "authenticate: " + user + "/****** (password removed)" );
     return user;
   }
 
     logger.debug ( "getRealm" );
     return new String ( "xnatfs" );
   }
+
+  public LockManager getLockManager () {
+    return mLockManager;
+  }
 }

Source/org/xnat/xnatfs/webdav/xnatfs.java

   public static File sTemporaryDirectory;
 
   SecurityManager securityManager;
+  LockManager mLockManager;
 
   public XNATFS () {
     // Configure log4j
     Logger.getLogger ( "org.apache.commons" ).setLevel ( Level.WARN );
     Logger.getLogger ( "httpclient.wire" ).setLevel ( Level.WARN );
     Logger.getLogger ( "org.xnat.xnatfs.webdav." ).setLevel ( Level.DEBUG );
+    Logger.getLogger ( "com.bradmcevoy.http" ).setLevel ( Level.DEBUG );
     configureCache ();
     configureConnection ();
     sTemporaryDirectory = new File ( System.getProperty ( "java.io.tmpdir" ) );
     sTemporaryDirectory.mkdirs ();
 
     securityManager = this;
+    mLockManager = new LockManager ();
   }
 
   /*
     if ( path.startsWith ( "/xnatfs" ) ) {
       path = path.replaceFirst ( "/xnatfs", "" );
     }
-    logger.debug ( "Couldn't find node '" + path + "', trying to create the file in the parent" );
+    // logger.debug ( "Couldn't find node '" + path +
+    // "', trying to create the file in the parent" );
     return createChild ( path );
   }
 
    * @return The new Node. Should never return null.
    */
   synchronized Resource createChild ( String path ) {
-    logger.debug ( "createChild: " + path );
+    // logger.debug ( "createChild: " + path );
     if ( path == null || path.equals ( "" ) ) {
       logger.error ( "createChild: Null path" );
       return null;
     }
     // End the recursion
     if ( path.equals ( "/" ) ) {
-      logger.error ( "createChild: Should have found root, creating new" );
+      // logger.error ( "createChild: Should have found root, creating new" );
+      // logger.warn ( "Reminder: returning UploadDirectory" );
+      // UploadDirectory d = new UploadDirectory ( this, null, "/", "/" );
+      // return d;
+
       Root root = new Root ( this, null, "/", "/" );
       return root;
     }
       return null;
     }
     try {
-      logger.debug ( "createChild: having parent " + VirtualResource.dirname ( path ) + " create child " + VirtualResource.tail ( path ) );
+      // logger.debug ( "createChild: having parent " + VirtualResource.dirname
+      // ( path ) + " create child " + VirtualResource.tail ( path ) );
       return parent.child ( VirtualResource.tail ( path ) );
     } catch ( Exception e ) {
       logger.error ( "Falied to create child: " + VirtualResource.tail ( path ), e );
    * 
    * @see com.bradmcevoy.http.ResourceFactory#getSupportedLevels()
    */
+  // Note that supported levels of 1,2 is required for mac os write operations,
+  // but might interfere with MS office editing operations
   public String getSupportedLevels () {
     return "1,2";
   }
   }
 
   public Object authenticate ( String user, String password ) {
-    logger.debug ( "authenticate: " + user + "/" + password );
+    logger.debug ( "authenticate: " + user + "/****** (password removed)" );
     return user;
   }
 
     logger.debug ( "getRealm" );
     return new String ( "xnatfs" );
   }
+
+  public LockManager getLockManager () {
+    return mLockManager;
+  }
 }

lib/milton-api-1.4.2-sources.jar

Binary file added.

lib/milton-console-1.4.2-sources.jar

Binary file added.

lib/milton-filesystem-1.4.2-sources.jar

Binary file added.

lib/milton-ftp-1.4.2-sources.jar

Binary file added.

lib/milton-servlet-1.4.2-sources.jar

Binary file added.

xnatfsUsage.pdf

Binary file added.
+{\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf540
+{\fonttbl\f0\froman\fcharset0 Times-Roman;\f1\fmodern\fcharset0 Courier;}
+{\colortbl;\red255\green255\blue255;\red0\green23\blue229;}
+\margl1440\margr1440\vieww14200\viewh16240\viewkind0
+\deftab720
+\pard\pardeftab720\sa320\ql\qnatural
+
+\f0\b\fs48 \cf0 Introduction\
+\pard\pardeftab720\sa240\ql\qnatural
+
+\b0\fs24 \cf0 xnatfsStandalone.jar implements a local WebDAV server for XNAT. The server requests authentication credentials that are passed onto the XNAT server.\
+\pard\pardeftab720\sa320\ql\qnatural
+
+\b\fs48 \cf0 Usage\
+\pard\pardeftab720\ql\qnatural
+
+\f1\b0\fs24 \cf0 java -jar xnatfsStandalone-1.0.0.jar --server central.xnat.org --scheme https --port 80 8081\
+\pard\pardeftab720\sa240\ql\qnatural
+
+\f0 \cf0 Arguments\
+\pard\pardeftab720\ql\qnatural
+
+\f1 \cf0 \'a0--server \'a0 -- XNAT instance to connect to\
+\'a0--scheme \'a0 -- prefix on URL (http or https supported)\
+\'a0--port \'a0 \'a0 -- port on the XNAT instance (80 is default)\
+\'a0<port> \'a0 \'a0 -- Local port to run the WebDAV server (8081 is the default)\
+\
+\pard\pardeftab720\sa320\ql\qnatural
+
+\f0\b\fs48 \cf0 Connecting to xnatfs:\
+\pard\pardeftab720\sa240\ql\qnatural
+
+\b0\fs24 \cf0 When connecting to xnatfs, you will be prompted for your username/password.  Please use your XNAT credentials.  NB: these will be transmitted to xnatfs more or less in plain text, and transmitted to XNAT in the same manner.  This is the same behavior as if you were connecting directly to the XNAT instance.  The https protocol (see the 
+\f1 --scheme
+\f0  argument above) is the recommended connection scheme.
+\b \
+Mac Instructions:\
+
+\b0 On the Mac, you can connect through Finder->Go->Connect to Server... with a URL of {\field{\*\fldinst{HYPERLINK "http://127.0.0.1:8081/"}}{\fldrslt \cf2 \ul \ulc2 http://127.0.0.1:8081}}. Incidentally, you can use a web browser to do the same thing. \
+
+\b Windows Instructions:\
+
+\b0 Not sure what you need to do under Windows. From what I've read, WebDAV support is fairly broken under WinXP, and only slightly better using Vista. Haven't tried Win7 yet.  Some help may be found here {\field{\*\fldinst{HYPERLINK "http://www.wikispaces.com/WebDAV+Windows+XP"}}{\fldrslt http://www.wikispaces.com/WebDAV+Windows+XP}} and {\field{\*\fldinst{HYPERLINK "http://www.powers1.net/techup/node/2"}}{\fldrslt http://www.powers1.net/techup/node/2}} using NetDrive.\
+
+\b Linux/Unix Instructions:\
+
+\b0 Under Linux, the various file managers support WebDAV fairly well. In all cases, the server will prompt you for your username/password. Use your XNAT credentials, which will be forwarded on to the XNAT instance.\
+\pard\pardeftab720\sa320\ql\qnatural
+
+\b\fs48 \cf0 Known Issues\
+\pard\pardeftab720\sa240\ql\qnatural
+
+\b0\fs24 \cf0 On the Mac, if a file is opened or copied from an expanded folder in list view, xnatfs hangs for some unknown reason, and the file can not be copied due to insufficient privileges.\
+At the moment, the local data is served using http, but we could implement https, I think. Though I haven't tried it, I think more than one user can connect at a time, all with different XNAT credentials.\
+\pard\pardeftab720\sa320\ql\qnatural
+
+\b\fs48 \cf0 Contact Information\
+\pard\pardeftab720\sa240\ql\qnatural
+
+\b0\fs24 \cf0 Please contact Daniel Blezek ({\field{\*\fldinst{HYPERLINK "mailto:daniel.blezek@gmail.com"}}{\fldrslt daniel.blezek@gmail.com}}) with any comments, bugs or suggestions.\
+The project page for xnatfs is hosted by Google Code {\field{\*\fldinst{HYPERLINK "http://code.google.com/p/xnatfs/"}}{\fldrslt http://code.google.com/p/xnatfs/}}.}