Commit Diff


commit - 1939c62d29abc7a7d42c831f096b5cc27f4c1182
commit + c4610f866124bd70c2c5eee691a35ecd94ab2487
blob - /dev/null
blob + a115da635abf2030cd9f127207a5e930c7c9e970 (mode 644)
--- /dev/null
+++ modules/vfs.s3/src/main/java/com/thinkberg/vfs/s3/S3FileName.java
@@ -0,0 +1,29 @@
+/*
+ * 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.vfs.s3;
+
+import org.apache.commons.vfs.FileType;
+import org.apache.commons.vfs.provider.local.LocalFileName;
+
+/**
+ * @author Matthias L. Jugel
+ */
+public class S3FileName extends LocalFileName {
+  protected S3FileName(final String scheme, final String rootFile, final String path, final FileType type) {
+    super(scheme, rootFile, path, type);
+  }
+}
blob - /dev/null
blob + 98a261c899effacd3c1f562cd3db06c5357b9f43 (mode 644)
--- /dev/null
+++ modules/vfs.s3/src/main/java/com/thinkberg/vfs/s3/S3FileNameParser.java
@@ -0,0 +1,58 @@
+/*
+ * 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.vfs.s3;
+
+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 + 9c9ec0692b4d9c3723a8db92f77b2e782ff4747f (mode 644)
--- /dev/null
+++ modules/vfs.s3/src/main/java/com/thinkberg/vfs/s3/S3FileProvider.java
@@ -0,0 +1,108 @@
+/*
+ * 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.vfs.s3;
+
+import com.thinkberg.vfs.s3.jets3t.Jets3tFileSystem;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.vfs.*;
+import org.apache.commons.vfs.provider.AbstractOriginatingFileProvider;
+import org.apache.commons.vfs.util.UserAuthenticatorUtils;
+import org.jets3t.service.S3Service;
+import org.jets3t.service.S3ServiceException;
+import org.jets3t.service.impl.rest.httpclient.RestS3Service;
+import org.jets3t.service.security.AWSCredentials;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+
+/**
+ * An S3 file provider. Create an S3 file system out of an S3 file name.
+ * Also defines the capabilities of the file system.
+ *
+ * @author Matthias L. Jugel
+ */
+public class S3FileProvider extends AbstractOriginatingFileProvider {
+  public final static Collection<Capability> 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*/
+
+  ));
+
+  private static final UserAuthenticationData.Type[] AUTHENTICATOR_TYPES = new UserAuthenticationData.Type[]{
+          UserAuthenticationData.USERNAME,
+          UserAuthenticationData.PASSWORD
+  };
+
+  private S3Service service;
+  private static final Log LOG = LogFactory.getLog(S3FileProvider.class);
+
+  public S3FileProvider() {
+    super();
+    setFileNameParser(S3FileNameParser.getInstance());
+  }
+
+  /**
+   * Create a file system with the S3 root provided.
+   *
+   * @param fileName          the S3 file name that defines the root (bucket)
+   * @param fileSystemOptions file system options
+   * @return an S3 file system
+   * @throws FileSystemException if te file system cannot be created
+   */
+  protected FileSystem doCreateFileSystem(FileName fileName, FileSystemOptions fileSystemOptions) throws FileSystemException {
+    LOG.debug(String.format("creating new file system '%s'", fileName));
+    if (null == service) {
+      LOG.debug("creating new S3 service");
+      UserAuthenticationData authenticationInfo = UserAuthenticatorUtils.authenticate(fileSystemOptions, AUTHENTICATOR_TYPES);
+      String accessKey = UserAuthenticatorUtils.toString(UserAuthenticatorUtils.getData(authenticationInfo, UserAuthenticationData.USERNAME, null));
+      String secretKey = UserAuthenticatorUtils.toString(UserAuthenticatorUtils.getData(authenticationInfo, UserAuthenticationData.PASSWORD, null));
+
+      try {
+        service = new RestS3Service(new AWSCredentials(accessKey, secretKey));
+      } catch (S3ServiceException e) {
+        throw new FileSystemException("Amazon S3 service initialization failed", e);
+      } finally {
+        authenticationInfo.cleanup();
+      }
+    }
+
+    return new Jets3tFileSystem(service, (S3FileName) fileName, fileSystemOptions);
+  }
+
+  /**
+   * Get the capabilities of the file system provider.
+   *
+   * @return the file system capabilities
+   */
+  public Collection getCapabilities() {
+    return capabilities;
+  }
+}
blob - /dev/null
blob + f63cf8f032a072313edd39e344a09e9259c6f63b (mode 644)
--- /dev/null
+++ modules/vfs.s3/src/main/resources/META-INF/vfs-providers.xml
@@ -0,0 +1,24 @@
+<!--
+  ~ 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.vfs.s3.S3FileProvider">
+        <scheme name="s3"/>
+        <if-available class-name="org.jets3t.service.S3Service"/>
+    </provider>
+
+</providers>
blob - /dev/null
blob + 675245da96c776ed242485906769d4017f22d830 (mode 644)
--- /dev/null
+++ modules/vfs.s3/src/test/java/com/thinkberg/vfs/s3/S3TestCase.java
@@ -0,0 +1,64 @@
+/*
+ * 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.vfs.s3;
+
+import com.thinkberg.vfs.s3.tests.S3FileProviderTest;
+import junit.framework.TestCase;
+import org.apache.commons.vfs.FileObject;
+import org.apache.commons.vfs.FileSystemOptions;
+import org.apache.commons.vfs.VFS;
+import org.apache.commons.vfs.auth.StaticUserAuthenticator;
+import org.apache.commons.vfs.impl.DefaultFileSystemConfigBuilder;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Properties;
+import java.util.Random;
+
+/**
+ * @author Matthias L. Jugel
+ */
+public class S3TestCase extends TestCase {
+  protected final static String BUCKETID;
+  protected static String FSURI;
+  protected static FileObject ROOT;
+  protected static FileSystemOptions OPTIONS;
+
+
+  static {
+    BUCKETID = "MOXOTEST" + String.format("%X", new Random(System.currentTimeMillis()).nextLong());
+    Properties userConfig = new Properties();
+
+    InputStream propertyResource = S3FileProviderTest.class.getResourceAsStream("/s3.auth.properties");
+    assertNotNull(propertyResource);
+
+    try {
+      userConfig.load(propertyResource);
+      StaticUserAuthenticator userAuthenticator =
+              new StaticUserAuthenticator("",
+                                          userConfig.getProperty("s3.access.key", ""),
+                                          userConfig.getProperty("s3.secret.key", ""));
+      OPTIONS = new FileSystemOptions();
+      FSURI = String.format("s3://%s/", BUCKETID);
+
+      DefaultFileSystemConfigBuilder.getInstance().setUserAuthenticator(OPTIONS, userAuthenticator);
+      ROOT = VFS.getManager().resolveFile(FSURI, OPTIONS);
+    } catch (IOException e) {
+      assertTrue("initialization failed", false);
+    }
+  }
+}
blob - /dev/null
blob + 2f17587d2386da0b3978c7478970da3626c00ca6 (mode 644)
--- /dev/null
+++ modules/vfs.s3/src/test/java/com/thinkberg/vfs/s3/tests/S3FileNameTest.java
@@ -0,0 +1,45 @@
+/*
+ * 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.vfs.s3.tests;
+
+import com.thinkberg.vfs.s3.S3FileName;
+import com.thinkberg.vfs.s3.S3FileNameParser;
+import com.thinkberg.vfs.s3.S3TestCase;
+import junit.framework.Assert;
+import org.apache.commons.vfs.FileName;
+import org.apache.commons.vfs.FileSystemException;
+
+import java.net.URI;
+
+/**
+ * @author Matthias L. Jugel
+ */
+public class S3FileNameTest extends S3TestCase {
+  public void testGetBucketFromUri() throws FileSystemException {
+    String uriString = ROOT + "/junk.txt";
+    String bucketId = URI.create(uriString).getHost();
+    FileName fileName = S3FileNameParser.getInstance().parseUri(null, null, uriString);
+    Assert.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 + 4ab104f040636ab7b267aed201ad98af0049f444 (mode 644)
--- /dev/null
+++ modules/vfs.s3/src/test/java/com/thinkberg/vfs/s3/tests/S3FileProviderTest.java
@@ -0,0 +1,200 @@
+/*
+ * 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.vfs.s3.tests;
+
+import com.thinkberg.vfs.s3.S3FileName;
+import com.thinkberg.vfs.s3.S3TestCase;
+import com.thinkberg.vfs.s3.jets3t.Jets3tFileSystem;
+import junit.framework.Assert;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.vfs.*;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * @author Matthias L. Jugel
+ */
+public class S3FileProviderTest extends S3TestCase {
+  private static final FileSelector ALL_FILE_SELECTOR = new FileSelector() {
+
+    public boolean includeFile(FileSelectInfo fileInfo) throws Exception {
+      return true;
+    }
+
+    public boolean traverseDescendents(FileSelectInfo fileInfo) throws Exception {
+      return true;
+    }
+  };
+  private static final String FOLDER = "/directory";
+  private static final String FILE = FOLDER + "/newfile.txt";
+
+  static {
+    LogFactory.getLog(S3FileProviderTest.class).debug("initializing ...");
+    try {
+      ROOT.delete(ALL_FILE_SELECTOR);
+    } catch (FileSystemException e) {
+      // just delete, ignore the rest
+    }
+  }
+
+  public void testDoCreateFileSystem() throws FileSystemException {
+    FileObject object = ROOT.resolveFile("/");
+    Assert.assertEquals(BUCKETID, ((S3FileName) object.getName()).getRootFile());
+  }
+
+  public void testFileSystemIsEmpty() throws FileSystemException {
+    FileObject object = ROOT.resolveFile("/");
+    assertEquals(1, object.findFiles(ALL_FILE_SELECTOR).length);
+  }
+
+  public void testRootDirectoryIsFolder() throws FileSystemException {
+    FileObject object = ROOT.resolveFile("/");
+    assertEquals(FileType.FOLDER, object.getType());
+  }
+
+  public void testCreateFolder() throws FileSystemException {
+    FileObject object = ROOT.resolveFile(FOLDER);
+    assertFalse(object.exists());
+    object.createFolder();
+    assertTrue(object.exists());
+    assertEquals(FileType.FOLDER, object.getType());
+  }
+
+  public void testGetFolder() throws FileSystemException {
+    FileObject object = ROOT.resolveFile(FOLDER);
+    assertTrue(object.exists());
+    assertEquals(FileType.FOLDER, object.getType());
+  }
+
+  public void testCreateFile() throws IOException {
+    FileObject object = ROOT.resolveFile(FILE);
+    assertFalse(object.exists());
+    OutputStream os = object.getContent().getOutputStream();
+    os.write(0xfc);
+    os.close();
+    assertTrue(object.exists());
+    assertEquals(FileType.FILE, object.getType());
+  }
+
+  public void testCreateEmptyFile() throws IOException {
+    FileObject object = ROOT.resolveFile(FILE + ".empty");
+    assertFalse(object.exists());
+    object.createFile();
+    assertTrue(object.exists());
+    assertEquals(FileType.FILE, object.getType());
+  }
+
+  public void testFileHasLastModifiedTimestamp() throws FileSystemException {
+    FileObject object = ROOT.resolveFile(FILE);
+    object.getContent().getLastModifiedTime();
+  }
+
+  public void testGetFolderListing() throws FileSystemException {
+    FileObject object = ROOT.resolveFile(FOLDER);
+    FileObject[] files = object.findFiles(new DepthFileSelector(1));
+    assertEquals(3, files.length);
+  }
+
+  public void testMissingFile() throws FileSystemException {
+    FileObject object = ROOT.resolveFile("/nonexisting.txt");
+    assertFalse(object.exists());
+  }
+
+  public void testGetLastModifiedTimeFolder() throws FileSystemException {
+    FileObject object = ROOT.resolveFile(FOLDER);
+    object.getContent().getLastModifiedTime();
+  }
+
+  public void testGetLastModifiedTimeFile() throws FileSystemException {
+    FileObject object = ROOT.resolveFile(FILE);
+    object.getContent().getLastModifiedTime();
+  }
+
+  public void testDeleteFile() throws FileSystemException {
+    FileObject object = ROOT.resolveFile(FILE);
+    object.delete();
+    assertFalse(object.exists());
+  }
+
+  public void testDeleteFolder() throws FileSystemException {
+    FileObject object = ROOT.resolveFile(FOLDER);
+    object.delete(ALL_FILE_SELECTOR);
+    assertFalse(object.exists());
+  }
+
+  public void testCopyFolder() throws FileSystemException {
+    FileObject origFolder = ROOT.resolveFile(FOLDER);
+    origFolder.createFolder();
+
+    origFolder.resolveFile("file.0").createFile();
+    origFolder.resolveFile("file.1").createFile();
+    origFolder.resolveFile("file.2").createFile();
+
+    FileObject[] origFiles = origFolder.findFiles(new DepthFileSelector(1));
+    assertEquals(4, origFiles.length);
+
+    FileObject destFolder = ROOT.resolveFile(FOLDER + "_dest");
+    assertFalse(destFolder.exists());
+    destFolder.copyFrom(origFolder, new DepthFileSelector(1));
+    assertTrue(destFolder.exists());
+
+    FileObject[] destFiles = destFolder.findFiles(new DepthFileSelector(1));
+    assertEquals(4, destFiles.length);
+
+    for (int i = 0; i < origFiles.length; i++) {
+      assertEquals(origFiles[i].getName().getRelativeName(origFolder.getName()),
+                   destFiles[i].getName().getRelativeName(destFolder.getName()));
+    }
+
+    origFolder.delete(ALL_FILE_SELECTOR);
+    destFolder.delete(ALL_FILE_SELECTOR);
+
+    assertFalse(origFolder.exists());
+    assertFalse(destFolder.exists());
+  }
+
+//  public void testMoveFolder() throws FileSystemException {
+//
+//  }
+
+  public void testCloseFileSystem() throws FileSystemException {
+    VFS.getManager().closeFileSystem(ROOT.getFileSystem());
+  }
+
+  public void testDestroyFileSystem() throws FileSystemException {
+    final FileSystem fileSystem = ROOT.getFileSystem();
+    assertTrue(fileSystem instanceof Jets3tFileSystem);
+    ((Jets3tFileSystem) fileSystem).destroyFileSystem();
+  }
+
+  private class DepthFileSelector implements FileSelector {
+    int depth = 0;
+
+    public DepthFileSelector(int depth) {
+      this.depth = depth;
+    }
+
+    public boolean includeFile(FileSelectInfo fileInfo) throws Exception {
+      return fileInfo.getDepth() <= depth;
+    }
+
+    public boolean traverseDescendents(FileSelectInfo fileInfo) throws Exception {
+      return fileInfo.getDepth() < depth;
+    }
+  }
+}