commit c4610f866124bd70c2c5eee691a35ecd94ab2487 from: leo date: Mon Jan 26 20:37:21 2009 UTC refactored to separate dav and vfs into modules 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 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 @@ + + + + + + + + + + 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; + } + } +}