commit - /dev/null
commit + 1bbed3956b1d921c893322d595688ad003e70940
blob - /dev/null
blob + d645695673349e3947e8e5ae42332d0ac3164cd7 (mode 644)
--- /dev/null
+++ LICENSE
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
blob - /dev/null
blob + 7028744f430a3e18a81939ea3f859c9b5e991eb1 (mode 644)
--- /dev/null
+++ NOTICE
+=========================================================================
+== NOTICE file corresponding to section 4(d) of the Apache License, ==
+== Version 2.0. ==
+=========================================================================
+
+Moxo S3 DAV Proxy Server
+Copyright 2007 Matthias L. Jugel.
+
+This product includes software developed at
+
+ Many Ideas (for WebDAV) taken from s3DAV (c) Pierre Carion
+ http://www.carion.org/s3dav/
+
+ The Apache Software Foundation (http://www.apache.org/).
+ MetaStuff, Ltd. (DOM4J, http://dom4j.org).
+ Mort Bay Consulting (Jetty, http://jetty.mortbay.org).
+ James Murty (jetst3, http://http://jets3t.s3.amazonaws.com)
+
+
+
blob - /dev/null
blob + 709526aee6deb2cbb9880883eca5aaf026160cc9 (mode 644)
--- /dev/null
+++ README
+Moxo S3 DAV Proxy Server
+Copyright 2007 Matthias L. Jugel. See LICENSE for details.
+http://thinkberg.com/
+
+This is a first go on two issues:
+
+a) a WebDAV server based on apache-commons-vfs
+b) an Amazon S3 provider backend for apache-commons-vfs
+
+The WebDAV server is semi-complete in a sense that it works well for most tests in the
+listmus test suite except for property handling which is virtually non-existing.
+
+The VFS backend is started and provides read-only access. I have managed to browse through
+my test backup on S3 using MacOSX own Finder DAV client.
+
+INFO:
+
+Subversion: http://thinkberg.com/svn/moxo/trunk
+
+The maven build process is not yet fully completed, so to get this running a little bit
+of tweaking is necessary, unless you use IntelliJ IDEA and maven idea:idea to create
+an IDEA project file.
+
+To run either the MoxoJettyRunner or the MoxoTest you need to include the src/main/resources
+directory in your classpath. Also copy the file moxo.template.properties and edit it to
+include your Amazon S3 access information as well as the bucket to use. Right now the bucket
+must already exist and contain files uploaded using the Uploader or Synchronize from Jets3t.
+
+Define the system property "moxo.properties" to point to your moxo.properties file:
+
+java -cp ... -Dmoxo.properties=moxo.properties com.thinkberg.moxo.MoxoJettyRunner
+
+TODO:
+
+- Create an executable JAR with all required libraries. The Main is already prepared to do
+ that but I have not yet fully understood how to get maven to package the jars right next
+ to the compiled classes.
+- Add write access to the VFS backend.
+- Much more ...
blob - /dev/null
blob + d645695673349e3947e8e5ae42332d0ac3164cd7 (mode 644)
--- /dev/null
+++ licenses/APACHE-LICENSE.txt
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
blob - /dev/null
blob + a90650a4bc01ea8bc40312e8c70e7759c5806857 (mode 644)
--- /dev/null
+++ licenses/DOM4J-BSD.txt
+Copyright 2001-2005 (C) MetaStuff, Ltd. All Rights Reserved.
+
+Redistribution and use of this software and associated documentation
+("Software"), with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Redistributions of source code must retain copyright
+ statements and notices. Redistributions must also contain a
+ copy of this document.
+
+2. Redistributions in binary form must reproduce the
+ above copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+3. The name "DOM4J" must not be used to endorse or promote
+ products derived from this Software without prior written
+ permission of MetaStuff, Ltd. For written permission,
+ please contact dom4j-info@metastuff.com.
+
+4. Products derived from this Software may not be called "DOM4J"
+ nor may "DOM4J" appear in their names without prior written
+ permission of MetaStuff, Ltd. DOM4J is a registered
+ trademark of MetaStuff, Ltd.
+
+5. Due credit should be given to the DOM4J Project -
+ http://www.dom4j.org
+
+THIS SOFTWARE IS PROVIDED BY METASTUFF, LTD. AND CONTRIBUTORS
+``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+METASTUFF, LTD. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+OF THE POSSIBILITY OF SUCH DAMAGE.
blob - /dev/null
blob + 366af8a561343bd5d1b259929b35c50840a49bae (mode 644)
--- /dev/null
+++ pom.xml
+<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>
+</project>
blob - /dev/null
blob + 81f9dc3075c006734e4d4a76f76f86d0023776df (mode 644)
--- /dev/null
+++ src/main/java/com/thinkberg/moxo/Main.java
+/*
+ * 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 java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.security.Policy;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.jar.JarEntry;
+import java.util.jar.JarInputStream;
+
+/**
+ * The launcher is responsible for extracting referenced libraries from the jar and
+ * setting up the classpath.
+ *
+ * @author Matthias L. Jugel
+ */
+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();
+ if (null == parentClassLoader) {
+ parentClassLoader = Main.class.getClassLoader();
+ }
+ if (null == parentClassLoader) {
+ parentClassLoader = ClassLoader.getSystemClassLoader();
+ }
+ URLClassLoader classLoader = new URLClassLoader(initClassPath(), parentClassLoader);
+ Thread.currentThread().setContextClassLoader(classLoader);
+
+ System.setSecurityManager(null);
+
+ try {
+ Policy.getPolicy().refresh();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ Class mainClass = classLoader.loadClass("com.thinkberg.moxo.MoxoJettyRunner");
+ final Method main = mainClass.getDeclaredMethod("main", new Class[]{String[].class});
+ main.invoke(null, new Object[]{args});
+ }
+
+ /**
+ * Read the jar manifest and add class-path entries to the classpath.
+ *
+ * @return the classpath
+ */
+ private static URL[] initClassPath() {
+ List<URL> urlArray = new ArrayList<URL>();
+ InputStream manifestIn = null;
+ InputStream jarIn = null;
+ try {
+ manifestIn = location.openStream();
+ JarInputStream launcherJarIs = new JarInputStream(manifestIn);
+ StringBuffer classPath = new StringBuffer(location.getFile());
+ List<URL> classpathList = new ArrayList<URL>(urlArray);
+ JarEntry jarEntry;
+ while (null != (jarEntry = launcherJarIs.getNextJarEntry())) {
+ if (!jarEntry.isDirectory() && jarEntry.getName().endsWith(".jar")) {
+ try {
+ URL classPathEntry = getResourceUrl(jarEntry.getName());
+ if (!classpathList.contains(classPathEntry)) {
+ urlArray.add(classPathEntry);
+ classPath.append(File.pathSeparatorChar);
+ classPath.append(classPathEntry.getFile());
+ }
+ } catch (IOException e) {
+ System.err.println("ignored '" + jarEntry.getName() + "'");
+ }
+ }
+ }
+
+ System.setProperty("java.class.path", classPath.toString());
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ manifestIn.close();
+ } catch (Throwable ignore) {
+ // ignore errors
+ }
+ try {
+ jarIn.close();
+ } catch (Throwable ignore) {
+ // ignore errors
+ }
+ }
+ return urlArray.toArray(new URL[0]);
+ }
+
+ /**
+ * Get URL.
+ *
+ * @param resource resource name/path
+ * @return the url pointing to the resource
+ * @throws IOException if the resource cannot be accessed
+ */
+ private static URL getResourceUrl(String resource) throws IOException {
+ File directoryBase = new File(location.getFile()).getParentFile();
+ File file = new File(resource);
+ if (file.isAbsolute() && file.exists()) {
+ return file.toURL();
+ }
+ file = new File(directoryBase, resource);
+ if (file.exists()) {
+ return file.toURL();
+ }
+
+ URL resourceURL = Main.class.getResource("/" + resource);
+ if (null != resourceURL) {
+ return extract(resourceURL);
+ }
+
+ throw new MalformedURLException(resource);
+ }
+
+ /**
+ * Extract file from launcher jar to be able to access is via classpath.
+ *
+ * @param resource the jar resource to be extracted
+ * @return a url pointing to the new file
+ * @throws IOException if the extraction was not possible
+ */
+ private static URL extract(URL resource) throws IOException {
+ File f = File.createTempFile("launcher_", ".jar");
+ f.deleteOnExit();
+ if (f.getParentFile() != null) {
+ f.getParentFile().mkdirs();
+ }
+ InputStream is = new BufferedInputStream(resource.openStream());
+ FileOutputStream os = new FileOutputStream(f);
+ byte[] arr = new byte[8192];
+ for (int i = 0; i >= 0; i = is.read(arr)) {
+ os.write(arr, 0, i);
+ }
+ is.close();
+ os.close();
+ return f.toURL();
+ }
+}
\ No newline at end of file
blob - /dev/null
blob + e1d96cf755ad2bafbd99a230101d870629bf7423 (mode 644)
--- /dev/null
+++ src/main/java/com/thinkberg/moxo/MoxoJettyRunner.java
+/*
+ * 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.mortbay.jetty.Server;
+import org.mortbay.xml.XmlConfiguration;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+/**
+ * The Java Webserver starter uses jetty.
+ *
+ * @author Matthias L. Jugel
+ */
+public class MoxoJettyRunner {
+ private static final String CONF_JETTY_XML = "jetty.xml";
+
+ public static void main(String[] args) {
+ System.out.println("Moxo S3 DAV Proxy (c) 2007 Matthias L. Jugel");
+
+ // set encoding of the JVM and make sure Jetty decodes URIs correctly
+ System.setProperty("file.encoding", "UTF-8");
+ System.setProperty("org.mortbay.util.URI.charset", "UTF-8");
+
+ try {
+ Server server = new Server();
+ XmlConfiguration xmlConfiguration = new XmlConfiguration(getResource(CONF_JETTY_XML));
+ xmlConfiguration.configure(server);
+ server.start();
+ server.join();
+ } catch (Exception e) {
+ System.err.println("Can't start server: " + e.getMessage());
+ System.exit(1);
+ }
+ }
+
+ @SuppressWarnings({"SameParameterValue"})
+ private static URL getResource(String resource) {
+ URL url = MoxoJettyRunner.class.getResource("/" + resource);
+ if (null == url) {
+ try {
+ url = new File(resource).toURL();
+ System.err.println("Loading configuration from file: " + url.toExternalForm());
+ } catch (MalformedURLException e) {
+ // ignore ...
+ }
+ }
+ return url;
+ }
+}
blob - /dev/null
blob + 0e0225a25f66bc7037f99212080eb30f5bf7e944 (mode 644)
--- /dev/null
+++ src/main/java/com/thinkberg/moxo/ResourceManager.java
+/*
+ * 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 filesystem: " + e.getMessage());
+ e.printStackTrace();
+ }
+ }
+
+ public static FileObject getFileObject(String path) throws FileSystemException {
+ return root.resolveFile(path);
+ }
+}
blob - /dev/null
blob + 34da23d2a591b3326dc3d78d18ae9a335ad6b4ca (mode 644)
--- /dev/null
+++ src/main/java/com/thinkberg/moxo/dav/CopyHandler.java
+/*
+ * 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.dav;
+
+import com.thinkberg.moxo.vfs.DepthFileSelector;
+import org.apache.commons.vfs.FileObject;
+import org.apache.commons.vfs.FileSystemException;
+
+/**
+ * @author Matthias L. Jugel
+ * @version $Id$
+ */
+public class CopyHandler extends CopyMoveBase {
+ protected void copyOrMove(FileObject object, FileObject target, int depth) throws FileSystemException {
+ target.copyFrom(object, new DepthFileSelector(depth));
+ }
+}
blob - /dev/null
blob + b6e185d27e3e58d31107a6a92117d79beee64682 (mode 644)
--- /dev/null
+++ src/main/java/com/thinkberg/moxo/dav/CopyMoveBase.java
+/*
+ * 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.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;
+import org.apache.commons.vfs.FileSystemException;
+import org.apache.commons.vfs.FileType;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * @author Matthias L. Jugel
+ * @version $Id$
+ */
+public abstract class CopyMoveBase extends WebdavHandler {
+
+ public void service(HttpServletRequest request, HttpServletResponse response) throws IOException {
+ boolean overwrite = getOverwrite(request);
+ FileObject object = ResourceManager.getFileObject(request.getPathInfo());
+ FileObject targetObject = getDestination(request);
+
+ try {
+ // check that we can write the target
+ LockManager.getInstance().checkCondition(targetObject, getIf(request));
+ // if we move, check that we can actually write on the source
+ if ("MOVE".equals(request.getMethod())) {
+ LockManager.getInstance().checkCondition(object, getIf(request));
+ }
+ } catch (LockException e) {
+ if (e.getLocks() != null) {
+ response.sendError(SC_LOCKED);
+ } else {
+ response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
+ }
+ return;
+ }
+
+
+ if (null == targetObject) {
+ response.sendError(HttpServletResponse.SC_BAD_REQUEST);
+ return;
+ }
+
+ if (object.equals(targetObject)) {
+ response.sendError(HttpServletResponse.SC_FORBIDDEN);
+ return;
+ }
+
+ if (targetObject.exists()) {
+ if (!overwrite) {
+ response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
+ return;
+ }
+ response.setStatus(HttpServletResponse.SC_NO_CONTENT);
+ } else {
+ FileObject targetParent = targetObject.getParent();
+ if (!targetParent.exists() ||
+ !FileType.FOLDER.equals(targetParent.getType())) {
+ response.sendError(HttpServletResponse.SC_CONFLICT);
+ }
+ response.setStatus(HttpServletResponse.SC_CREATED);
+ }
+
+ copyOrMove(object, targetObject, getDepth(request));
+ }
+
+ protected abstract void copyOrMove(FileObject object, FileObject target, int depth) throws FileSystemException;
+
+}
blob - /dev/null
blob + 3533537d1b03790bb012ae0befe0c5539f1330a5 (mode 644)
--- /dev/null
+++ src/main/java/com/thinkberg/moxo/dav/DeleteHandler.java
+/*
+ * 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.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;
+import org.apache.commons.vfs.FileSelectInfo;
+import org.apache.commons.vfs.FileSelector;
+import org.mortbay.jetty.Request;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * @author Matthias L. Jugel
+ * @version $Id$
+ */
+public class DeleteHandler extends WebdavHandler {
+
+ private final static FileSelector ALL_FILES_SELECTOR = new FileSelector() {
+ public boolean includeFile(FileSelectInfo fileSelectInfo) throws Exception {
+ return true;
+ }
+
+ public boolean traverseDescendents(FileSelectInfo fileSelectInfo) throws Exception {
+ return true;
+ }
+ };
+
+ public void service(HttpServletRequest request, HttpServletResponse response) throws IOException {
+ FileObject object = ResourceManager.getFileObject(request.getPathInfo());
+ if (request instanceof Request) {
+ String fragment = ((Request) request).getUri().getFragment();
+ if (fragment != null) {
+ response.sendError(HttpServletResponse.SC_FORBIDDEN);
+ return;
+ }
+ }
+
+ try {
+ LockManager.getInstance().checkCondition(object, getIf(request));
+ } catch (LockException e) {
+ if (e.getLocks() != null) {
+ response.sendError(SC_LOCKED);
+ } else {
+ response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
+ }
+ return;
+ }
+
+ if (object.exists()) {
+ if (object.delete(ALL_FILES_SELECTOR) > 0) {
+ response.setStatus(HttpServletResponse.SC_OK);
+ } else {
+ response.sendError(HttpServletResponse.SC_FORBIDDEN);
+ }
+ } else {
+ response.sendError(HttpServletResponse.SC_NOT_FOUND);
+ }
+ }
+}
blob - /dev/null
blob + bcfd04dc1bfcf5fd118934e8a0108b8e9f592b13 (mode 644)
--- /dev/null
+++ src/main/java/com/thinkberg/moxo/dav/GetHandler.java
+/*
+ * 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.dav;
+
+import com.thinkberg.moxo.ResourceManager;
+import org.apache.commons.vfs.FileContent;
+import org.apache.commons.vfs.FileObject;
+import org.apache.commons.vfs.FileSystemException;
+import org.apache.commons.vfs.FileType;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * @author Matthias L. Jugel
+ * @version $Id$
+ */
+public class GetHandler extends WebdavHandler {
+
+ public void service(HttpServletRequest request, HttpServletResponse response) throws IOException {
+ FileObject object = ResourceManager.getFileObject(request.getPathInfo());
+
+ if (object.exists()) {
+ if (FileType.FOLDER.equals(object.getType())) {
+ response.sendError(HttpServletResponse.SC_FORBIDDEN);
+ return;
+ }
+
+ setHeader(response, object.getContent());
+
+ InputStream is = object.getContent().getInputStream();
+ OutputStream os = response.getOutputStream();
+ Util.copyStream(is, os);
+ is.close();
+ } else {
+ response.sendError(HttpServletResponse.SC_NOT_FOUND);
+ }
+ }
+
+ void setHeader(HttpServletResponse response, FileContent content) throws FileSystemException {
+ response.setHeader("Last-Modified", Util.getDateString(content.getLastModifiedTime()));
+ response.setHeader("Content-Type", content.getContentInfo().getContentType());
+ }
+
+
+}
blob - /dev/null
blob + 51fd09bff59cddb5c4212b9d20ddb56a28d8c5d3 (mode 644)
--- /dev/null
+++ src/main/java/com/thinkberg/moxo/dav/HeadHandler.java
+/*
+ * 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.dav;
+
+import com.thinkberg.moxo.ResourceManager;
+import org.apache.commons.vfs.FileObject;
+import org.apache.commons.vfs.FileType;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * @author Matthias L. Jugel
+ * @version $Id$
+ */
+public class HeadHandler extends GetHandler {
+
+ public void service(HttpServletRequest request, HttpServletResponse response) throws IOException {
+ FileObject object = ResourceManager.getFileObject(request.getPathInfo());
+
+ if (object.exists()) {
+ if (FileType.FOLDER.equals(object.getType())) {
+ response.sendError(HttpServletResponse.SC_FORBIDDEN);
+ } else {
+ setHeader(response, object.getContent());
+ }
+ } else {
+ response.sendError(HttpServletResponse.SC_NOT_FOUND);
+ }
+ }
+}
blob - /dev/null
blob + 8e7e95dc2594e7ce20d4a8e2afd6b1d6729fb992 (mode 644)
--- /dev/null
+++ src/main/java/com/thinkberg/moxo/dav/LockHandler.java
+/*
+ * 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.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.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.io.SAXReader;
+import org.dom4j.io.XMLWriter;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.net.URL;
+import java.util.Iterator;
+
+/**
+ * Handle WebDAV LOCK requests.
+ *
+ * @author Matthias L. Jugel
+ * @version $Id$
+ */
+public class LockHandler extends WebdavHandler {
+ private static final String TAG_LOCKSCOPE = "lockscope";
+ private static final String TAG_LOCKTYPE = "locktype";
+ private static final String TAG_OWNER = "owner";
+ private static final String TAG_HREF = "href";
+ private static final String TAG_PROP = "prop";
+ private static final String TAG_LOCKDISCOVERY = "lockdiscovery";
+
+ private static final String HEADER_LOCK_TOKEN = "Lock-Token";
+
+ public void service(HttpServletRequest request, HttpServletResponse response) throws IOException {
+ FileObject object = ResourceManager.getFileObject(request.getPathInfo());
+
+ try {
+ Lock lock = LockManager.getInstance().checkCondition(object, getIf(request));
+ if (lock != null) {
+ sendLockAcquiredResponse(response, lock);
+ return;
+ }
+ } catch (LockException e) {
+ // handle locks below
+ }
+
+ try {
+ SAXReader saxReader = new SAXReader();
+ Document lockInfo = saxReader.read(request.getInputStream());
+ //log(lockInfo);
+
+ Element rootEl = lockInfo.getRootElement();
+ String lockScope = null, lockType = null;
+ Object owner = null;
+ Iterator elIt = rootEl.elementIterator();
+ while (elIt.hasNext()) {
+ Element el = (Element) elIt.next();
+ if (TAG_LOCKSCOPE.equals(el.getName())) {
+ lockScope = el.selectSingleNode("*").getName();
+ } else if (TAG_LOCKTYPE.equals(el.getName())) {
+ lockType = el.selectSingleNode("*").getName();
+ } else if (TAG_OWNER.equals(el.getName())) {
+ // TODO correctly handle owner
+ Node subEl = el.selectSingleNode("*");
+ if (subEl != null && TAG_HREF.equals(subEl.getName())) {
+ owner = new URL(el.selectSingleNode("*").getText());
+ } else {
+ owner = el.getText();
+ }
+ }
+ }
+
+ log("LOCK(" + lockType + ", " + lockScope + ", " + owner + ")");
+
+ Lock requestedLock = new Lock(object, lockType, lockScope, owner, getDepth(request), getTimeout(request));
+ try {
+ LockManager.getInstance().acquireLock(requestedLock);
+ sendLockAcquiredResponse(response, requestedLock);
+ } catch (LockConflictException e) {
+ response.sendError(SC_LOCKED);
+ } catch (IllegalArgumentException e) {
+ response.sendError(HttpServletResponse.SC_BAD_REQUEST);
+ }
+ } catch (DocumentException e) {
+ e.printStackTrace();
+ response.sendError(HttpServletResponse.SC_BAD_REQUEST);
+ }
+ }
+
+ private void sendLockAcquiredResponse(HttpServletResponse response, Lock lock) throws IOException {
+ response.setContentType("text/xml");
+ response.setCharacterEncoding("UTF-8");
+ response.setHeader(HEADER_LOCK_TOKEN, "<" + lock.getToken() + ">");
+
+ Document propDoc = DocumentHelper.createDocument();
+ Element propEl = propDoc.addElement(TAG_PROP, "DAV:");
+ Element lockdiscoveryEl = propEl.addElement(TAG_LOCKDISCOVERY);
+
+ lock.serializeToXml(lockdiscoveryEl);
+
+ XMLWriter xmlWriter = new XMLWriter(response.getWriter());
+ xmlWriter.write(propDoc);
+ log(propDoc);
+ }
+}
blob - /dev/null
blob + c7bd42b7da7d33d12157b1d0f6dac1a96b20dfbb (mode 644)
--- /dev/null
+++ src/main/java/com/thinkberg/moxo/dav/MkColHandler.java
+/*
+ * 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.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;
+import org.apache.commons.vfs.FileSystemException;
+import org.apache.commons.vfs.FileType;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * @author Matthias L. Jugel
+ * @version $Id$
+ */
+public class MkColHandler extends WebdavHandler {
+
+ public void service(HttpServletRequest request, HttpServletResponse response) throws IOException {
+ if (request.getReader().readLine() != null) {
+ response.sendError(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);
+ return;
+ }
+
+ FileObject object = ResourceManager.getFileObject(request.getPathInfo());
+
+ try {
+ LockManager.getInstance().checkCondition(object, getIf(request));
+ } catch (LockException e) {
+ if (e.getLocks() != null) {
+ response.sendError(SC_LOCKED);
+ } else {
+ response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
+ }
+ return;
+ }
+
+ if (object.exists()) {
+ response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
+ return;
+ }
+
+ if (!object.getParent().exists() || !FileType.FOLDER.equals(object.getParent().getType())) {
+ response.sendError(HttpServletResponse.SC_CONFLICT);
+ return;
+ }
+
+ try {
+ object.createFolder();
+ response.setStatus(HttpServletResponse.SC_CREATED);
+ } catch (FileSystemException e) {
+ response.sendError(HttpServletResponse.SC_FORBIDDEN);
+ }
+ }
+}
blob - /dev/null
blob + 5cb1a045ff6f0fc46e8991898548660850d8a10a (mode 644)
--- /dev/null
+++ src/main/java/com/thinkberg/moxo/dav/MoveHandler.java
+/*
+ * 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.dav;
+
+import com.thinkberg.moxo.vfs.DepthFileSelector;
+import org.apache.commons.vfs.FileObject;
+import org.apache.commons.vfs.FileSystemException;
+
+/**
+ * @author Matthias L. Jugel
+ * @version $Id$
+ */
+public class MoveHandler extends CopyMoveBase {
+ protected void copyOrMove(FileObject object, FileObject target, int depth) throws FileSystemException {
+ target.copyFrom(object, new DepthFileSelector(depth));
+ object.delete(new DepthFileSelector());
+ }
+}
blob - /dev/null
blob + fb5876098ccd381b67aa1aa2065a55e2ee93d7af (mode 644)
--- /dev/null
+++ src/main/java/com/thinkberg/moxo/dav/OptionsHandler.java
+/*
+ * 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.dav;
+
+import com.thinkberg.moxo.ResourceManager;
+import org.apache.commons.vfs.FileObject;
+import org.apache.commons.vfs.FileType;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * @author Matthias L. Jugel
+ * @version $Id$
+ */
+public class OptionsHandler extends WebdavHandler {
+
+ public void service(HttpServletRequest request, HttpServletResponse response) throws IOException {
+ response.setHeader("DAV", "1, 2");
+
+ String path = request.getPathInfo();
+ StringBuffer options = new StringBuffer();
+ FileObject object = ResourceManager.getFileObject(path);
+ if (object.exists()) {
+ options.append("OPTIONS, GET, HEAD, POST, DELETE, TRACE, COPY, MOVE, LOCK, UNLOCK, PROPFIND");
+ if (FileType.FOLDER.equals(object.getType())) {
+ options.append(", PUT");
+ }
+ } else {
+ options.append("OPTIONS, MKCOL, PUT, LOCK");
+ }
+ response.setHeader("Allow", options.toString());
+
+ // see: http://www-128.ibm.com/developerworks/rational/library/2089.html
+ response.setHeader("MS-Author-Via", "DAV");
+ }
+}
blob - /dev/null
blob + e0038189d0aaef0103b1f32ab0a53ea3a228e09e (mode 644)
--- /dev/null
+++ src/main/java/com/thinkberg/moxo/dav/PostHandler.java
+/*
+ * 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.dav;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * @author Matthias L. Jugel
+ * @version $Id$
+ */
+public class PostHandler extends WebdavHandler {
+
+ public void service(HttpServletRequest request, HttpServletResponse response) throws IOException {
+ response.sendError(HttpServletResponse.SC_BAD_REQUEST);
+ }
+}
blob - /dev/null
blob + 169c09204e05f26f192ab23f7ad567443352bb55 (mode 644)
--- /dev/null
+++ src/main/java/com/thinkberg/moxo/dav/PropFindHandler.java
+/*
+ * 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.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.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.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.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Matthias L. Jugel
+ * @version $Id$
+ */
+public class PropFindHandler extends WebdavHandler {
+ private static final String TAG_PROP = "prop";
+ private static final String TAG_ALLPROP = "allprop";
+ private static final String TAG_PROPNAMES = "propnames";
+ private static final String TAG_MULTISTATUS = "multistatus";
+ private static final String TAG_HREF = "href";
+ private static final String TAG_RESPONSE = "response";
+
+ public void service(HttpServletRequest request, HttpServletResponse response) throws IOException {
+ SAXReader saxReader = new SAXReader();
+ try {
+ Document propDoc = saxReader.read(request.getInputStream());
+ // log(propDoc);
+ Element propFindEl = propDoc.getRootElement();
+ Element propEl = (Element) propFindEl.elementIterator().next();
+ String propElName = propEl.getName();
+
+ List<String> requestedProperties = new ArrayList<String>();
+ boolean ignoreValues = false;
+ if (TAG_PROP.equals(propElName)) {
+ for (Object id : propEl.elements()) {
+ requestedProperties.add(((Element) id).getName());
+ }
+ } else if (TAG_ALLPROP.equals(propElName)) {
+ requestedProperties = DavResource.ALL_PROPERTIES;
+ } else if (TAG_PROPNAMES.equals(propElName)) {
+ requestedProperties = DavResource.ALL_PROPERTIES;
+ ignoreValues = true;
+ }
+
+ FileObject object = ResourceManager.getFileObject(request.getPathInfo());
+ if (object.exists()) {
+ // respond as XML encoded multi status
+ response.setContentType("text/xml");
+ response.setCharacterEncoding("UTF-8");
+ response.setStatus(SC_MULTI_STATUS);
+
+ Document multiStatusResponse =
+ getMultiStatusRespons(object,
+ requestedProperties,
+ getBaseUrl(request),
+ getDepth(request),
+ ignoreValues);
+ //log(multiStatusResponse);
+
+ // write the actual response
+ XMLWriter writer = new XMLWriter(response.getWriter(), OutputFormat.createCompactFormat());
+ writer.write(multiStatusResponse);
+ writer.flush();
+ writer.close();
+
+ } else {
+ log("!! " + object.getName().getPath() + " NOT FOUND");
+ response.sendError(HttpServletResponse.SC_NOT_FOUND);
+ }
+ } catch (DocumentException e) {
+ log("!! inavlid request: " + e.getMessage());
+ response.sendError(HttpServletResponse.SC_BAD_REQUEST);
+ }
+ }
+
+ private Document getMultiStatusRespons(FileObject object,
+ List<String> requestedProperties,
+ URL baseUrl,
+ int depth,
+ boolean ignoreValues) throws FileSystemException {
+ Document propDoc = DocumentHelper.createDocument();
+ propDoc.setXMLEncoding("UTF-8");
+
+ Element multiStatus = propDoc.addElement(TAG_MULTISTATUS, "DAV:");
+ FileObject[] children = object.findFiles(new DepthFileSelector(depth));
+ for (FileObject child : children) {
+ Element responseEl = multiStatus.addElement(TAG_RESPONSE);
+ try {
+ URL url = new URL(baseUrl, URLEncoder.encode(child.getName().getPath(), "UTF-8"));
+ log("!! " + url);
+ responseEl.addElement(TAG_HREF).addText(url.toExternalForm());
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ DavResource resource = DavResourceFactory.getInstance().getDavResource(child);
+ resource.setIgnoreValues(ignoreValues);
+ resource.serializeToXml(responseEl, requestedProperties);
+ }
+ return propDoc;
+ }
+}
blob - /dev/null
blob + 9848117271155f8ff4227471f202c991479dffba (mode 644)
--- /dev/null
+++ src/main/java/com/thinkberg/moxo/dav/PropPatchHandler.java
+/*
+ * 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.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.vfs.FileObject;
+import org.dom4j.Document;
+import org.dom4j.DocumentException;
+import org.dom4j.DocumentHelper;
+import org.dom4j.Element;
+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.IOException;
+import java.net.URL;
+import java.util.List;
+
+/**
+ * Handle PROPPATCH requests. This currently a dummy only and will return a
+ * forbidden status for any attempt to modify or remove a property.
+ *
+ * @author Matthias L. Jugel
+ */
+public class PropPatchHandler extends WebdavHandler {
+ public void service(HttpServletRequest request, HttpServletResponse response) throws IOException {
+ FileObject object = ResourceManager.getFileObject(request.getPathInfo());
+
+ try {
+ LockManager.getInstance().checkCondition(object, getIf(request));
+ } catch (LockException e) {
+ if (e.getLocks() != null) {
+ response.sendError(SC_LOCKED);
+ } else {
+ response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
+ }
+ return;
+ }
+
+ SAXReader saxReader = new SAXReader();
+ try {
+ Document propDoc = saxReader.read(request.getInputStream());
+// log(propDoc);
+
+ response.setContentType("text/xml");
+ response.setCharacterEncoding("UTF-8");
+ response.setStatus(SC_MULTI_STATUS);
+
+ if (object.exists()) {
+ Document resultDoc = DocumentHelper.createDocument();
+ Element multiStatusResponse = resultDoc.addElement("multistatus", "DAV:");
+ Element responseEl = multiStatusResponse.addElement("response");
+ try {
+ URL url = new URL(getBaseUrl(request), URLEncoder.encode(object.getName().getPath(), "UTF-8"));
+ log("!! " + url);
+ responseEl.addElement("href").addText(url.toExternalForm());
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ Element propstatEl = responseEl.addElement("propstat");
+ Element propEl = propstatEl.addElement("prop");
+
+ Element propertyUpdateEl = propDoc.getRootElement();
+ for (Object elObject : propertyUpdateEl.elements()) {
+ Element el = (Element) elObject;
+ if ("set".equals(el.getName())) {
+ for (Object propObject : el.elements()) {
+ setProperty(propEl, object, (Element) propObject);
+ }
+ } else if ("remove".equals(el.getName())) {
+ for (Object propObject : el.elements()) {
+ removeProperty(propEl, object, (Element) propObject);
+ }
+ }
+ }
+ propstatEl.addElement("status").addText(DavResource.STATUS_403);
+
+// log(resultDoc);
+
+ // write the actual response
+ XMLWriter writer = new XMLWriter(response.getWriter(), OutputFormat.createCompactFormat());
+ writer.write(resultDoc);
+ writer.flush();
+ writer.close();
+ } else {
+ log("!! " + object.getName().getPath() + " NOT FOUND");
+ response.sendError(HttpServletResponse.SC_NOT_FOUND);
+ }
+ } catch (DocumentException e) {
+ log("!! inavlid request: " + e.getMessage());
+ response.sendError(HttpServletResponse.SC_BAD_REQUEST);
+ }
+ }
+
+ private void setProperty(Element root, FileObject object, Element el) {
+ List propList = el.elements();
+ for (Object propElObject : propList) {
+ Element propEl = (Element) propElObject;
+ for (int i = 0; i < propEl.nodeCount(); i++) {
+ propEl.node(i).detach();
+ }
+ root.add(propEl.detach());
+ }
+ }
+
+ private void removeProperty(Element root, FileObject object, Element el) {
+ setProperty(root, object, el);
+ }
+
+
+}
blob - /dev/null
blob + 0299d52204489160b361b989b9f0e74e73180407 (mode 644)
--- /dev/null
+++ src/main/java/com/thinkberg/moxo/dav/PutHandler.java
+/*
+ * 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.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;
+import org.apache.commons.vfs.FileType;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * @author Matthias L. Jugel
+ * @version $Id$
+ */
+public class PutHandler extends WebdavHandler {
+ public void service(HttpServletRequest request, HttpServletResponse response) throws IOException {
+ FileObject object = ResourceManager.getFileObject(request.getPathInfo());
+
+ try {
+ LockManager.getInstance().checkCondition(object, getIf(request));
+ } catch (LockException e) {
+ if (e.getLocks() != null) {
+ response.sendError(SC_LOCKED);
+ } else {
+ response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
+ }
+ return;
+ }
+
+ // it is forbidden to write data on a folder
+ if (object.exists() && FileType.FOLDER.equals(object.getType())) {
+ response.sendError(HttpServletResponse.SC_FORBIDDEN);
+ return;
+ }
+
+ FileObject parent = object.getParent();
+ if (!parent.exists()) {
+ response.sendError(HttpServletResponse.SC_FORBIDDEN);
+ return;
+ }
+
+ if (!FileType.FOLDER.equals(parent.getType())) {
+ response.sendError(HttpServletResponse.SC_CONFLICT);
+ return;
+ }
+
+ InputStream is = request.getInputStream();
+ OutputStream os = object.getContent().getOutputStream();
+ log("copied " + Util.copyStream(is, os) + " bytes");
+ os.close();
+
+ response.setStatus(HttpServletResponse.SC_CREATED);
+ }
+}
blob - /dev/null
blob + 7e66661da892e8d3514f76702c740496e62ced5d (mode 644)
--- /dev/null
+++ src/main/java/com/thinkberg/moxo/dav/URLEncoder.java
+/*
+ * 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.dav;
+
+import java.io.UnsupportedEncodingException;
+import java.util.BitSet;
+
+/**
+ * Encode a URL but leave some special characters in plain text.
+ *
+ * @author Matthias L. Jugel
+ */
+class URLEncoder {
+
+ private static BitSet keepPlain;
+
+ static {
+ keepPlain = new BitSet(256);
+ int i;
+ for (i = 'a'; i <= 'z'; i++) {
+ keepPlain.set(i);
+ }
+ for (i = 'A'; i <= 'Z'; i++) {
+ keepPlain.set(i);
+ }
+ for (i = '0'; i <= '9'; i++) {
+ keepPlain.set(i);
+ }
+ keepPlain.set('+');
+ keepPlain.set('-');
+ keepPlain.set('_');
+ keepPlain.set('.');
+ keepPlain.set('*');
+ keepPlain.set('/');
+ keepPlain.set(':');
+ }
+
+
+ @SuppressWarnings({"SameParameterValue"})
+ public static String encode(String s, String enc) throws UnsupportedEncodingException {
+ byte[] buf = s.getBytes(enc);
+ StringBuffer result = new StringBuffer();
+ for (byte aBuf : buf) {
+ int c = (int) aBuf;
+ if (keepPlain.get(c & 0xFF)) {
+ result.append((char) c);
+ } else {
+ result.append('%').append(Integer.toHexString(c & 0xFF).toUpperCase());
+ }
+ }
+ return result.toString();
+ }
+}
blob - /dev/null
blob + c9b1dbfa79cede571c23e5fbce3af2951df4c49e (mode 644)
--- /dev/null
+++ src/main/java/com/thinkberg/moxo/dav/UnlockHandler.java
+/*
+ * 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.dav;
+
+import com.thinkberg.moxo.ResourceManager;
+import com.thinkberg.moxo.dav.lock.LockManager;
+import org.apache.commons.vfs.FileObject;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * @author Matthias L. Jugel
+ * @version $Id$
+ */
+public class UnlockHandler extends WebdavHandler {
+
+ public void service(HttpServletRequest request, HttpServletResponse response) throws IOException {
+ FileObject object = ResourceManager.getFileObject(request.getPathInfo());
+ String lockTokenHeader = request.getHeader("Lock-Token");
+ String lockToken = lockTokenHeader.substring(1, lockTokenHeader.length() - 1);
+ log("UNLOCK(" + lockToken + ")");
+
+ if (LockManager.getInstance().releaseLock(object, lockToken)) {
+ response.setStatus(HttpServletResponse.SC_NO_CONTENT);
+ } else {
+ response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+ }
+ }
+}
blob - /dev/null
blob + 1729f3d7d238c8ce6d25dae22834c23c3f734ea3 (mode 644)
--- /dev/null
+++ src/main/java/com/thinkberg/moxo/dav/Util.java
+/*
+ * 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.dav;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+
+/**
+ * @author Matthias L. Jugel
+ * @version $Id$
+ */
+public class Util {
+
+ private static final SimpleDateFormat httpDateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
+
+ public static String getDateString(long time) {
+ return httpDateFormat.format(new Date(time));
+ }
+
+// public static String getISODateString(long time) {
+// return "";
+// }
+
+
+ public static int copyStream(InputStream is, OutputStream os) throws IOException {
+ byte[] buffer = new byte[4096];
+ int bytesRead, bytesCount = 0;
+ while ((bytesRead = is.read(buffer)) != -1) {
+ os.write(buffer, 0, bytesRead);
+ bytesCount += bytesRead;
+ }
+ os.flush();
+
+ return bytesCount;
+ }
+}
blob - /dev/null
blob + c9780e5ce9c328ffad20e67ea417ac5ce93ae6e7 (mode 644)
--- /dev/null
+++ src/main/java/com/thinkberg/moxo/dav/WebdavHandler.java
+/*
+ * 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.dav;
+
+import com.thinkberg.moxo.ResourceManager;
+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 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;
+import java.util.Arrays;
+
+/**
+ * @author Matthias L. Jugel
+ * @version $Id$
+ */
+public abstract class WebdavHandler {
+ static final int SC_LOCKED = 423;
+ static final int SC_MULTI_STATUS = 207;
+
+ private HttpServlet servlet;
+
+ 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();
+ try {
+ XMLWriter xmlWriter = new XMLWriter(bos, OutputFormat.createPrettyPrint());
+ xmlWriter.write(element);
+ System.out.print(bos.toString());
+ } catch (IOException e) {
+ servlet.log("!! " + e.getMessage());
+ }
+ }
+
+ void log(String message) {
+ servlet.log("## " + message);
+ }
+
+
+ static URL getBaseUrl(HttpServletRequest request) {
+ try {
+ String requestUrl = request.getRequestURL().toString();
+ String requestUri = request.getRequestURI();
+ String requestUrlBase = requestUrl.substring(0, requestUrl.length() - requestUri.length());
+ return new URL(requestUrlBase);
+ } catch (MalformedURLException e) {
+ // ignore ...
+ }
+ return null;
+ }
+
+
+ /**
+ * Get the depth header value. This value defines how operations
+ * like propfind, move, copy etc. handle collections. A depth value
+ * of 0 will only return the current collection, 1 will return
+ * children too and infinity will recursively operate.
+ *
+ * @param request the servlet request
+ * @return the depth value as 0, 1 or Integer.MAX_VALUE;
+ */
+ int getDepth(HttpServletRequest request) {
+ String depth = request.getHeader("Depth");
+ int depthValue;
+
+ if (null == depth || "infinity".equalsIgnoreCase(depth)) {
+ depthValue = Integer.MAX_VALUE;
+ } else {
+ depthValue = Integer.parseInt(depth);
+ }
+
+ log("Depth: " + depthValue);
+ return depthValue;
+ }
+
+ /**
+ * Get the overwrite header value, whether to overwrite destination
+ * objects or collections or not.
+ *
+ * @param request the servlet request
+ * @return true or false
+ */
+ boolean getOverwrite(HttpServletRequest request) {
+ String overwrite = request.getHeader("Overwrite");
+ boolean overwriteValue = overwrite == null || "T".equals(overwrite);
+ log("Overwrite: " + overwriteValue);
+ return overwriteValue;
+ }
+
+ /**
+ * Get the destination object or collection. The destination header contains
+ * a URL to the destination which is returned as a file object.
+ *
+ * @param request the servlet request
+ * @return the file object of the destination
+ * @throws FileSystemException if the filesystem cannot create a file object
+ * @throws MalformedURLException if the url is misformatted
+ */
+ FileObject getDestination(HttpServletRequest request) throws FileSystemException, MalformedURLException {
+ String targetUrlStr = request.getHeader("Destination");
+ FileObject targetObject = null;
+ if (null != targetUrlStr) {
+ URL target = new URL(targetUrlStr);
+ targetObject = ResourceManager.getFileObject(target.getPath());
+ log("Destination: " + targetObject.getName().getPath());
+ }
+
+ return targetObject;
+ }
+
+ /**
+ * Get the if header.
+ *
+ * @param request the request
+ * @return the value if the If: header.
+ */
+ String getIf(HttpServletRequest request) {
+ return request.getHeader("If");
+ }
+
+ /**
+ * Get and parse the timeout header value.
+ *
+ * @param request the request
+ * @return the timeout
+ */
+ long getTimeout(HttpServletRequest request) {
+ String timeout = request.getHeader("Timeout");
+ if (null != timeout) {
+ String[] timeoutValues = timeout.split(",[ ]*");
+ log(Arrays.asList(timeoutValues).toString());
+ if ("infinity".equalsIgnoreCase(timeoutValues[0])) {
+ return -1;
+ } else {
+ return Integer.parseInt(timeoutValues[0].replaceAll("Second-", ""));
+ }
+ }
+ return -1;
+ }
+}
blob - /dev/null
blob + 4356ce41848ea62de23b8a036e7aea950df89046 (mode 644)
--- /dev/null
+++ src/main/java/com/thinkberg/moxo/dav/data/AbstractDavResource.java
+/*
+ * 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.dav.data;
+
+import org.dom4j.Element;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author Matthias L. Jugel
+ * @version $Id$
+ */
+public abstract class AbstractDavResource {
+ private static final String STATUS_200 = "HTTP/1.1 200 OK";
+ private static final String STATUS_404 = "HTTP/1.1 404 Not Found";
+ public static final String STATUS_403 = "HTTP/1.1 403 Forbidden";
+
+ private static final String TAG_PROPSTAT = "propstat";
+ 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);
+
+ Set<String> missingProperties = new HashSet<String>();
+ for (String propertyName : requestedProperties) {
+ if (!addPropertyValue(propEl, propertyName)) {
+ missingProperties.add(propertyName);
+ }
+ }
+ propStatEl.addElement(TAG_STATUS).addText(STATUS_200);
+
+ // add missing properties status
+ if (missingProperties.size() > 0) {
+ propStatEl = root.addElement(TAG_PROPSTAT);
+ propEl = propStatEl.addElement(TAG_PROP);
+ for (String el : missingProperties) {
+ propEl.addElement(el);
+ }
+ propStatEl.addElement(TAG_STATUS).addText(STATUS_404);
+ }
+
+ return root;
+ }
+
+ protected abstract boolean addPropertyValue(Element root, String propertyName);
+}
blob - /dev/null
blob + 918da1c04a6de60ad3be17615e2e920da8a2975b (mode 644)
--- /dev/null
+++ src/main/java/com/thinkberg/moxo/dav/data/DavCollection.java
+/*
+ * 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.dav.data;
+
+import org.apache.commons.vfs.FileObject;
+import org.dom4j.Element;
+
+/**
+ * @author Matthias L. Jugel
+ * @version $Id$
+ */
+public class DavCollection extends DavResource {
+ public static final String COLLECTION = "collection";
+
+ public DavCollection(FileObject object) {
+ super(object);
+ }
+
+
+ public DavCollection(FileObject object, boolean ignoreValues) {
+ super(object, ignoreValues);
+ }
+
+ protected boolean addResourceTypeProperty(Element root) {
+ root.addElement(PROP_RESOURCETYPE).addElement(COLLECTION);
+ return true;
+ }
+
+ /**
+ * Ignore content language for collections.
+ *
+ * @param root the prop element to add to
+ * @return true, even though nothing is added
+ */
+ protected boolean addGetContentLanguageProperty(Element root) {
+ return true;
+ }
+
+ /**
+ * Ignore content length for collections.
+ *
+ * @param root the prop element to add to
+ * @return true, even though nothing is added
+ */
+ protected boolean addGetContentLengthProperty(Element root) {
+ return true;
+ }
+
+ /**
+ * Ignore content type for collections.
+ *
+ * @param root the prop element to add to
+ * @return true, even though nothing is added
+ */
+ protected boolean addGetContentTypeProperty(Element root) {
+ return true;
+ }
+
+ protected boolean addQuotaProperty(Element root) {
+ root.addElement(PROP_QUOTA).addText("1000000");
+ return true;
+ }
+
+ protected boolean addQuotaUsedProperty(Element root) {
+ // TODO add correct handling of used quota
+ root.addElement(PROP_QUOTA_USED).addText("0");
+ return true;
+ }
+
+ protected boolean addQuotaAvailableBytesProperty(Element root) {
+ root.addElement(PROP_QUOTA_AVAILABLE_BYTES).addText("1000000");
+ return true;
+ }
+
+ protected boolean addQuotaUsedBytesProperty(Element root) {
+ // TODO add correct handling of used quota
+ root.addElement(PROP_QUOTA_USED_BYTES).addText("0");
+ return true;
+ }
+}
blob - /dev/null
blob + 220a25349e7c8e941c9ae4944459052216833000 (mode 644)
--- /dev/null
+++ src/main/java/com/thinkberg/moxo/dav/data/DavResource.java
+/*
+ * 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.dav.data;
+
+import com.thinkberg.moxo.dav.Util;
+import com.thinkberg.moxo.dav.lock.Lock;
+import com.thinkberg.moxo.dav.lock.LockManager;
+import org.apache.commons.vfs.FileObject;
+import org.apache.commons.vfs.FileSystemException;
+import org.dom4j.Element;
+
+import java.util.Arrays;
+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
+ public static final String PROP_CREATION_DATE = "creationdate";
+ public static final String PROP_DISPLAY_NAME = "displayname";
+ private static final String PROP_GET_CONTENT_LANGUAGE = "getcontentlanguage";
+ private static final String PROP_GET_CONTENT_LENGTH = "getcontentlength";
+ private static final String PROP_GET_CONTENT_TYPE = "getcontenttype";
+ private static final String PROP_GET_ETAG = "getetag";
+ private static final String PROP_GET_LAST_MODIFIED = "getlastmodified";
+ private static final String PROP_LOCK_DISCOVERY = "lockdiscovery";
+ public static final String PROP_RESOURCETYPE = "resourcetype";
+ private static final String PROP_SOURCE = "source";
+ private static final String PROP_SUPPORTED_LOCK = "supportedlock";
+
+ // non-standard properties
+ static final String PROP_QUOTA = "quota";
+ static final String PROP_QUOTA_USED = "quotaused";
+ static final String PROP_QUOTA_AVAILABLE_BYTES = "quota-available-bytes";
+ static final String PROP_QUOTA_USED_BYTES = "quota-used-bytes";
+
+ // list of standard supported properties (for allprop/propname)
+ public static final List<String> ALL_PROPERTIES = Arrays.asList(
+ PROP_CREATION_DATE,
+ PROP_DISPLAY_NAME,
+ PROP_GET_CONTENT_LANGUAGE,
+ PROP_GET_CONTENT_LENGTH,
+ PROP_GET_CONTENT_TYPE,
+ PROP_GET_ETAG,
+ PROP_GET_LAST_MODIFIED,
+ PROP_LOCK_DISCOVERY,
+ PROP_RESOURCETYPE,
+ PROP_SOURCE,
+ PROP_SUPPORTED_LOCK
+ );
+
+ private final FileObject object;
+ private boolean ignoreValues = false;
+
+ public DavResource(FileObject object) {
+ this(object, false);
+ }
+
+
+ public DavResource(FileObject object, boolean ignoreValues) {
+ this.object = object;
+ this.ignoreValues = ignoreValues;
+
+ }
+
+ /**
+ * Ignore values
+ *
+ * @param ignoreValues true if the serialized xml should not contain values
+ */
+ public void setIgnoreValues(boolean ignoreValues) {
+ this.ignoreValues = ignoreValues;
+ }
+
+ /**
+ * Add the value for a given property to the result document. If the value
+ * is missing or can not be added for some reason it will return false to
+ * indicate a missing property.
+ *
+ * @param root the root element for the result document fragment
+ * @param propertyName the property name to add
+ * @return true for successful addition and false for missing data
+ */
+ protected boolean addPropertyValue(Element root, String propertyName) {
+ if (PROP_CREATION_DATE.equals(propertyName)) {
+ return addCreationDateProperty(root);
+ } else if (PROP_DISPLAY_NAME.equals(propertyName)) {
+ return addGetDisplayNameProperty(root);
+ } else if (PROP_GET_CONTENT_LANGUAGE.equals(propertyName)) {
+ return addGetContentLanguageProperty(root);
+ } else if (PROP_GET_CONTENT_LENGTH.equals(propertyName)) {
+ return addGetContentLengthProperty(root);
+ } else if (PROP_GET_CONTENT_TYPE.equals(propertyName)) {
+ return addGetContentTypeProperty(root);
+ } else if (PROP_GET_ETAG.equals(propertyName)) {
+ return addGetETagProperty(root);
+ } else if (PROP_GET_LAST_MODIFIED.equals(propertyName)) {
+ return addGetLastModifiedProperty(root);
+ } else if (PROP_LOCK_DISCOVERY.equals(propertyName)) {
+ return addLockDiscoveryProperty(root);
+ } else if (PROP_RESOURCETYPE.equals(propertyName)) {
+ return addResourceTypeProperty(root);
+ } else if (PROP_SOURCE.equals(propertyName)) {
+ return addSourceProperty(root);
+ } else if (PROP_SUPPORTED_LOCK.equals(propertyName)) {
+ return addSupportedLockProperty(root);
+ } else {
+ // handle non-standard properties (keep a little separate)
+ if (PROP_QUOTA.equals(propertyName)) {
+ return addQuotaProperty(root);
+ } else if (PROP_QUOTA_USED.equals(propertyName)) {
+ return addQuotaUsedProperty(root);
+ } else if (PROP_QUOTA_AVAILABLE_BYTES.equals(propertyName)) {
+ return addQuotaAvailableBytesProperty(root);
+ } else if (PROP_QUOTA_USED_BYTES.equals(propertyName)) {
+ return addQuotaUsedBytesProperty(root);
+ }
+ }
+
+ 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) {
+ el.addCDATA(object.getName().getBaseName());
+ }
+ 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);
+ if (!ignoreValues) {
+ el.addText("" + object.getContent().getSize());
+ }
+ return true;
+ } catch (FileSystemException e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ @SuppressWarnings({"WeakerAccess"})
+ protected boolean addGetContentTypeProperty(Element root) {
+ try {
+ String contentType = object.getContent().getContentInfo().getContentType();
+ if (null == contentType || "".equals(contentType)) {
+ return false;
+ }
+
+ Element el = root.addElement(PROP_GET_CONTENT_TYPE);
+ if (!ignoreValues) {
+ el.addText(contentType);
+ }
+ return true;
+ } catch (FileSystemException e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ @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);
+ if (!ignoreValues) {
+ el.addText(Util.getDateString(object.getContent().getLastModifiedTime()));
+ }
+ return true;
+ } catch (FileSystemException e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ @SuppressWarnings({"WeakerAccess"})
+ protected boolean addLockDiscoveryProperty(Element root) {
+ Element lockdiscoveryEl = root.addElement(PROP_LOCK_DISCOVERY);
+ try {
+ List<Lock> locks = LockManager.getInstance().discoverLock(object);
+ if (locks != null && !locks.isEmpty()) {
+ for (Lock lock : locks) {
+ if (lock != null && !ignoreValues) {
+ lock.serializeToXml(lockdiscoveryEl);
+ }
+ }
+ }
+ return true;
+ } catch (FileSystemException e) {
+ e.printStackTrace();
+ root.remove(lockdiscoveryEl);
+ return false;
+ }
+ }
+
+ @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) {
+ Element exclLockentryEl = supportedlockEl.addElement("lockentry");
+ exclLockentryEl.addElement("lockscope").addElement("exclusive");
+ exclLockentryEl.addElement("locktype").addElement("write");
+ Element sharedLockentryEl = supportedlockEl.addElement("lockentry");
+ sharedLockentryEl.addElement("lockscope").addElement("shared");
+ sharedLockentryEl.addElement("locktype").addElement("write");
+ }
+
+ 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 - /dev/null
blob + 8d66d4881885922677820797ffae246c27b96ef9 (mode 644)
--- /dev/null
+++ src/main/java/com/thinkberg/moxo/dav/data/DavResourceFactory.java
+/*
+ * 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.dav.data;
+
+import org.apache.commons.vfs.FileObject;
+import org.apache.commons.vfs.FileSystemException;
+import org.apache.commons.vfs.FileType;
+
+/**
+ * @author Matthias L. Jugel
+ * @version $Id$
+ */
+public class DavResourceFactory {
+ private static DavResourceFactory instance;
+
+ public static DavResourceFactory getInstance() {
+ if (null == instance) {
+ instance = new DavResourceFactory();
+ }
+ return instance;
+ }
+
+ private DavResourceFactory() {
+
+ }
+
+ public DavResource getDavResource(FileObject object) throws FileSystemException {
+ if (FileType.FOLDER.equals(object.getType())) {
+ return new DavCollection(object);
+ } else {
+ return new DavResource(object);
+ }
+ }
+}
blob - /dev/null
blob + 44f4c9aaa4abefb4b0565650ccaa2960ed875225 (mode 644)
--- /dev/null
+++ src/main/java/com/thinkberg/moxo/dav/lock/Lock.java
+/*
+ * 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.dav.lock;
+
+import org.apache.commons.vfs.FileObject;
+import org.dom4j.Element;
+
+import java.net.URL;
+
+/**
+ * @author Matthias L. Jugel
+ * @version $Id$
+ */
+public class Lock {
+ public final static String SHARED = "shared";
+ public final static String EXCLUSIVE = "exclusive";
+
+ public final static String WRITE = "write";
+
+ private final FileObject object;
+ private final String type;
+ private final String scope;
+ private final Object owner;
+ private final int depth;
+ private final long timeout;
+ private final String token;
+
+
+ public Lock(FileObject object, String type, String scope, Object owner,
+ int depth, long timeout) {
+ this.object = object;
+ this.type = type;
+ this.scope = scope;
+ this.owner = owner;
+ this.depth = depth;
+ this.timeout = timeout;
+
+ this.token = "opaquelocktoken:" + Integer.toHexString(object.hashCode());
+ }
+
+ public FileObject getObject() {
+ return object;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public String getScope() {
+ return scope;
+ }
+
+ public Object getOwner() {
+ return owner;
+ }
+
+ public int getDepth() {
+ return depth;
+ }
+
+ @SuppressWarnings({"WeakerAccess"})
+ public String getDepthValue() {
+ switch (depth) {
+ case 0:
+ return "0";
+ case 1:
+ return "1";
+ default:
+ return "Infinity";
+ }
+ }
+
+ @SuppressWarnings({"WeakerAccess"})
+ public String getTimeout() {
+ if (timeout == -1) {
+ return "Infinity";
+ }
+ return "Second-" + timeout;
+ }
+
+ public String getToken() {
+ return this.token;
+ }
+
+ /**
+ * Create an XML serialized version of the lock by adding an activelock tag
+ * with the locks properties to the root element provided.
+ *
+ * @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());
+ activelockEl.addElement("lockscope").addElement(getScope());
+ activelockEl.addElement("depth").addText(getDepthValue());
+ // TODO handle owner correctly
+ if (getOwner() instanceof URL) {
+ activelockEl.addElement("owner").addElement("href")
+ .addText(((URL) getOwner()).toExternalForm());
+ } else {
+ activelockEl.addElement("owner").addText((String) getOwner());
+ }
+ activelockEl.addElement("timeout").addText(getTimeout());
+ activelockEl.addElement("locktoken")
+ .addElement("href").addText(getToken());
+
+ return root;
+ }
+
+ /**
+ * There can only be one lock per object, thus compare the file objects.
+ *
+ * @param other the other lock to compare to
+ * @return whether this lock is for the same file object
+ */
+ public boolean equals(Object other) {
+ return object.equals(((Lock) other).object);
+ }
+
+
+ public String toString() {
+ return new StringBuffer().append("Lock[")
+ .append(object).append(",")
+ .append(type).append(",")
+ .append(scope).append("]").toString();
+ }
+}
+
blob - /dev/null
blob + 0944f6914361ef87067e9e0739796e73448509e2 (mode 644)
--- /dev/null
+++ src/main/java/com/thinkberg/moxo/dav/lock/LockConditionFailedException.java
+/*
+ * 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.dav.lock;
+
+import java.util.List;
+
+/**
+ * @author Matthias L. Jugel
+ * @version $Id$
+ */
+public class LockConditionFailedException extends LockException {
+ private String condition = null;
+
+ public LockConditionFailedException(List<Lock> locks) {
+ super(locks);
+ }
+
+ public LockConditionFailedException(List<Lock> locks, String condition) {
+ super(locks);
+ this.condition = condition;
+
+ }
+
+ public String getCondition() {
+ return condition;
+ }
+}
blob - /dev/null
blob + bc0d2328671d1f91e7b57dc24d33acd8d01e849f (mode 644)
--- /dev/null
+++ src/main/java/com/thinkberg/moxo/dav/lock/LockConflictException.java
+/*
+ * 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.dav.lock;
+
+import java.util.List;
+
+/**
+ * @author Matthias L. Jugel
+ * @version $Id$
+ */
+public class LockConflictException extends LockException {
+ public LockConflictException(List<Lock> locks) {
+ super(locks);
+ }
+}
blob - /dev/null
blob + 937cae1dfcf4c4ed1d768feb4e82ebf20cff4a46 (mode 644)
--- /dev/null
+++ src/main/java/com/thinkberg/moxo/dav/lock/LockException.java
+/*
+ * 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.dav.lock;
+
+import java.util.List;
+
+/**
+ * @author Matthias L. Jugel
+ * @version $Id$
+ */
+public class LockException extends Exception {
+ private final List<Lock> locks;
+
+ public LockException(List<Lock> locks) {
+ super();
+ this.locks = locks;
+ }
+
+ public List<Lock> getLocks() {
+ return locks;
+ }
+}
blob - /dev/null
blob + 3a78f6d504fbd03a7ef5615a5b68b6a02b64931e (mode 644)
--- /dev/null
+++ src/main/java/com/thinkberg/moxo/dav/lock/LockManager.java
+/*
+ * 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.dav.lock;
+
+import com.thinkberg.moxo.vfs.DepthFileSelector;
+import org.apache.commons.vfs.FileObject;
+import org.apache.commons.vfs.FileSelectInfo;
+import org.apache.commons.vfs.FileSystemException;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * The lock manager is responsible for exclusive and shared write locks on the
+ * DAV server. It is used to acquire a lock, release a lock, discover existing
+ * locks or check conditions. The lock manager is a singleton.
+ *
+ * @author Matthias L. Jugel
+ * @version $Id$
+ */
+public class LockManager {
+ private static LockManager instance = null;
+
+ /**
+ * Get an instance of the lock manager.
+ *
+ * @return the lock manager
+ */
+ public static LockManager getInstance() {
+ if (null == instance) {
+ instance = new LockManager();
+ }
+
+ return instance;
+ }
+
+ private final Map<FileObject, List<Lock>> lockMap;
+
+ /**
+ * The lock manager is a singleton and cannot be instantiated directly.
+ */
+ private LockManager() {
+ lockMap = new HashMap<FileObject, List<Lock>>();
+ }
+
+ /**
+ * Acquire a lock. This will first check for conflicts and throws exceptions if
+ * there are existing locks or for some reason the lock could not be acquired.
+ *
+ * @param lock the lock to acquire
+ * @throws LockConflictException if an existing lock has priority
+ * @throws FileSystemException if the file object and its path cannot be accessed
+ */
+ public void acquireLock(Lock lock) throws LockConflictException, FileSystemException {
+ checkConflicts(lock);
+ addLock(lock);
+ }
+
+ /**
+ * Release a lock on a file object with a given lock token. Releeases the lock if
+ * if one exists and if the lock token is valid for the found lock.
+ *
+ * @param object the file object we want to unlock
+ * @param token the lock token associated with the file object
+ * @return true if the lock has been released, false if not
+ */
+ public boolean releaseLock(FileObject object, String token) {
+ List<Lock> locks = lockMap.get(object);
+ if (null != locks) {
+ for (Lock lock : locks) {
+ if (lock.getToken().equals(token)) {
+ locks.remove(lock);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Discover locks for a given file object. This will find locks for the object
+ * itself and parent path locks with a depth that reaches the file object.
+ *
+ * @param object the file object to find locks for
+ * @return the locks that are found for this file object
+ * @throws FileSystemException if the file object or its parents cannot be accessed
+ */
+ public List<Lock> discoverLock(FileObject object) throws FileSystemException {
+ FileObject parent = object;
+ while (parent != null) {
+ List<Lock> parentLocks = lockMap.get(parent);
+ if (parentLocks != null && !parentLocks.isEmpty()) {
+ return parentLocks;
+ }
+ parent = parent.getParent();
+ }
+
+ return null;
+ }
+
+ /**
+ * Check a condition for a file object. The condition check looks for locks on the
+ * given file object and will throw exceptions if the condition does not meet the
+ * lock requirements (i.e. lock token in the condition differes from the token in
+ * the discovered lock) or no condition exists if a lock was discovered. If no lock
+ * was discovered but a condition exists a lock condition exception will be thrown.
+ *
+ * @param object the file object in question
+ * @param ifCond the condition to check
+ * @return the lock found for the given if condition and the object
+ * @throws FileSystemException if the object or path cannot be accessed
+ * @throws LockConflictException if there is a a lock but no condition
+ * @throws LockConditionFailedException if the condition and lock does not match
+ */
+ public Lock checkCondition(FileObject object, String ifCond)
+ throws FileSystemException, LockConflictException, LockConditionFailedException {
+ List<Lock> locks = discoverLock(object);
+ if (null != locks && !locks.isEmpty()) {
+ // if there is no condition but a lock, this must fail
+ if (null == ifCond) {
+ throw new LockConflictException(locks);
+ }
+
+ // simple check whether the token is in the condition (TODO: check for NOT)
+ for (Lock lock : locks) {
+ if (ifCond.indexOf("<" + lock.getToken() + ">") != -1) {
+ return lock;
+ }
+ }
+ throw new LockConditionFailedException(locks);
+ } else if (null != ifCond) {
+ // no lock but a condition must fail too
+ throw new LockConditionFailedException(null);
+ }
+
+ return null;
+ }
+
+
+ /**
+ * Add a lock to the list of shared locks of a given object.
+ *
+ * @param lock the lock to add
+ */
+ private void addLock(Lock lock) {
+ FileObject object = lock.getObject();
+ List<Lock> locks = lockMap.get(object);
+ if (null == locks) {
+ locks = new ArrayList<Lock>();
+ lockMap.put(object, locks);
+ }
+ locks.add(lock);
+ }
+
+ /**
+ * Check whether a lock conflicts with already existing locks up and down the path.
+ * First we go up the path to check for parent locks that may include the file object
+ * and the go down the directory tree (if depth requires it) to check locks that
+ * will conflict.
+ *
+ * @param requestedLock the lock requested
+ * @throws LockConflictException if a conflicting lock was found
+ * @throws FileSystemException if the file object or path cannot be accessed
+ */
+ private void checkConflicts(final Lock requestedLock) throws LockConflictException, FileSystemException {
+ // find locks in the parent path
+ FileObject parent = requestedLock.getObject();
+ while (parent != null) {
+ List<Lock> parentLocks = lockMap.get(parent);
+ if (parentLocks != null && !parentLocks.isEmpty()) {
+ for (Lock parentLock : parentLocks) {
+ if (Lock.EXCLUSIVE.equals(requestedLock.getScope()) || Lock.EXCLUSIVE.equals(parentLock.getScope())) {
+ throw new LockConflictException(parentLocks);
+ }
+ }
+ }
+ parent = parent.getParent();
+ }
+
+ // look for locks down the path (if depth requests it)
+ if (requestedLock.getDepth() != 0 && requestedLock.getObject().getChildren().length > 0) {
+ requestedLock.getObject().findFiles(new DepthFileSelector(1, requestedLock.getDepth()) {
+ public boolean includeFile(FileSelectInfo fileSelectInfo) throws Exception {
+ List<Lock> childLocks = lockMap.get(fileSelectInfo.getFile());
+ for (Lock childLock : childLocks) {
+ if (Lock.EXCLUSIVE.equals(requestedLock.getScope()) || Lock.EXCLUSIVE.equals(childLock.getScope())) {
+ throw new LockConflictException(childLocks);
+ }
+ }
+ return false;
+ }
+ }, false, new ArrayList());
+ }
+ }
+}
blob - /dev/null
blob + 5d8f96a1cb04ca9f1dfd9b2d00daa1eb695d0e06 (mode 644)
--- /dev/null
+++ src/main/java/com/thinkberg/moxo/s3/S3Connector.java
+/*
+ * 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.s3;
+
+import org.apache.commons.vfs.FileSystemException;
+import org.jets3t.service.Jets3tProperties;
+import org.jets3t.service.S3Service;
+import org.jets3t.service.S3ServiceException;
+import org.jets3t.service.impl.rest.httpclient.RestS3Service;
+import org.jets3t.service.model.S3Bucket;
+import org.jets3t.service.security.AWSCredentials;
+
+/**
+ * @author Matthias L. Jugel
+ */
+public class S3Connector {
+ private static final String APPLICATION_DESCRIPTION = "S3 VFS Connector/1.0";
+
+ private static S3Connector instance;
+
+ /**
+ * Get an instance of the S3Connector which is initialized and authenticated to the
+ * Amazon S3 Service.
+ *
+ * @return an S3 connector
+ * @throws FileSystemException if connection or authentication fails
+ */
+ public static S3Connector getInstance() throws FileSystemException {
+ if (null == instance) {
+ instance = new S3Connector();
+ }
+ return instance;
+ }
+
+ private S3Service service;
+
+ /**
+ * Initialize Amazon S3.
+ *
+ * @throws FileSystemException if S3 can't be initialized
+ */
+ private S3Connector() throws FileSystemException {
+ String propertiesFileName = System.getProperty("moxo.properties", "moxo.properties");
+ Jets3tProperties properties = Jets3tProperties.getInstance(propertiesFileName);
+
+ if (!properties.isLoaded()) {
+ throw new FileSystemException("can't find S3 configuration: " + propertiesFileName);
+ }
+
+ AWSCredentials awsCredentials = new AWSCredentials(
+ properties.getStringProperty("accesskey", null),
+ properties.getStringProperty("secretkey", null));
+
+
+ try {
+ service = new RestS3Service(awsCredentials, APPLICATION_DESCRIPTION, null);
+ } catch (S3ServiceException e) {
+ throw new FileSystemException("can't initialize S3 Service", e);
+ }
+ }
+
+ /**
+ * Get a virtual file system root corresponding to a bucket.
+ *
+ * @param bucket the bucket that contains the filesystem
+ * @return the S3 root
+ * @throws FileSystemException if the bucket is not found
+ */
+ public S3VfsRoot getRoot(String bucket) throws FileSystemException {
+ try {
+ if (service.isBucketAccessible(bucket)) {
+ return new S3VfsRootImpl(service, new S3Bucket(bucket));
+ }
+ throw new FileSystemException("vsf.provider.vfs/cant-access.error");
+ } catch (S3ServiceException e) {
+ throw new FileSystemException(e);
+ }
+ }
+}
blob - /dev/null
blob + b647ccf5ddd6dc7bd616ac1e91ecafb4480ede53 (mode 644)
--- /dev/null
+++ src/main/java/com/thinkberg/moxo/s3/S3VfsObject.java
+/*
+ * 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.s3;
+
+import org.apache.commons.vfs.FileSystemException;
+import org.apache.commons.vfs.FileType;
+
+import java.io.InputStream;
+
+/**
+ * This interface describes methods to access S3 Objects whether files or folders.
+ *
+ * @author Matthias L. Jugel
+ */
+public interface S3VfsObject {
+ /**
+ * The base name of the S3 Object.
+ *
+ * @return the base name without path
+ */
+ public String getName();
+
+ /**
+ * The absolute path of the S3 Object.
+ *
+ * @return the absolute path
+ */
+ public String getPath();
+
+ /**
+ * The type of the S3 Object. May be file, folder or imaginary.
+ *
+ * @return the file type
+ * @see org.apache.commons.vfs.FileType#FILE
+ * @see org.apache.commons.vfs.FileType#FOLDER
+ * @see org.apache.commons.vfs.FileType#IMAGINARY
+ */
+ public FileType getType();
+
+ /**
+ * Get the last modified time. The value is undefined if this is an imaginary file.
+ *
+ * @return the last modified time in milliseconds
+ */
+ public long getLastModified();
+
+ /**
+ * Get the content length. The value may be 0 for imaginary files.
+ *
+ * @return the content length in bytes
+ */
+ public long getContentLength();
+
+ /**
+ * Get the actual content MIME type. May be null for imaginary files.
+ *
+ * @return the MIME type
+ */
+ public String getContentType();
+
+ /**
+ * Get the input stream to read data from the object or null if this file is imaginary
+ *
+ * @return the input stream
+ * @throws FileSystemException if the S3 object cannot be accessed
+ */
+ public InputStream getInputStream() throws FileSystemException;
+
+ /**
+ * List all children names relative to this S3 Object. It is assumed that this is
+ * only called on folders. Non-Folders may return results but there is no defined
+ * behaviour in such a case.
+ *
+ * @return the list of children names (without path)
+ * @throws FileSystemException if the object cannot be accessed
+ */
+ public String[] getChildren() throws FileSystemException;
+}
blob - /dev/null
blob + 413839eee68c4c031e4a9e55a9ad6c96d64f13c8 (mode 644)
--- /dev/null
+++ src/main/java/com/thinkberg/moxo/s3/S3VfsObjectImpl.java
+/*
+ * 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.s3;
+
+import org.apache.commons.vfs.FileSystemException;
+import org.apache.commons.vfs.FileType;
+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.InputStream;
+
+/**
+ * Implementation of the virtual S3 file system object using the Jets3t library.
+ *
+ * @author Matthias L. Jugel
+ */
+public class S3VfsObjectImpl implements S3VfsObject {
+ private final S3Service service;
+ private final S3Bucket bucket;
+ private S3Object object;
+
+ /**
+ * Create a new S3 Object wrapper for the virtual filesystem. If an object exists its details
+ * are loaded and a virtual object is created for non-existing files. If data is requested that
+ * is not part of the details (HEAD) the real object is loaded on demand.
+ *
+ * @param service the S3 service used for retrieving the object
+ * @param bucket the bucket the object is located in
+ * @param path the full path to the S3 object (the key)
+ * @throws FileSystemException if there is a problem accessing the object
+ */
+ @SuppressWarnings({"RedundantThrows"})
+ public S3VfsObjectImpl(S3Service service, S3Bucket bucket, String path) throws FileSystemException {
+ this.service = service;
+ this.bucket = bucket;
+ // check object and load details or create a virtual object
+ String s3Path = makeS3Path(path);
+ try {
+ object = service.getObjectDetails(bucket, s3Path);
+ } catch (S3ServiceException e) {
+ object = new S3Object(bucket, s3Path);
+ }
+ }
+
+ /**
+ * Get the name of this object.
+ *
+ * @return the base name of the object
+ */
+ public String getName() {
+ String key = object.getKey();
+ if ("".equals(key)) {
+ return "/";
+ }
+
+ int lastSlash = key.lastIndexOf("/");
+ if (lastSlash == -1) {
+ return key;
+ } else {
+ return key.substring(lastSlash + 1);
+ }
+ }
+
+ /**
+ * The file type as provided by commons-vfs.
+ *
+ * @return the file type
+ * @see org.apache.commons.vfs.FileType#FILE
+ * @see org.apache.commons.vfs.FileType#FOLDER
+ * @see org.apache.commons.vfs.FileType#IMAGINARY
+ */
+ public FileType getType() {
+ if (null == object.getContentType()) {
+ return FileType.IMAGINARY;
+ }
+
+ String key = object.getKey();
+ String contentType = object.getContentType();
+ if ("".equals(key) || "/".equals(key) || Mimetypes.MIMETYPE_JETS3T_DIRECTORY.equals(contentType)) {
+ return FileType.FOLDER;
+ }
+
+ return FileType.FILE;
+ }
+
+ /**
+ * Get the full path of the object as an absolute path (including heading /).
+ *
+ * @return the full path
+ */
+ public String getPath() {
+ return makeAbsolutePath(object.getKey());
+ }
+
+ /**
+ * Get the last modified time of the object. The result is undefined if this object does not exist.
+ *
+ * @return the last modified time
+ */
+ public long getLastModified() {
+ return object.getLastModifiedDate().getTime();
+ }
+
+ /**
+ * Get the content length of the object. The result is 0 for non-existing objects.
+ *
+ * @return the content length
+ */
+ public long getContentLength() {
+ return object.getContentLength();
+ }
+
+ /**
+ * Get the actual content type as a MIME type. The result is undefined if this object does not exist.
+ *
+ * @return the mime type
+ */
+ public String getContentType() {
+ return object.getContentType();
+ }
+
+ /**
+ * Get all children names relative to this object. It is assumed that
+ * this method is called for directories only. The returned names are
+ * not absolute names, but rather relative base names.
+ *
+ * @return the list of children names
+ * @throws FileSystemException if the object cannot be accessed
+ */
+ public String[] getChildren() throws FileSystemException {
+ String path = object.getKey();
+ // make sure we add a '/' slash at the end to find children
+ if (!"".equals(path)) {
+ path = path + "/";
+ }
+
+ try {
+ S3Object[] children = service.listObjects(bucket, path, "/");
+ String[] childrenNames = new String[children.length];
+ for (int i = 0; i < children.length; i++) {
+ if (!children[i].getKey().equals(path)) {
+ // strip path from name (leave only base name)
+ childrenNames[i] = children[i].getKey().replaceAll("[^/]*//*", "");
+ }
+ }
+ return childrenNames;
+ } catch (S3ServiceException e) {
+ throw new FileSystemException(e);
+ }
+ }
+
+ /**
+ * Get the input stream to read data from the object. Returns null for non-existing objects.
+ * Calling this method causes the object to be reloaded if it has not yet been fully
+ * retrieved from S3.
+ *
+ * @return the input stream
+ * @throws FileSystemException if the input stream cannot be accessed
+ */
+ public InputStream getInputStream() throws FileSystemException {
+ try {
+ InputStream is = object.getDataInputStream();
+ if (null == is) {
+ object = service.getObject(bucket, object.getKey());
+ is = object.getDataInputStream();
+ }
+ return is;
+ } catch (S3ServiceException e) {
+ throw new FileSystemException(e);
+ }
+ }
+
+ // Utility methods
+
+ /**
+ * Make an absolute path out of an S3 (Jets3t path) which does not contain
+ * a slash. If the key is "SomeDirectory/afile.txt" it will become
+ * "/SomeDirectory/afile.txt". All files will be handled with their full
+ * key name so this cannot be confused with relative file names which are
+ * handled by commons-vfs.
+ *
+ * @param key the objects key
+ * @return the absolute path name
+ */
+ private String makeAbsolutePath(String key) {
+ return "/" + key;
+ }
+
+ /**
+ * Create an S3 path (the key) from a commons-vfs path. This simply
+ * strips the slash from the beginning if it exists. We assume that the
+ * path is always absolute, so a missing slash at the beginning is
+ * simply ignored.
+ *
+ * @param path the absolute file path (with or without heading slash)
+ * @return the S3 object key
+ */
+ private String makeS3Path(String path) {
+ if ("".equals(path)) {
+ return path;
+ } else {
+ return path.substring(1);
+ }
+ }
+}
blob - /dev/null
blob + 6594366a8e657a91f2c102756d9a35b42e138610 (mode 644)
--- /dev/null
+++ src/main/java/com/thinkberg/moxo/s3/S3VfsRoot.java
+/*
+ * 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.s3;
+
+import org.apache.commons.vfs.FileSystemException;
+
+/**
+ * Virtual Root of an S3 file system. Implement this interface to be able to get
+ * objects from Amazon S3. The root should be bound to a bucket that contains the
+ * virtual file system. All calls to S3VfsRoot#getObject should return a virtual
+ * object within this file system.
+ *
+ * @author Matthias L. Jugel
+ */
+public interface S3VfsRoot {
+ /**
+ * Get a file object relative to this S3 root. A roo
+ *
+ * @param path the path of the file (the absolute path)
+ * @return the virtual S3 file object
+ * @throws FileSystemException if the object cannot be accessed
+ */
+ public S3VfsObject getObject(String path) throws FileSystemException;
+}
blob - /dev/null
blob + 435dc12b13ce00f347f7278cb86e82f7d44dc975 (mode 644)
--- /dev/null
+++ src/main/java/com/thinkberg/moxo/s3/S3VfsRootImpl.java
+/*
+ * 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.s3;
+
+import org.apache.commons.vfs.FileSystemException;
+import org.jets3t.service.S3Service;
+import org.jets3t.service.model.S3Bucket;
+
+/**
+ * Virtual Root of an S3 file system using the Jets3t library. The root
+ * stores the service and bucket to create file objects found in the
+ * bucket.
+ *
+ * @author Matthias L. Jugel
+ */
+public class S3VfsRootImpl implements S3VfsRoot {
+ private final S3Service service;
+ private final S3Bucket bucket;
+
+ /**
+ * Create a new bucket file system root.
+ *
+ * @param service the S3 service to use
+ * @param bucket the S3 bucket this root is bound to
+ */
+ public S3VfsRootImpl(S3Service service, S3Bucket bucket) {
+ this.service = service;
+ this.bucket = bucket;
+ }
+
+ /**
+ * Create a new S3VfsObject using the given path. The object may not exist
+ * in the bucket and will then return an object that has an imaginary file type.
+ *
+ * @param path the absolute path to the file
+ * @return the virtual S3 object representing the file
+ * @throws FileSystemException if the file cannot be accessed
+ * @see com.thinkberg.moxo.s3.S3VfsObject
+ */
+ public S3VfsObject getObject(String path) throws FileSystemException {
+ return new S3VfsObjectImpl(service, bucket, path);
+ }
+}
blob - /dev/null
blob + ee85960287fcb8693e420e89092915792c5c13e3 (mode 644)
--- /dev/null
+++ src/main/java/com/thinkberg/moxo/servlet/MoxoS3WebdavServlet.java
+/*
+ * 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 + c955d451826291a42e1b6aac6197ae8efee01db4 (mode 644)
--- /dev/null
+++ src/main/java/com/thinkberg/moxo/vfs/DepthFileSelector.java
+/*
+ * 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 org.apache.commons.vfs.FileSelectInfo;
+import org.apache.commons.vfs.FileSelector;
+
+/**
+ * A file selector that operates depth of the directory structure and will
+ * select all files up to and including the depth given in the constructor.
+ *
+ * @author Matthias L. Jugel
+ * @version $Id$
+ */
+public class DepthFileSelector implements FileSelector {
+ private final int maxDepth;
+ private final int minDepth;
+
+ /**
+ * Create a file selector that will select ALL files.
+ */
+ public DepthFileSelector() {
+ this(0, Integer.MAX_VALUE);
+ }
+
+ /**
+ * Create a file selector that will select all files up to and including
+ * the directory depth.
+ *
+ * @param depth the maximum depth
+ */
+ public DepthFileSelector(int depth) {
+ this(0, depth);
+ }
+
+ @SuppressWarnings({"SameParameterValue"})
+ public DepthFileSelector(int min, int max) {
+ minDepth = min;
+ maxDepth = max;
+ }
+
+ public boolean includeFile(FileSelectInfo fileSelectInfo) throws Exception {
+ int depth = fileSelectInfo.getDepth();
+ return depth >= minDepth && depth <= maxDepth;
+ }
+
+ public boolean traverseDescendents(FileSelectInfo fileSelectInfo) throws Exception {
+ return fileSelectInfo.getDepth() < maxDepth;
+ }
+}
blob - /dev/null
blob + 5768fc6965485ff468abf9e84001279acbb70358 (mode 644)
--- /dev/null
+++ src/main/java/com/thinkberg/moxo/vfs/S3FileName.java
+/*
+ * 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 org.apache.commons.vfs.FileType;
+import org.apache.commons.vfs.provider.local.LocalFileName;
+
+/**
+ * @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 - /dev/null
blob + 9c5d272ffd3f18b369af876efa3fa7762060d730 (mode 644)
--- /dev/null
+++ src/main/java/com/thinkberg/moxo/vfs/S3FileNameParser.java
+/*
+ * 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 org.apache.commons.vfs.FileName;
+import org.apache.commons.vfs.FileSystemException;
+import org.apache.commons.vfs.FileType;
+import org.apache.commons.vfs.provider.AbstractFileNameParser;
+import org.apache.commons.vfs.provider.UriParser;
+import org.apache.commons.vfs.provider.VfsComponentContext;
+
+/**
+ * @author Matthias L. Jugel
+ */
+public class S3FileNameParser extends AbstractFileNameParser {
+ private static final S3FileNameParser instance = new S3FileNameParser();
+
+ public static S3FileNameParser getInstance() {
+ return instance;
+ }
+
+ private S3FileNameParser() {
+
+ }
+
+
+ public FileName parseUri(final VfsComponentContext context, final FileName base, final String filename) throws FileSystemException {
+ StringBuffer name = new StringBuffer();
+
+ String scheme = UriParser.extractScheme(filename, name);
+ UriParser.canonicalizePath(name, 0, name.length(), this);
+
+ UriParser.fixSeparators(name);
+
+ // Normalise the path
+ FileType fileType = UriParser.normalisePath(name);
+
+ // Extract the root prefix
+ final String bucketName = UriParser.extractFirstElement(name);
+
+
+ return new S3FileName(scheme, bucketName, name.toString(), fileType);
+ }
+
+}
blob - /dev/null
blob + 7e14e4183a36cc376545891551023afd1a7e67ba (mode 644)
--- /dev/null
+++ src/main/java/com/thinkberg/moxo/vfs/S3FileObject.java
+/*
+ * 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 com.thinkberg.moxo.s3.S3VfsObject;
+import org.apache.commons.vfs.FileName;
+import org.apache.commons.vfs.FileType;
+import org.apache.commons.vfs.provider.AbstractFileObject;
+
+import java.io.InputStream;
+
+/**
+ * A VFS wrapper for the S3 file object. All requests are proxied through to the actual
+ * data object that contains an implementation of the virtual S3 Object.
+ *
+ * @author Matthias L. Jugel
+ * @see com.thinkberg.moxo.s3.S3VfsObject
+ */
+public class S3FileObject extends AbstractFileObject {
+
+ private final S3VfsObject s3Object;
+
+ /**
+ * Create a new S3 file object with the given virtual S3 object as data backend.
+ *
+ * @param fileName the file name of the current file in the virtual filesystem
+ * @param fileSystem the filesystem used (@see S3FileSystem)
+ * @param s3Object the actual data object
+ */
+ @SuppressWarnings({"WeakerAccess"})
+ protected S3FileObject(FileName fileName, S3FileSystem fileSystem, S3VfsObject s3Object) {
+ super(fileName, fileSystem);
+ this.s3Object = s3Object;
+ }
+
+ protected FileType doGetType() throws Exception {
+ return s3Object.getType();
+ }
+
+ protected String[] doListChildren() throws Exception {
+ return s3Object.getChildren();
+ }
+
+ protected long doGetContentSize() throws Exception {
+ return s3Object.getContentLength();
+ }
+
+
+ protected long doGetLastModifiedTime() throws Exception {
+ return s3Object.getLastModified();
+ }
+
+ protected InputStream doGetInputStream() throws Exception {
+ return s3Object.getInputStream();
+ }
+
+}
blob - /dev/null
blob + c0fa0a50a3b224fdf9528bfdc917c8ad756523de (mode 644)
--- /dev/null
+++ src/main/java/com/thinkberg/moxo/vfs/S3FileProvider.java
+/*
+ * 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 com.thinkberg.moxo.s3.S3Connector;
+import com.thinkberg.moxo.s3.S3VfsRoot;
+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.provider.AbstractOriginatingFileProvider;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+
+/**
+ * @author Matthias L. Jugel
+ */
+public class S3FileProvider extends AbstractOriginatingFileProvider {
+
+ public final static Collection capabilities = Collections.unmodifiableCollection(Arrays.asList(
+/*
+ Capability.CREATE,
+ Capability.DELETE,
+ Capability.RENAME,
+*/
+Capability.GET_TYPE,
+Capability.GET_LAST_MODIFIED,
+/*
+ Capability.SET_LAST_MODIFIED_FILE,
+ Capability.SET_LAST_MODIFIED_FOLDER,
+*/
+Capability.LIST_CHILDREN,
+Capability.READ_CONTENT,
+Capability.URI/*,
+
+ Capability.WRITE_CONTENT,
+ Capability.APPEND_CONTENT,
+ Capability.RANDOM_ACCESS_READ,
+ Capability.RANDOM_ACCESS_WRITE
+*/
+ ));
+
+
+ public S3FileProvider() {
+ super();
+ setFileNameParser(S3FileNameParser.getInstance());
+ }
+
+ protected FileSystem doCreateFileSystem(FileName fileName, FileSystemOptions fileSystemOptions) throws FileSystemException {
+ S3FileName s3FileName = (S3FileName) fileName;
+ String s3BucketId = s3FileName.getRootFile();
+ S3VfsRoot s3VfsRoot = S3Connector.getInstance().getRoot(s3BucketId);
+ return new S3FileSystem(s3FileName, fileSystemOptions, s3VfsRoot);
+ }
+
+ public Collection getCapabilities() {
+ return capabilities;
+ }
+}
blob - /dev/null
blob + b93b1b2b2e973615eca2965d99c3ab752bfd73c3 (mode 644)
--- /dev/null
+++ src/main/java/com/thinkberg/moxo/vfs/S3FileSystem.java
+/*
+ * 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 com.thinkberg.moxo.s3.S3VfsRoot;
+import org.apache.commons.vfs.FileName;
+import org.apache.commons.vfs.FileObject;
+import org.apache.commons.vfs.FileSystem;
+import org.apache.commons.vfs.FileSystemOptions;
+import org.apache.commons.vfs.provider.AbstractFileSystem;
+
+import java.util.Collection;
+
+
+/**
+ * @author Matthias L. Jugel
+ */
+public class S3FileSystem extends AbstractFileSystem implements FileSystem {
+ private final S3VfsRoot s3VfsRoot;
+
+ @SuppressWarnings({"WeakerAccess"})
+ protected S3FileSystem(S3FileName fileName, FileSystemOptions fileSystemOptions, S3VfsRoot s3VfsRoot) {
+ super(fileName, null, fileSystemOptions);
+ this.s3VfsRoot = s3VfsRoot;
+ }
+
+ @SuppressWarnings({"unchecked"})
+ protected void addCapabilities(Collection caps) {
+ caps.addAll(S3FileProvider.capabilities);
+ }
+
+ protected FileObject createFile(FileName fileName) throws Exception {
+ return new S3FileObject(fileName, this, s3VfsRoot.getObject(fileName.getPathDecoded()));
+ }
+
+}
blob - /dev/null
blob + 686c14998f8bbad319b7f2e07f193ecb66f3cbb8 (mode 644)
--- /dev/null
+++ src/main/resources/META-INF/vfs-providers.xml
+<!--
+ ~ 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.
+ -->
+
+<providers>
+
+ <provider class-name="com.thinkberg.moxo.vfs.S3FileProvider">
+ <scheme name="s3"/>
+ <if-available class-name="org.jets3t.service.S3Service"/>
+ </provider>
+
+</providers>
blob - /dev/null
blob + ae39e881510681e8eb8b08d6c28510f4eec11b1d (mode 644)
--- /dev/null
+++ src/main/resources/jetty.xml
+<?xml version="1.0"?>
+<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://jetty.mortbay.org/configure.dtd">
+
+<!-- =============================================================== -->
+<!-- Configure the Jetty Server -->
+<!-- -->
+<!-- Documentation of this file format can be found at: -->
+<!-- http://docs.codehaus.org/display/JETTY/jetty.xml -->
+<!-- -->
+<!-- =============================================================== -->
+
+
+<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>
+
+ <!-- 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.
+ -->
+ <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>
+
+
+ <!-- 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>
+ -->
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <!-- 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>
+ </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>
blob - /dev/null
blob + 063bbb02000bd53c285e6e92b4a9669d148ed99d (mode 644)
--- /dev/null
+++ src/main/resources/moxo.template.properties
+#
+# 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.
+#
+
+#####################################
+# VFS provider application properties
+#
+# This file must be available on the
+# classpath when the VFS is used
+####################################
+
+# AWS Access Key (required)
+accesskey=<your AWS access key here>
+
+# AWS Secret Key (required)
+secretkey=<your AWS secret key here>
+
+# Access Control List setting to apply to uploads, must be one of: PRIVATE, PUBLIC_READ, PUBLIC_READ_WRITE
+# The ACL setting defaults to PRIVATE if this setting is missing.
+acl=PRIVATE
+
+# Password used when encrypting/decrypting files. Only required when the --crypto setting is used.
+password=<some encryption password here>
+
+# the bucket that contains the file system
+bucket=<the filesystem bucket>
\ No newline at end of file
blob - /dev/null
blob + d30eb9df05dee7f0339cc282835d00c601283bd2 (mode 644)
--- /dev/null
+++ src/test/java/com/thinkberg/moxo/MoxoTest.java
+package com.thinkberg.moxo;
+
+import com.thinkberg.moxo.dav.DavLockManagerTest;
+import com.thinkberg.moxo.dav.DavResourceTest;
+import com.thinkberg.moxo.s3.S3WrapperTest;
+import com.thinkberg.moxo.vfs.S3FileNameTest;
+import com.thinkberg.moxo.vfs.S3FileProviderTest;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.jets3t.service.Jets3tProperties;
+
+/**
+ * Unit tests for the Moxo S3 Amazon Proxy server.
+ *
+ * @author Matthias L. Jugel
+ */
+public class MoxoTest extends TestCase {
+ /**
+ * The complete Moxo Test suite.
+ *
+ * @param name name of the test case
+ */
+ public MoxoTest(String name) {
+ super(name);
+ }
+
+ /**
+ * Add all known tests to the suite.
+ *
+ * @return the suite of tests being tested
+ */
+ public static Test suite() {
+ TestSuite s = new TestSuite();
+
+ s.addTestSuite(DavResourceTest.class);
+ s.addTestSuite(DavLockManagerTest.class);
+
+ String propertiesFileName = System.getProperty("moxo.properties", "moxo.properties");
+ Jets3tProperties properties = Jets3tProperties.getInstance(propertiesFileName);
+
+ String bucketId = properties.getStringProperty("bucket", null);
+ if (null != bucketId) {
+ s.addTestSuite(S3WrapperTest.class);
+
+ s.addTestSuite(S3FileNameTest.class);
+ s.addTestSuite(S3FileProviderTest.class);
+ }
+
+ return s;
+ }
+
+
+}
blob - /dev/null
blob + 111a1dbd66bf028541af96c6cc04d9feef45c600 (mode 644)
--- /dev/null
+++ src/test/java/com/thinkberg/moxo/S3TestCase.java
+/*
+ * 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 - /dev/null
blob + 2fa2574ff79738bae97230feeef528a7ff9781d9 (mode 644)
--- /dev/null
+++ src/test/java/com/thinkberg/moxo/dav/DavLockManagerTest.java
+package com.thinkberg.moxo.dav;
+
+import com.thinkberg.moxo.dav.lock.Lock;
+import com.thinkberg.moxo.dav.lock.LockConflictException;
+import com.thinkberg.moxo.dav.lock.LockManager;
+
+/**
+ * @author Matthias L. Jugel
+ */
+public class DavLockManagerTest extends DavTestCase {
+ private final String OWNER_STR = "testowner";
+
+ public DavLockManagerTest() {
+ super();
+ }
+
+ public void testAcquireSharedFileLock() {
+ Lock sharedLock = new Lock(aFile, Lock.WRITE, Lock.SHARED, OWNER_STR, 0, 3600);
+ try {
+ LockManager.getInstance().acquireLock(sharedLock);
+ } catch (Exception e) {
+ assertNull(e.getMessage(), e);
+ }
+ }
+
+ public void testAcquireDoubleSharedFileLock() {
+ Lock sharedLock1 = new Lock(aFile, Lock.WRITE, Lock.SHARED, OWNER_STR, 0, 3600);
+ Lock sharedLock2 = new Lock(aFile, Lock.WRITE, Lock.SHARED, OWNER_STR + "1", 0, 3600);
+ try {
+ LockManager.getInstance().acquireLock(sharedLock1);
+ LockManager.getInstance().acquireLock(sharedLock2);
+ } catch (Exception e) {
+ assertNull(e.getMessage(), e);
+ }
+ }
+
+ public void testAcquireExclusiveLock() {
+ Lock sharedLock = new Lock(aFile, Lock.WRITE, Lock.SHARED, OWNER_STR, 0, 3600);
+ Lock exclusiveLock = new Lock(aFile, Lock.WRITE, Lock.EXCLUSIVE, OWNER_STR, 0, 3600);
+ try {
+ LockManager.getInstance().acquireLock(sharedLock);
+ LockManager.getInstance().acquireLock(exclusiveLock);
+ assertTrue("acquireLock() should fail", false);
+ } catch (Exception e) {
+ assertEquals(LockConflictException.class, e.getClass());
+ }
+ }
+}
blob - /dev/null
blob + 66f3e2ed8c1495e69dd6a684bf30ad748976367b (mode 644)
--- /dev/null
+++ src/test/java/com/thinkberg/moxo/dav/DavResourceTest.java
+package com.thinkberg.moxo.dav;
+
+import com.thinkberg.moxo.dav.data.DavCollection;
+import com.thinkberg.moxo.dav.data.DavResource;
+import org.apache.commons.vfs.FileSystemException;
+import org.dom4j.Element;
+
+import java.io.IOException;
+
+/**
+ * Test case for the DAV resource wrapper. Checks that resources are serialized
+ * correctly.
+ *
+ * @author Matthias L. Jugel
+ */
+public class DavResourceTest extends DavTestCase {
+ public void testFileCreationDateIsNull() throws FileSystemException {
+ Element root = serializeDavResource(aFile, DavResource.PROP_CREATION_DATE);
+ assertNull(selectExistingProperty(root, DavResource.PROP_CREATION_DATE));
+ }
+
+ public void testFileCreationDateIsMissing() throws IOException {
+ Element root = serializeDavResource(aFile, DavResource.PROP_CREATION_DATE);
+ assertEquals(DavResource.PROP_CREATION_DATE,
+ selectMissingPropertyName(root, DavResource.PROP_CREATION_DATE));
+ }
+
+ public void testFileDisplayNameWithValue() throws FileSystemException {
+ testPropertyValue(aFile, DavResource.PROP_DISPLAY_NAME, aFile.getName().getBaseName());
+ }
+
+ public void testFileDisplayNameWithoutValue() throws FileSystemException {
+ testPropertyNoValue(aFile, DavResource.PROP_DISPLAY_NAME);
+ }
+
+ public void testFileResourceTypeNotMissing() throws FileSystemException {
+ Element root = serializeDavResource(aFile, DavResource.PROP_RESOURCETYPE);
+ assertNull(selectMissingProperty(root, DavResource.PROP_RESOURCETYPE));
+ }
+
+ public void testDirectoryResourceTypeNotMissing() throws FileSystemException {
+ Element root = serializeDavResource(aDirectory, DavResource.PROP_RESOURCETYPE);
+ assertNull(selectMissingProperty(root, DavResource.PROP_RESOURCETYPE));
+ }
+
+ public void testFileResourceType() throws FileSystemException {
+ testPropertyNoValue(aFile, DavResource.PROP_RESOURCETYPE);
+ }
+
+ public void testDirectoryResourceType() throws FileSystemException {
+ Element root = serializeDavResource(aDirectory, DavResource.PROP_RESOURCETYPE);
+ assertNotNull(selectExistingProperty(root, DavResource.PROP_RESOURCETYPE).selectSingleNode(DavCollection.COLLECTION));
+ }
+}
blob - /dev/null
blob + 0a640610fa6e6e3de857f625d04981a4eaa3f1d3 (mode 644)
--- /dev/null
+++ src/test/java/com/thinkberg/moxo/dav/DavTestCase.java
+package com.thinkberg.moxo.dav;
+
+import com.thinkberg.moxo.dav.data.DavResource;
+import com.thinkberg.moxo.dav.data.DavResourceFactory;
+import junit.framework.TestCase;
+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.dom4j.DocumentHelper;
+import org.dom4j.Element;
+import org.dom4j.Node;
+
+import java.util.Arrays;
+
+/**
+ * Helper class for DAV tests.
+ *
+ * @author Matthias L. Jugel
+ */
+public class DavTestCase extends TestCase {
+ private static final String PROP_EXISTS = "propstat[status='HTTP/1.1 200 OK']/prop/";
+ private static final String PROP_MISSING = "propstat[status='HTTP/1.1 404 Not Found']/prop/";
+ private static final String EMPTY = "";
+
+ FileObject aFile;
+ FileObject aDirectory;
+
+
+ protected void setUp() throws Exception {
+ super.setUp();
+ FileSystemManager fsm = VFS.getManager();
+ FileObject fsRoot = fsm.createVirtualFileSystem(fsm.resolveFile("ram:/"));
+ aFile = fsRoot.resolveFile("/file.txt");
+ aFile.delete();
+ aFile.createFile();
+ aDirectory = fsRoot.resolveFile("/folder");
+ aDirectory.delete();
+ 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));
+ }
+
+ protected void testPropertyNoValue(FileObject object, String propertyName) throws FileSystemException {
+ Element root = serializeDavResource(object, propertyName, true);
+ assertEquals(EMPTY, selectExistingPropertyValue(root, propertyName));
+ }
+
+ Node selectExistingProperty(Element root, String propertyName) {
+ return root.selectSingleNode(PROP_EXISTS + propertyName);
+ }
+
+ Node selectMissingProperty(Element root, String propertyName) {
+ return root.selectSingleNode(PROP_MISSING + propertyName);
+ }
+
+ @SuppressWarnings({"SameParameterValue"})
+ String selectMissingPropertyName(Element root, String propertyName) {
+ return selectMissingProperty(root, propertyName).getName();
+ }
+
+ private String selectExistingPropertyValue(Element root, String propertyName) {
+ return selectExistingProperty(root, propertyName).getText();
+ }
+
+ Element serializeDavResource(FileObject object, String propertyName) throws FileSystemException {
+ return serializeDavResource(object, propertyName, false);
+ }
+
+ private Element serializeDavResource(FileObject object, String propertyName, boolean ignoreValues) throws FileSystemException {
+ Element root = DocumentHelper.createElement("root");
+ DavResourceFactory factory = DavResourceFactory.getInstance();
+ DavResource davResource = factory.getDavResource(object);
+ davResource.setIgnoreValues(ignoreValues);
+ davResource.serializeToXml(root, Arrays.asList(propertyName));
+ return root;
+ }
+
+}
blob - /dev/null
blob + 4daf32ab4820a8f9afbb63a5bb033383d7855fbe (mode 644)
--- /dev/null
+++ src/test/java/com/thinkberg/moxo/s3/S3WrapperTest.java
+/*
+ * 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.s3;
+
+import junit.framework.TestCase;
+import org.apache.commons.vfs.FileSystemException;
+import org.apache.commons.vfs.FileType;
+import org.jets3t.service.Jets3tProperties;
+
+import java.io.InputStream;
+
+import com.thinkberg.moxo.S3TestCase;
+
+/**
+ * @author Matthias L. Jugel
+ */
+public class S3WrapperTest extends S3TestCase {
+ public void testCreateConnection() throws FileSystemException {
+ assertNotNull(S3Connector.getInstance());
+ }
+
+ public void testGetS3BucketRoot() throws FileSystemException {
+ assertNotNull(S3Connector.getInstance().getRoot(BUCKETID));
+ }
+
+ public void testGetS3BucketRootMissing() throws FileSystemException {
+ try {
+ assertNull(S3Connector.getInstance().getRoot(BUCKETID+".NOTEXISTING"));
+ } catch (FileSystemException e) {
+ assertNotNull(e);
+ }
+ }
+
+ public void testGetRootFolder() throws FileSystemException {
+ S3VfsRoot root = S3Connector.getInstance().getRoot(BUCKETID);
+ S3VfsObject slashObject = root.getObject("/");
+ assertEquals("/", slashObject.getName());
+ }
+
+ public void testRootFolderTypeIsCorrect() throws FileSystemException {
+ S3VfsRoot root = S3Connector.getInstance().getRoot(BUCKETID);
+ S3VfsObject slashObject = root.getObject("/");
+ assertEquals(FileType.FOLDER, slashObject.getType());
+ }
+
+ public void testRootFolderListing() throws FileSystemException {
+ S3VfsRoot root = S3Connector.getInstance().getRoot(BUCKETID);
+ S3VfsObject slashObject = root.getObject("/");
+ assertEquals(1, slashObject.getChildren().length);
+ }
+
+ public void testFolderTypeIsCorrect() throws FileSystemException {
+ S3VfsRoot root = S3Connector.getInstance().getRoot(BUCKETID);
+ S3VfsObject folderObject = root.getObject("/Sites");
+ assertEquals(FileType.FOLDER, folderObject.getType());
+ }
+
+ public void testFileTypeIsCorrect() throws FileSystemException {
+ S3VfsRoot root = S3Connector.getInstance().getRoot(BUCKETID);
+ S3VfsObject fileObject = root.getObject("/Sites/Sites/index.html");
+ assertEquals(FileType.FILE, fileObject.getType());
+ }
+
+ public void testFolderSize() throws FileSystemException {
+ S3VfsRoot root = S3Connector.getInstance().getRoot(BUCKETID);
+ S3VfsObject folderObject = root.getObject("/Sites/Sites/images");
+ assertEquals(3, folderObject.getChildren().length);
+ }
+
+ public void testGetFile() throws FileSystemException {
+ S3VfsRoot root = S3Connector.getInstance().getRoot(BUCKETID);
+ S3VfsObject fileObject = root.getObject("/Sites/Sites/images/macosxlogo.gif");
+ assertNotNull(fileObject.getInputStream());
+ }
+
+ public void testGetMissingFileIsImaginary() throws FileSystemException {
+ S3VfsRoot root = S3Connector.getInstance().getRoot(BUCKETID);
+ S3VfsObject fileObject = root.getObject("/notexisting.txt");
+ assertEquals(FileType.IMAGINARY, fileObject.getType());
+ }
+}
blob - /dev/null
blob + 91cc77a793bd5a99291d9625ec74622b90b9102d (mode 644)
--- /dev/null
+++ src/test/java/com/thinkberg/moxo/vfs/S3FileNameTest.java
+/*
+ * 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.apache.commons.vfs.FileSystemException;
+import org.apache.commons.vfs.FileName;
+import org.jets3t.service.Jets3tProperties;
+import com.thinkberg.moxo.S3TestCase;
+
+/**
+ * @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());
+ }
+
+ public void testGetRootFolderFromUri() throws FileSystemException {
+ String path = "/myfolder";
+ String uri = ROOT + path;
+ FileName fileName = S3FileNameParser.getInstance().parseUri(null, null, uri);
+ assertEquals(path, fileName.getPath());
+ }
+}
blob - /dev/null
blob + 6fddf9948fa98da70f3c6f8d3e6268191d6ee83d (mode 644)
--- /dev/null
+++ src/test/java/com/thinkberg/moxo/vfs/S3FileProviderTest.java
+/*
+ * 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 org.apache.commons.vfs.FileObject;
+import org.apache.commons.vfs.FileSystemException;
+import org.apache.commons.vfs.FileSystemOptions;
+import org.apache.commons.vfs.FileSystemManager;
+import org.apache.commons.vfs.VFS;
+import org.apache.commons.vfs.FileType;
+import org.apache.commons.vfs.impl.DefaultFileSystemManager;
+import org.jets3t.service.Jets3tProperties;
+import junit.framework.TestCase;
+import com.thinkberg.moxo.S3TestCase;
+
+/**
+ * @author Matthias L. Jugel
+ */
+public class S3FileProviderTest extends S3TestCase {
+ public void testDoCreateFileSystem() throws FileSystemException {
+ FileObject object = VFS.getManager().resolveFile(ROOT);
+ assertEquals(BUCKETID, ((S3FileName)object.getName()).getRootFile());
+ }
+
+ 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");
+ assertEquals(FileType.FOLDER, object.getType());
+ }
+
+ public void testGetDirectoryListing() throws FileSystemException {
+ FileObject object = VFS.getManager().resolveFile(ROOT + "/Sites/Sites/images");
+ FileObject[] files = object.findFiles(new DepthFileSelector(1));
+ assertEquals(4, files.length);
+ }
+
+ public void testMissingFile() throws FileSystemException {
+ FileObject object = VFS.getManager().resolveFile(ROOT + "/nonexisting.txt");
+ assertFalse(object.exists());
+ }
+}