/*****************************************************************************
* Copyright (c) 2008 g-Eclipse Consortium
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Initial development of the original code was made for the
* g-Eclipse project founded by European Union
* project number: FP6-IST-034327 http://www.geclipse.eu/
*
* Contributors:
* Mathias Stuempert - initial API and implementation
* Moritz Post - added getObjectDetails(), refined some details
*****************************************************************************/
package eu.geclipse.aws.s3.internal.fileSystem;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.net.URI;
import java.net.URISyntaxException;
import org.eclipse.core.filesystem.IFileInfo;
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.filesystem.provider.FileInfo;
import org.eclipse.core.filesystem.provider.FileStore;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.jets3t.service.S3Service;
import org.jets3t.service.S3ServiceException;
import org.jets3t.service.model.S3Bucket;
import org.jets3t.service.model.S3Object;
import eu.geclipse.aws.s3.IS3Constants;
import eu.geclipse.aws.s3.IS3Problems;
import eu.geclipse.aws.s3.internal.Activator;
import eu.geclipse.aws.s3.internal.S3ServiceRegistry;
import eu.geclipse.core.reporting.ProblemException;
/**
* {@link IFileStore} implementation for the amazon S3 service.
*/
public class S3FileStore extends FileStore implements IFileStore {
/**
* The parent node of this store. <code>null</code> for root stores.
*/
private S3FileStore storeParent;
/**
* The name of this store.
*/
private String storeName;
/**
* This store's file info or <code>null</code> if not yet available.
*/
private FileInfo storeInfo;
/**
* Create a root node for the account with the specified access key ID.
*
* @param name The name of the bucket.
*/
public S3FileStore( final String name ) {
this( null, name );
}
/**
* Create a child node with the specified name.
*
* @param parent The parent node of this node.
* @param name The name of this node.
*/
public S3FileStore( final S3FileStore parent, final String name ) {
this.storeParent = parent;
this.storeName = name;
}
@Override
public String[] childNames( final int options, final IProgressMonitor monitor )
throws CoreException
{
String[] result = new String[ 0 ];
S3Service service = getService();
if( service != null ) {
if( isRoot() ) {
result = S3FileStore.childNamesFromRoot( service );
} else if( isBucket() ) {
result = S3FileStore.childNamesFromBucket( service, getName() );
}
}
return result;
}
@Override
public void delete( final int options, final IProgressMonitor monitor )
throws CoreException
{
try {
if( isBucket() ) {
S3Service service = getService();
S3Bucket bucket = getBucket();
service.deleteBucket( bucket );
} else if( isObject() ) {
S3Service service = getService();
S3Bucket bucket = getBucket();
service.deleteObject( bucket, getName() );
}
internalFetchInfo().setExists( false );
} catch( S3ServiceException s3Exc ) {
throw new ProblemException( IS3Problems.S3_DELETE_FAILED,
s3Exc,
Activator.PLUGIN_ID );
}
}
@Override
public IFileInfo fetchInfo( final int options, final IProgressMonitor monitor )
throws CoreException
{
return internalFetchInfo();
}
@Override
public IFileStore getChild( final String name ) {
return new S3FileStore( this, name );
}
@Override
public String getName() {
return this.storeName;
}
@Override
public IFileStore getParent() {
return this.storeParent;
}
@Override
public IFileStore mkdir( final int options, final IProgressMonitor monitor )
throws CoreException
{
if( !isBucket() ) {
throw new ProblemException( IS3Problems.S3_BUCKET_IN_BUCKET_FAILED,
Activator.PLUGIN_ID );
}
S3Service service = getService();
try {
service.createBucket( getName() );
this.storeInfo = null;
} catch( S3ServiceException s3Exc ) {
ProblemException pExc = new ProblemException( IS3Problems.S3_BUCKET_CREATION_FAILED,
s3Exc,
Activator.PLUGIN_ID );
throw pExc;
}
return this;
}
@Override
public InputStream openInputStream( final int options,
final IProgressMonitor monitor )
throws CoreException
{
InputStream result = null;
S3Object object = getObject();
if( object != null ) {
try {
result = object.getDataInputStream();
} catch( S3ServiceException s3Exc ) {
throw new ProblemException( IS3Problems.S3_INPUT_FAILED,
s3Exc,
Activator.PLUGIN_ID );
}
}
internalFetchInfo().setExists( true );
return result;
}
@Override
public OutputStream openOutputStream( final int options,
final IProgressMonitor monitor )
throws CoreException
{
PipedOutputStream result = null;
if( isObject() ) {
try {
final S3Service service = getService();
final S3Bucket bucket = getBucket();
result = new PipedOutputStream();
final PipedOutputStream pos = result;
final PipedInputStream pis = new PipedInputStream( result );
final S3Object object = new S3Object( bucket, getName() );
object.setDataInputStream( pis );
Thread worker = new Thread( new Runnable() {
public void run() {
try {
service.putObject( bucket, object );
pos.close();
pis.close();
} catch( S3ServiceException s3Exc ) {
try {
internalFetchInfo().setExists( false );
} catch( ProblemException pExc ) {
Activator.log( pExc );
}
Activator.log( s3Exc );
} catch( IOException ioExc ) {
Activator.log( ioExc );
}
}
} );
worker.start();
} catch( IOException ioExc ) {
throw new ProblemException( IS3Problems.S3_OUTPUT_FAILED,
ioExc,
Activator.PLUGIN_ID );
}
}
return result;
}
@Override
public URI toURI() {
String authority = getName();
String path = null;
IFileStore par = getParent();
if( par != null ) {
URI parURI = par.toURI();
authority = parURI.getAuthority();
path = parURI.getPath();
if( path == null || path.length() == 0 ) {
path = IS3Constants.S3_PATH_SEPARATOR + getName();
} else {
path += IS3Constants.S3_PATH_SEPARATOR + getName();
}
} else {
authority = getAccessKeyID();
}
URI result = null;
try {
result = new URI( IS3Constants.S3_SCHEME, authority, path, null, null );
} catch( URISyntaxException uriExc ) {
Activator.log( uriExc );
}
return result;
}
/**
* Fetch this node's info.
*
* @throws ProblemException If the info retrieval from the service failed.
*/
protected FileInfo internalFetchInfo() throws ProblemException {
if( this.storeInfo == null ) {
this.storeInfo = new FileInfo();
this.storeInfo.setName( getName() );
this.storeInfo.setDirectory( isDirectory() );
this.storeInfo.setExists( false );
if( isRoot() ) {
this.storeInfo.setExists( false );
} else if( isBucket() ) {
try {
S3Service service = getService();
boolean exists = service != null
&& service.isBucketAccessible( getName() );
this.storeInfo.setExists( exists );
this.storeInfo.setDirectory( exists );
} catch( S3ServiceException s3Exc ) {
// Just ignore and leave exists to be false
}
} else if( isObject() ) {
S3Object object = getObjectDetails();
this.storeInfo.setExists( object != null );
if( object != null ) {
this.storeInfo.setLastModified( object.getLastModifiedDate()
.getTime() );
this.storeInfo.setLength( object.getContentLength() );
}
}
}
return this.storeInfo;
}
/**
* Determine if this store corresponds to a S3 bucket, i.e. a directory.
*
* @return True if this store is a bucket.
*/
private boolean isBucket() {
return getParent() != null && getParent().getParent() == null;
}
/**
* Determine if this store corresponds to a S3 object, i.e. a file.
*
* @return True if this store is an object.
*/
private boolean isObject() {
return !isRoot() && !isBucket();
}
/**
* Determine if this store corresponds to a S3 account.
*
* @return True if this store is an account.
*/
private boolean isRoot() {
return getParent() == null;
}
/**
* Get the access key ID of this node.
*
* @return This node's access key, i.e. the access key of the root node.
*/
private String getAccessKeyID() {
return isRoot()
? this.storeName
: ( ( S3FileStore )getParent() ).getAccessKeyID();
}
/**
* Get the bucket corresponding to this node or the underlying node.
*
* @return The bucket or <code>null<code> if this is a root node.
*/
private S3Bucket getBucket() {
S3Bucket result = null;
if( isBucket() ) {
result = new S3Bucket( getName() );
} else if( isObject() ) {
result = ( ( S3FileStore )getParent() ).getBucket();
}
return result;
}
/**
* Get the objects details and metadata corresponding to this node.
*
* @return The object or <code>null</code> if this node represents a root or a
* bucket.
* @throws ProblemException If the object could not be retrieved.
*/
private S3Object getObjectDetails() throws ProblemException {
S3Object result = null;
if( isObject() ) {
S3Service service = getService();
S3Bucket bucket = getBucket();
try {
result = service.getObjectDetails( bucket, getName() );
} catch( S3ServiceException s3Exc ) {
throw new ProblemException( IS3Problems.S3_OBJECT_LOAD_FAILED,
s3Exc,
Activator.PLUGIN_ID );
}
}
return result;
}
/**
* Get the object corresponding to this node.
*
* @return The object or <code>null</code> if this node represents a root or a
* bucket.
* @throws ProblemException If the object could not be retrieved.
*/
private S3Object getObject() throws ProblemException {
S3Object result = null;
if( isObject() ) {
S3Service service = getService();
S3Bucket bucket = getBucket();
try {
result = service.getObject( bucket, getName() );
} catch( S3ServiceException s3Exc ) {
throw new ProblemException( IS3Problems.S3_OBJECT_LOAD_FAILED,
s3Exc,
Activator.PLUGIN_ID );
}
}
return result;
}
/**
* Get the service that is used to perform operations on this node. If the
* service is not yet created it will be created by this method and cached for
* later use.
*
* @return This node's service.
* @throws ProblemException If the service was not already in the cache and
* its creation failed.
*/
private S3Service getService() throws ProblemException {
S3Service service = S3ServiceRegistry.getRegistry()
.getService( getAccessKeyID() );
return service;
}
/**
* Determines if this node may be seen as a directory.
*
* @return True if this node is either a root or a bucket.
*/
private boolean isDirectory() {
return !isObject();
}
/**
* Get the names of the buckets found for the specified service.
*
* @param service The service to be queried.
* @return The names of all buckets contained in the specified service.
* @throws ProblemException If an error occurred during child retrieval.
*/
private static String[] childNamesFromRoot( final S3Service service )
throws ProblemException
{
String[] result = new String[ 0 ];
try {
S3Bucket[] buckets = service.listAllBuckets();
if( buckets != null ) {
result = new String[ buckets.length ];
for( int i = 0; i < buckets.length; i++ ) {
result[ i ] = buckets[ i ].getName();
}
}
} catch( S3ServiceException s3sExc ) {
throw new ProblemException( IS3Problems.S3_LIST_FAILED,
s3sExc,
Activator.PLUGIN_ID );
}
return result;
}
/**
* Get the names of all objects contained in the specified bucket using the
* specified service.
*
* @param service The service to be used.
* @param bucketName The name of the bucket to list.
* @return The names of all children found in the specified bucket.
* @throws ProblemException If an error occurred during child retrieval.
*/
private static String[] childNamesFromBucket( final S3Service service,
final String bucketName )
throws ProblemException
{
String[] result = new String[ 0 ];
try {
S3Bucket bucket = new S3Bucket( bucketName );
S3Object[] objects = service.listObjects( bucket );
if( objects != null ) {
result = new String[ objects.length ];
for( int i = 0; i < objects.length; i++ ) {
result[ i ] = objects[ i ].getKey();
}
}
} catch( S3ServiceException s3sExc ) {
throw new ProblemException( IS3Problems.S3_LIST_FAILED,
s3sExc,
Activator.PLUGIN_ID );
}
return result;
}
}