Issue 80: public acl for atmos

This commit is contained in:
Adrian Cole 2011-06-03 02:07:12 -07:00
parent 9257f3a1e6
commit 1f535ebb6f
21 changed files with 475 additions and 86 deletions

View File

@ -44,7 +44,9 @@ import org.jclouds.atmos.functions.ParseObjectFromHeadersAndHttpContent;
import org.jclouds.atmos.functions.ParseSystemMetadataFromHeaders; import org.jclouds.atmos.functions.ParseSystemMetadataFromHeaders;
import org.jclouds.atmos.functions.ParseUserMetadataFromHeaders; import org.jclouds.atmos.functions.ParseUserMetadataFromHeaders;
import org.jclouds.atmos.functions.ReturnEndpointIfAlreadyExists; import org.jclouds.atmos.functions.ReturnEndpointIfAlreadyExists;
import org.jclouds.atmos.functions.ReturnTrueIfGroupACLIsOtherRead;
import org.jclouds.atmos.options.ListOptions; import org.jclouds.atmos.options.ListOptions;
import org.jclouds.atmos.options.PutOptions;
import org.jclouds.blobstore.functions.ThrowContainerNotFoundOn404; import org.jclouds.blobstore.functions.ThrowContainerNotFoundOn404;
import org.jclouds.blobstore.functions.ThrowKeyNotFoundOn404; import org.jclouds.blobstore.functions.ThrowKeyNotFoundOn404;
import org.jclouds.http.options.GetOptions; import org.jclouds.http.options.GetOptions;
@ -107,7 +109,7 @@ public interface AtmosAsyncClient {
@ExceptionParser(ReturnEndpointIfAlreadyExists.class) @ExceptionParser(ReturnEndpointIfAlreadyExists.class)
@Produces(MediaType.APPLICATION_OCTET_STREAM) @Produces(MediaType.APPLICATION_OCTET_STREAM)
@Consumes(MediaType.WILDCARD) @Consumes(MediaType.WILDCARD)
ListenableFuture<URI> createDirectory(@PathParam("directoryName") String directoryName); ListenableFuture<URI> createDirectory(@PathParam("directoryName") String directoryName, PutOptions... options);
/** /**
* @see AtmosClient#createFile * @see AtmosClient#createFile
@ -117,7 +119,8 @@ public interface AtmosAsyncClient {
@Consumes(MediaType.WILDCARD) @Consumes(MediaType.WILDCARD)
ListenableFuture<URI> createFile( ListenableFuture<URI> createFile(
@PathParam("parent") String parent, @PathParam("parent") String parent,
@PathParam("name") @ParamParser(AtmosObjectName.class) @BinderParam(BindMetadataToHeaders.class) AtmosObject object); @PathParam("name") @ParamParser(AtmosObjectName.class) @BinderParam(BindMetadataToHeaders.class) AtmosObject object,
PutOptions... options);
/** /**
* @see AtmosClient#updateFile * @see AtmosClient#updateFile
@ -128,7 +131,8 @@ public interface AtmosAsyncClient {
@Consumes(MediaType.WILDCARD) @Consumes(MediaType.WILDCARD)
ListenableFuture<Void> updateFile( ListenableFuture<Void> updateFile(
@PathParam("parent") String parent, @PathParam("parent") String parent,
@PathParam("name") @ParamParser(AtmosObjectName.class) @BinderParam(BindMetadataToHeaders.class) AtmosObject object); @PathParam("name") @ParamParser(AtmosObjectName.class) @BinderParam(BindMetadataToHeaders.class) AtmosObject object,
PutOptions... options);
/** /**
* @see AtmosClient#readFile * @see AtmosClient#readFile
@ -190,12 +194,14 @@ public interface AtmosAsyncClient {
@Consumes(MediaType.WILDCARD) @Consumes(MediaType.WILDCARD)
ListenableFuture<Boolean> pathExists(@PathParam("path") String path); ListenableFuture<Boolean> pathExists(@PathParam("path") String path);
// signature currently doesn't work /**
// @POST * @see AtmosClient#isPublic
// @QueryParams(keys = "acl") */
// @Headers(keys = { "x-emc-useracl", "x-emc-groupacl" }, values = { "root=FULL_CONTROL", @HEAD
// "other=READ" }) @ResponseParser(ReturnTrueIfGroupACLIsOtherRead.class)
// @Consumes(MediaType.WILDCARD) @Path("/{path}")
// void makePublic(@Endpoint URI url); @Consumes(MediaType.WILDCARD)
@ExceptionParser(ReturnFalseOnNotFoundOr404.class)
ListenableFuture<Boolean> isPublic(@PathParam("path") String path);
} }

View File

@ -27,6 +27,7 @@ import org.jclouds.atmos.domain.DirectoryEntry;
import org.jclouds.atmos.domain.SystemMetadata; import org.jclouds.atmos.domain.SystemMetadata;
import org.jclouds.atmos.domain.UserMetadata; import org.jclouds.atmos.domain.UserMetadata;
import org.jclouds.atmos.options.ListOptions; import org.jclouds.atmos.options.ListOptions;
import org.jclouds.atmos.options.PutOptions;
import org.jclouds.concurrent.Timeout; import org.jclouds.concurrent.Timeout;
import org.jclouds.http.options.GetOptions; import org.jclouds.http.options.GetOptions;
@ -52,13 +53,13 @@ public interface AtmosClient {
BoundedSet<? extends DirectoryEntry> listDirectory(String directoryName, ListOptions... options); BoundedSet<? extends DirectoryEntry> listDirectory(String directoryName, ListOptions... options);
URI createDirectory(String directoryName); URI createDirectory(String directoryName, PutOptions... options);
@Timeout(duration = 10, timeUnit = TimeUnit.MINUTES) @Timeout(duration = 10, timeUnit = TimeUnit.MINUTES)
URI createFile(String parent, AtmosObject object); URI createFile(String parent, AtmosObject object, PutOptions... options);
@Timeout(duration = 10, timeUnit = TimeUnit.MINUTES) @Timeout(duration = 10, timeUnit = TimeUnit.MINUTES)
void updateFile(String parent, AtmosObject object); void updateFile(String parent, AtmosObject object, PutOptions... options);
@Timeout(duration = 10, timeUnit = TimeUnit.MINUTES) @Timeout(duration = 10, timeUnit = TimeUnit.MINUTES)
AtmosObject readFile(String path, GetOptions... options); AtmosObject readFile(String path, GetOptions... options);
@ -73,4 +74,6 @@ public interface AtmosClient {
boolean pathExists(String path); boolean pathExists(String path);
boolean isPublic(String path);
} }

View File

@ -19,8 +19,10 @@
package org.jclouds.atmos.blobstore; package org.jclouds.atmos.blobstore;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static org.jclouds.atmos.options.PutOptions.Builder.publicRead;
import java.net.URI; import java.net.URI;
import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
@ -79,6 +81,7 @@ public class AtmosAsyncBlobStore extends BaseAsyncBlobStore {
private final Crypto crypto; private final Crypto crypto;
private final BlobToHttpGetOptions blob2ObjectGetOptions; private final BlobToHttpGetOptions blob2ObjectGetOptions;
private final Provider<FetchBlobMetadata> fetchBlobMetadataProvider; private final Provider<FetchBlobMetadata> fetchBlobMetadataProvider;
private final Map<String, Boolean> isPublic;
@Inject @Inject
AtmosAsyncBlobStore(BlobStoreContext context, BlobUtils blobUtils, AtmosAsyncBlobStore(BlobStoreContext context, BlobUtils blobUtils,
@ -87,7 +90,8 @@ public class AtmosAsyncBlobStore extends BaseAsyncBlobStore {
ObjectToBlob object2Blob, ObjectToBlobMetadata object2BlobMd, BlobToObject blob2Object, ObjectToBlob object2Blob, ObjectToBlobMetadata object2BlobMd, BlobToObject blob2Object,
BlobStoreListOptionsToListOptions container2ContainerListOptions, BlobStoreListOptionsToListOptions container2ContainerListOptions,
DirectoryEntryListToResourceMetadataList container2ResourceList, Crypto crypto, DirectoryEntryListToResourceMetadataList container2ResourceList, Crypto crypto,
BlobToHttpGetOptions blob2ObjectGetOptions, Provider<FetchBlobMetadata> fetchBlobMetadataProvider) { BlobToHttpGetOptions blob2ObjectGetOptions, Provider<FetchBlobMetadata> fetchBlobMetadataProvider,
Map<String, Boolean> isPublic) {
super(context, blobUtils, service, defaultLocation, locations); super(context, blobUtils, service, defaultLocation, locations);
this.blob2ObjectGetOptions = checkNotNull(blob2ObjectGetOptions, "blob2ObjectGetOptions"); this.blob2ObjectGetOptions = checkNotNull(blob2ObjectGetOptions, "blob2ObjectGetOptions");
this.sync = checkNotNull(sync, "sync"); this.sync = checkNotNull(sync, "sync");
@ -100,6 +104,7 @@ public class AtmosAsyncBlobStore extends BaseAsyncBlobStore {
this.object2BlobMd = checkNotNull(object2BlobMd, "object2BlobMd"); this.object2BlobMd = checkNotNull(object2BlobMd, "object2BlobMd");
this.crypto = checkNotNull(crypto, "crypto"); this.crypto = checkNotNull(crypto, "crypto");
this.fetchBlobMetadataProvider = checkNotNull(fetchBlobMetadataProvider, "fetchBlobMetadataProvider"); this.fetchBlobMetadataProvider = checkNotNull(fetchBlobMetadataProvider, "fetchBlobMetadataProvider");
this.isPublic = checkNotNull(isPublic, "isPublic");
} }
/** /**
@ -231,11 +236,18 @@ public class AtmosAsyncBlobStore extends BaseAsyncBlobStore {
*/ */
@Override @Override
public ListenableFuture<String> putBlob(final String container, final Blob blob) { public ListenableFuture<String> putBlob(final String container, final Blob blob) {
final org.jclouds.atmos.options.PutOptions options = new org.jclouds.atmos.options.PutOptions();
try {
if (isPublic.get(container + "/"))
options.publicRead();
} catch (NullPointerException e) {
// MapMaker
}
return Futures.makeListenable(service.submit(new Callable<String>() { return Futures.makeListenable(service.submit(new Callable<String>() {
@Override @Override
public String call() throws Exception { public String call() throws Exception {
return AtmosUtils.putBlob(sync, crypto, blob2Object, container, blob); return AtmosUtils.putBlob(sync, crypto, blob2Object, container, blob, options);
} }
@Override @Override
@ -264,7 +276,13 @@ public class AtmosAsyncBlobStore extends BaseAsyncBlobStore {
public ListenableFuture<Boolean> createContainerInLocation(Location location, String container, public ListenableFuture<Boolean> createContainerInLocation(Location location, String container,
CreateContainerOptions options) { CreateContainerOptions options) {
if (options.isPublicRead()) if (options.isPublicRead())
throw new UnsupportedOperationException("publicRead"); return Futures.compose(async.createDirectory(container, publicRead()), new Function<URI, Boolean>() {
public Boolean apply(URI from) {
return true;
}
}, service);
return createContainerInLocation(location, container); return createContainerInLocation(location, container);
} }

View File

@ -29,6 +29,7 @@ import javax.inject.Singleton;
import org.jclouds.atmos.AtmosAsyncClient; import org.jclouds.atmos.AtmosAsyncClient;
import org.jclouds.atmos.blobstore.functions.BlobToObject; import org.jclouds.atmos.blobstore.functions.BlobToObject;
import org.jclouds.atmos.domain.AtmosObject; import org.jclouds.atmos.domain.AtmosObject;
import org.jclouds.atmos.options.PutOptions;
import org.jclouds.blobstore.BlobRequestSigner; import org.jclouds.blobstore.BlobRequestSigner;
import org.jclouds.blobstore.domain.Blob; import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.functions.BlobToHttpGetOptions; import org.jclouds.blobstore.functions.BlobToHttpGetOptions;
@ -58,7 +59,7 @@ public class AtmosBlobRequestSigner implements BlobRequestSigner {
this.blob2ObjectGetOptions = checkNotNull(blob2ObjectGetOptions, "blob2ObjectGetOptions"); this.blob2ObjectGetOptions = checkNotNull(blob2ObjectGetOptions, "blob2ObjectGetOptions");
this.getMethod = AtmosAsyncClient.class.getMethod("readFile", String.class, GetOptions[].class); this.getMethod = AtmosAsyncClient.class.getMethod("readFile", String.class, GetOptions[].class);
this.deleteMethod = AtmosAsyncClient.class.getMethod("deletePath", String.class); this.deleteMethod = AtmosAsyncClient.class.getMethod("deletePath", String.class);
this.createMethod = AtmosAsyncClient.class.getMethod("createFile", String.class, AtmosObject.class); this.createMethod = AtmosAsyncClient.class.getMethod("createFile", String.class, AtmosObject.class, PutOptions[].class);
} }
@Override @Override

View File

@ -19,7 +19,9 @@
package org.jclouds.atmos.blobstore; package org.jclouds.atmos.blobstore;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static org.jclouds.atmos.options.PutOptions.Builder.publicRead;
import java.util.Map;
import java.util.Set; import java.util.Set;
import javax.inject.Inject; import javax.inject.Inject;
@ -67,6 +69,7 @@ public class AtmosBlobStore extends BaseBlobStore {
private final Crypto crypto; private final Crypto crypto;
private final BlobToHttpGetOptions blob2ObjectGetOptions; private final BlobToHttpGetOptions blob2ObjectGetOptions;
private final Provider<FetchBlobMetadata> fetchBlobMetadataProvider; private final Provider<FetchBlobMetadata> fetchBlobMetadataProvider;
private final Map<String, Boolean> isPublic;
@Inject @Inject
AtmosBlobStore(BlobStoreContext context, BlobUtils blobUtils, Supplier<Location> defaultLocation, AtmosBlobStore(BlobStoreContext context, BlobUtils blobUtils, Supplier<Location> defaultLocation,
@ -74,7 +77,8 @@ public class AtmosBlobStore extends BaseBlobStore {
ObjectToBlobMetadata object2BlobMd, BlobToObject blob2Object, ObjectToBlobMetadata object2BlobMd, BlobToObject blob2Object,
BlobStoreListOptionsToListOptions container2ContainerListOptions, BlobStoreListOptionsToListOptions container2ContainerListOptions,
DirectoryEntryListToResourceMetadataList container2ResourceList, Crypto crypto, DirectoryEntryListToResourceMetadataList container2ResourceList, Crypto crypto,
BlobToHttpGetOptions blob2ObjectGetOptions, Provider<FetchBlobMetadata> fetchBlobMetadataProvider) { BlobToHttpGetOptions blob2ObjectGetOptions, Provider<FetchBlobMetadata> fetchBlobMetadataProvider,
Map<String, Boolean> isPublic) {
super(context, blobUtils, defaultLocation, locations); super(context, blobUtils, defaultLocation, locations);
this.blob2ObjectGetOptions = checkNotNull(blob2ObjectGetOptions, "blob2ObjectGetOptions"); this.blob2ObjectGetOptions = checkNotNull(blob2ObjectGetOptions, "blob2ObjectGetOptions");
this.sync = checkNotNull(sync, "sync"); this.sync = checkNotNull(sync, "sync");
@ -86,6 +90,7 @@ public class AtmosBlobStore extends BaseBlobStore {
this.object2BlobMd = checkNotNull(object2BlobMd, "object2BlobMd"); this.object2BlobMd = checkNotNull(object2BlobMd, "object2BlobMd");
this.crypto = checkNotNull(crypto, "crypto"); this.crypto = checkNotNull(crypto, "crypto");
this.fetchBlobMetadataProvider = checkNotNull(fetchBlobMetadataProvider, "fetchBlobMetadataProvider"); this.fetchBlobMetadataProvider = checkNotNull(fetchBlobMetadataProvider, "fetchBlobMetadataProvider");
this.isPublic = checkNotNull(isPublic, "isPublic");
} }
/** /**
@ -205,7 +210,14 @@ public class AtmosBlobStore extends BaseBlobStore {
*/ */
@Override @Override
public String putBlob(final String container, final Blob blob) { public String putBlob(final String container, final Blob blob) {
return AtmosUtils.putBlob(sync, crypto, blob2Object, container, blob); final org.jclouds.atmos.options.PutOptions options = new org.jclouds.atmos.options.PutOptions();
try {
if (isPublic.get(container + "/"))
options.publicRead();
} catch (NullPointerException e) {
// MapMaker
}
return AtmosUtils.putBlob(sync, crypto, blob2Object, container, blob, options);
} }
/** /**
@ -229,8 +241,10 @@ public class AtmosBlobStore extends BaseBlobStore {
@Override @Override
public boolean createContainerInLocation(Location location, String container, CreateContainerOptions options) { public boolean createContainerInLocation(Location location, String container, CreateContainerOptions options) {
if (options.isPublicRead()) if (options.isPublicRead()) {
throw new UnsupportedOperationException("publicRead"); sync.createDirectory(container, publicRead());
return true;
}
return createContainerInLocation(location, container); return createContainerInLocation(location, container);
} }
} }

View File

@ -18,6 +18,11 @@
*/ */
package org.jclouds.atmos.blobstore.config; package org.jclouds.atmos.blobstore.config;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import javax.inject.Singleton;
import org.jclouds.atmos.AtmosAsyncClient; import org.jclouds.atmos.AtmosAsyncClient;
import org.jclouds.atmos.AtmosClient; import org.jclouds.atmos.AtmosClient;
import org.jclouds.atmos.blobstore.AtmosAsyncBlobStore; import org.jclouds.atmos.blobstore.AtmosAsyncBlobStore;
@ -34,7 +39,10 @@ import org.jclouds.blobstore.internal.BlobStoreContextImpl;
import org.jclouds.blobstore.strategy.ContainsValueInListStrategy; import org.jclouds.blobstore.strategy.ContainsValueInListStrategy;
import org.jclouds.location.config.JustProviderLocationModule; import org.jclouds.location.config.JustProviderLocationModule;
import com.google.common.base.Function;
import com.google.common.collect.MapMaker;
import com.google.inject.AbstractModule; import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import com.google.inject.Scopes; import com.google.inject.Scopes;
import com.google.inject.TypeLiteral; import com.google.inject.TypeLiteral;
@ -57,4 +65,19 @@ public class AtmosBlobStoreContextModule extends AbstractModule {
bind(BlobRequestSigner.class).to(AtmosBlobRequestSigner.class); bind(BlobRequestSigner.class).to(AtmosBlobRequestSigner.class);
install(new JustProviderLocationModule()); install(new JustProviderLocationModule());
} }
@Provides
@Singleton
protected Map<String, Boolean> isPublic(final AtmosClient client) {
return new MapMaker().expireAfterWrite(30, TimeUnit.SECONDS).makeComputingMap(new Function<String, Boolean>() {
public Boolean apply(String directory) {
return client.isPublic(directory);
}
@Override
public String toString() {
return "isPublic()";
}
});
}
} }

View File

@ -36,7 +36,8 @@ public class BlobToContentMetadata implements Function<BlobMetadata, MutableCont
public MutableContentMetadata apply(BlobMetadata base) { public MutableContentMetadata apply(BlobMetadata base) {
MutableBlobMetadataImpl to = new MutableBlobMetadataImpl(); MutableBlobMetadataImpl to = new MutableBlobMetadataImpl();
HttpUtils.copy(base.getContentMetadata(), to.getContentMetadata()); HttpUtils.copy(base.getContentMetadata(), to.getContentMetadata());
return new DelegatingMutableContentMetadata(base.getName(), to.getContentMetadata()); return new DelegatingMutableContentMetadata(base.getUri(), base.getName(), base.getUri() != null ? base.getUri()
.getPath() : null, to.getContentMetadata());
} }
} }

View File

@ -27,6 +27,7 @@ import javax.inject.Singleton;
import org.jclouds.atmos.domain.AtmosObject; import org.jclouds.atmos.domain.AtmosObject;
import org.jclouds.atmos.domain.FileType; import org.jclouds.atmos.domain.FileType;
import org.jclouds.atmos.filters.ShareUrl;
import org.jclouds.atmos.functions.AtmosObjectName; import org.jclouds.atmos.functions.AtmosObjectName;
import org.jclouds.blobstore.domain.MutableBlobMetadata; import org.jclouds.blobstore.domain.MutableBlobMetadata;
import org.jclouds.blobstore.domain.StorageType; import org.jclouds.blobstore.domain.StorageType;
@ -34,7 +35,9 @@ import org.jclouds.blobstore.domain.internal.MutableBlobMetadataImpl;
import org.jclouds.http.HttpUtils; import org.jclouds.http.HttpUtils;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
/** /**
@ -43,13 +46,16 @@ import com.google.common.collect.Maps;
@Singleton @Singleton
public class ObjectToBlobMetadata implements Function<AtmosObject, MutableBlobMetadata> { public class ObjectToBlobMetadata implements Function<AtmosObject, MutableBlobMetadata> {
private final AtmosObjectName objectName; private final AtmosObjectName objectName;
private static final Set<String> systemMetadata = ImmutableSet.of("atime", "mtime", "ctime", private final ShareUrl shareUrl;
"itime", "type", "uid", "gid", "objectid", "objname", "size", "nlink", "policyname",
"content-md5"); private static final Set<String> systemMetadata = ImmutableSet.of("atime", "mtime", "ctime", "itime", "type", "uid",
"gid", "objectid", "objname", "size", "nlink", "policyname", "content-md5");
@Inject @Inject
protected ObjectToBlobMetadata(AtmosObjectName objectName) { protected ObjectToBlobMetadata(AtmosObjectName objectName, ShareUrl shareUrl)
throws SecurityException, NoSuchMethodException {
this.objectName = objectName; this.objectName = objectName;
this.shareUrl = shareUrl;
} }
public MutableBlobMetadata apply(AtmosObject from) { public MutableBlobMetadata apply(AtmosObject from) {
@ -60,6 +66,10 @@ public class ObjectToBlobMetadata implements Function<AtmosObject, MutableBlobMe
to.setLastModified(from.getSystemMetadata().getLastUserDataModification()); to.setLastModified(from.getSystemMetadata().getLastUserDataModification());
HttpUtils.copy(from.getContentMetadata(), to.getContentMetadata()); HttpUtils.copy(from.getContentMetadata(), to.getContentMetadata());
to.setName(objectName.apply(from)); to.setName(objectName.apply(from));
to.setUri(from.getContentMetadata().getUri());
to.setContainer(Iterables.get(Splitter.on('/').split(from.getContentMetadata().getPath()),0));
if (from.getAllHeaders().containsEntry("x-emc-groupacl", "other=READ"))
to.setPublicUri(shareUrl.apply(from.getContentMetadata().getPath()));
if (from.getSystemMetadata().getType() == FileType.DIRECTORY) { if (from.getSystemMetadata().getType() == FileType.DIRECTORY) {
to.setType(StorageType.FOLDER); to.setType(StorageType.FOLDER);
} else { } else {

View File

@ -18,6 +18,7 @@
*/ */
package org.jclouds.atmos.config; package org.jclouds.atmos.config;
import java.util.Date;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.inject.Named; import javax.inject.Named;
@ -43,8 +44,8 @@ import com.google.common.base.Suppliers;
import com.google.inject.Provides; import com.google.inject.Provides;
/** /**
* Configures the EMC Atmos Online Storage authentication service connection, * Configures the EMC Atmos Online Storage authentication service connection, including logging and
* including logging and http transport. * http transport.
* *
* @author Adrian Cole * @author Adrian Cole
*/ */
@ -74,7 +75,7 @@ public class AtmosRestClientModule extends RestClientModule<AtmosClient, AtmosAs
@Provides @Provides
@TimeStamp @TimeStamp
Supplier<String> provideTimeStampCache(@Named(Constants.PROPERTY_SESSION_INTERVAL) long seconds, Supplier<String> provideTimeStampCache(@Named(Constants.PROPERTY_SESSION_INTERVAL) long seconds,
final DateService dateService) { final DateService dateService) {
return Suppliers.memoizeWithExpiration(new Supplier<String>() { return Suppliers.memoizeWithExpiration(new Supplier<String>() {
public String get() { public String get() {
return dateService.rfc822DateFormat(); return dateService.rfc822DateFormat();
@ -82,6 +83,12 @@ public class AtmosRestClientModule extends RestClientModule<AtmosClient, AtmosAs
}, seconds, TimeUnit.SECONDS); }, seconds, TimeUnit.SECONDS);
} }
@Provides
@TimeStamp
protected Long provideShareableUrlTimeout() {
return new Date().getTime() + TimeUnit.HOURS.toMillis(1);
}
@Override @Override
protected void bindErrorHandlers() { protected void bindErrorHandlers() {
bind(HttpErrorHandler.class).annotatedWith(Redirection.class).to(ParseAtmosErrorFromXmlContent.class); bind(HttpErrorHandler.class).annotatedWith(Redirection.class).to(ParseAtmosErrorFromXmlContent.class);

View File

@ -18,6 +18,8 @@
*/ */
package org.jclouds.atmos.domain; package org.jclouds.atmos.domain;
import java.net.URI;
import org.jclouds.atmos.domain.internal.DelegatingMutableContentMetadata; import org.jclouds.atmos.domain.internal.DelegatingMutableContentMetadata;
import com.google.inject.ImplementedBy; import com.google.inject.ImplementedBy;
@ -34,4 +36,11 @@ public interface MutableContentMetadata extends org.jclouds.io.MutableContentMet
public void setName(String name); public void setName(String name);
public URI getUri();
public void setUri(URI uri);
public String getPath();
public void setPath(String path);
} }

View File

@ -55,7 +55,7 @@ public class AtmosObjectImpl extends PayloadEnclosingImpl implements AtmosObject
} }
public AtmosObject create(MutableContentMetadata contentMetadata, SystemMetadata systemMetadata, public AtmosObject create(MutableContentMetadata contentMetadata, SystemMetadata systemMetadata,
UserMetadata userMetadata) { UserMetadata userMetadata) {
return new AtmosObjectImpl(contentMetadata, systemMetadata, userMetadata); return new AtmosObjectImpl(contentMetadata, systemMetadata, userMetadata);
} }
} }
@ -75,7 +75,7 @@ public class AtmosObjectImpl extends PayloadEnclosingImpl implements AtmosObject
private Multimap<String, String> allHeaders = LinkedHashMultimap.create(); private Multimap<String, String> allHeaders = LinkedHashMultimap.create();
public AtmosObjectImpl(MutableContentMetadata contentMetadata, SystemMetadata systemMetadata, public AtmosObjectImpl(MutableContentMetadata contentMetadata, SystemMetadata systemMetadata,
UserMetadata userMetadata) { UserMetadata userMetadata) {
this.contentMetadata = contentMetadata; this.contentMetadata = contentMetadata;
this.systemMetadata = systemMetadata; this.systemMetadata = systemMetadata;
this.userMetadata = userMetadata; this.userMetadata = userMetadata;
@ -165,7 +165,7 @@ public class AtmosObjectImpl extends PayloadEnclosingImpl implements AtmosObject
@Override @Override
public void setPayload(Payload data) { public void setPayload(Payload data) {
this.payload = data; this.payload = data;
this.contentMetadata = new DelegatingMutableContentMetadata(contentMetadata.getName(), this.contentMetadata = new DelegatingMutableContentMetadata(contentMetadata.getUri(), contentMetadata.getName(),
payload.getContentMetadata()); contentMetadata.getPath(), payload.getContentMetadata());
} }
} }

View File

@ -18,6 +18,8 @@
*/ */
package org.jclouds.atmos.domain.internal; package org.jclouds.atmos.domain.internal;
import java.net.URI;
import org.jclouds.atmos.domain.MutableContentMetadata; import org.jclouds.atmos.domain.MutableContentMetadata;
import org.jclouds.io.ContentMetadataBuilder; import org.jclouds.io.ContentMetadataBuilder;
import org.jclouds.io.payloads.BaseMutableContentMetadata; import org.jclouds.io.payloads.BaseMutableContentMetadata;
@ -29,16 +31,21 @@ import com.google.common.collect.Multimap;
* @author Adrian Cole * @author Adrian Cole
*/ */
public class DelegatingMutableContentMetadata implements MutableContentMetadata { public class DelegatingMutableContentMetadata implements MutableContentMetadata {
private URI uri;
private String name; private String name;
private String path;
private final org.jclouds.io.MutableContentMetadata delegate; private final org.jclouds.io.MutableContentMetadata delegate;
public DelegatingMutableContentMetadata() { public DelegatingMutableContentMetadata() {
this(null, new BaseMutableContentMetadata()); this(null, null, null, new BaseMutableContentMetadata());
} }
public DelegatingMutableContentMetadata(String name, org.jclouds.io.MutableContentMetadata delegate) { public DelegatingMutableContentMetadata(URI uri, String name, String path,
org.jclouds.io.MutableContentMetadata delegate) {
this.uri = uri;
this.name = name; this.name = name;
this.delegate = delegate; this.delegate = delegate;
this.path = path;
} }
@Override @Override
@ -90,15 +97,10 @@ public class DelegatingMutableContentMetadata implements MutableContentMetadata
if (getClass() != obj.getClass()) if (getClass() != obj.getClass())
return false; return false;
DelegatingMutableContentMetadata other = (DelegatingMutableContentMetadata) obj; DelegatingMutableContentMetadata other = (DelegatingMutableContentMetadata) obj;
if (delegate == null) { if (uri == null) {
if (other.delegate != null) if (other.uri != null)
return false; return false;
} else if (!delegate.equals(other.delegate)) } else if (!uri.equals(other.uri))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false; return false;
return true; return true;
} }
@ -107,14 +109,13 @@ public class DelegatingMutableContentMetadata implements MutableContentMetadata
public int hashCode() { public int hashCode() {
final int prime = 31; final int prime = 31;
int result = 1; int result = 1;
result = prime * result + ((delegate == null) ? 0 : delegate.hashCode()); result = prime * result + ((uri == null) ? 0 : uri.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result; return result;
} }
@Override @Override
public String toString() { public String toString() {
return "[name=" + name + ", delegate=" + delegate + "]"; return "[uri=" + uri + ", name=" + name + ", path=" + path + ", delegate=" + delegate + "]";
} }
public org.jclouds.io.MutableContentMetadata getDelegate() { public org.jclouds.io.MutableContentMetadata getDelegate() {
@ -162,4 +163,24 @@ public class DelegatingMutableContentMetadata implements MutableContentMetadata
return delegate.toBuilder(); return delegate.toBuilder();
} }
@Override
public URI getUri() {
return uri;
}
@Override
public void setUri(URI uri) {
this.uri = uri;
}
@Override
public String getPath() {
return path;
}
@Override
public void setPath(String path) {
this.path = path;
}
} }

View File

@ -0,0 +1,107 @@
/**
*
* Copyright (C) 2011 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* 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 org.jclouds.atmos.filters;
import static org.jclouds.Constants.LOGGER_SIGNATURE;
import static org.jclouds.Constants.PROPERTY_CREDENTIAL;
import static org.jclouds.Constants.PROPERTY_IDENTITY;
import java.net.URI;
import javax.annotation.Resource;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import javax.ws.rs.core.UriBuilder;
import org.jclouds.crypto.Crypto;
import org.jclouds.crypto.CryptoStreams;
import org.jclouds.date.TimeStamp;
import org.jclouds.http.HttpException;
import org.jclouds.io.InputSuppliers;
import org.jclouds.location.Provider;
import org.jclouds.logging.Logger;
import com.google.common.base.Function;
/**
* Signs the EMC Atmos Online Storage request.
*
* @see <a href="https://community.emc.com/community/labs/atmos_online" />
* @author Adrian Cole
*
*/
@Singleton
public class ShareUrl implements Function<String, URI> {
private final String uid;
private final byte[] key;
private final URI provider;
private final javax.inject.Provider<Long> timeStampProvider;
private final javax.inject.Provider<UriBuilder> uriBuilders;
private final Crypto crypto;
@Resource
Logger logger = Logger.NULL;
@Resource
@Named(LOGGER_SIGNATURE)
Logger signatureLog = Logger.NULL;
@Inject
public ShareUrl(@Named(PROPERTY_IDENTITY) String uid, @Named(PROPERTY_CREDENTIAL) String encodedKey,
@Provider URI provider, @TimeStamp javax.inject.Provider<Long> timeStampProvider,
javax.inject.Provider<UriBuilder> uriBuilders, Crypto crypto) {
this.uid = uid;
this.key = CryptoStreams.base64(encodedKey);
this.provider = provider;
this.uriBuilders = uriBuilders;
this.timeStampProvider = timeStampProvider;
this.crypto = crypto;
}
@Override
public URI apply(String path) throws HttpException {
String requestedResource = new StringBuilder().append("/rest/namespace/").append(path).toString();
long expires = timeStampProvider.get();
String signature = signString(createStringToSign(requestedResource, expires));
return uriBuilders.get().uri(provider).path(requestedResource).queryParam("uid", uid).queryParam("expires",
expires).queryParam("signature", signature).build();
}
public String createStringToSign(String requestedResource, long expires) {
StringBuilder toSign = new StringBuilder();
toSign.append("GET\n");
toSign.append(requestedResource.toLowerCase()).append("\n");
toSign.append(uid).append("\n");
toSign.append(expires);
return toSign.toString();
}
public String signString(String toSign) {
String signature;
try {
signature = CryptoStreams.base64(CryptoStreams.mac(InputSuppliers.of(toSign), crypto.hmacSHA1(key)));
} catch (Exception e) {
throw new HttpException("error signing request", e);
}
return signature;
}
}

View File

@ -21,12 +21,16 @@ package org.jclouds.atmos.functions;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static org.jclouds.http.HttpUtils.attemptToParseSizeAndRangeFromHeaders; import static org.jclouds.http.HttpUtils.attemptToParseSizeAndRangeFromHeaders;
import java.net.URI;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton;
import org.jclouds.atmos.domain.AtmosObject; import org.jclouds.atmos.domain.AtmosObject;
import org.jclouds.blobstore.functions.ParseSystemAndUserMetadataFromHeaders; import org.jclouds.blobstore.functions.ParseSystemAndUserMetadataFromHeaders;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse; import org.jclouds.http.HttpResponse;
import org.jclouds.rest.InvocationContext;
import org.jclouds.rest.internal.GeneratedHttpRequest;
import com.google.common.base.Function; import com.google.common.base.Function;
@ -36,16 +40,18 @@ import com.google.common.base.Function;
* @see ParseMetadataFromHeaders * @see ParseMetadataFromHeaders
* @author Adrian Cole * @author Adrian Cole
*/ */
@Singleton public class ParseObjectFromHeadersAndHttpContent implements Function<HttpResponse, AtmosObject>,
public class ParseObjectFromHeadersAndHttpContent implements Function<HttpResponse, AtmosObject> { InvocationContext<ParseObjectFromHeadersAndHttpContent> {
private final ParseSystemMetadataFromHeaders systemMetadataParser; private final ParseSystemMetadataFromHeaders systemMetadataParser;
private final ParseUserMetadataFromHeaders userMetadataParser; private final ParseUserMetadataFromHeaders userMetadataParser;
private final AtmosObject.Factory objectProvider; private final AtmosObject.Factory objectProvider;
private URI uri;
private String path;
@Inject @Inject
public ParseObjectFromHeadersAndHttpContent(ParseSystemMetadataFromHeaders systemMetadataParser, public ParseObjectFromHeadersAndHttpContent(ParseSystemMetadataFromHeaders systemMetadataParser,
ParseUserMetadataFromHeaders userMetadataParser, AtmosObject.Factory objectProvider) { ParseUserMetadataFromHeaders userMetadataParser, AtmosObject.Factory objectProvider) {
this.systemMetadataParser = checkNotNull(systemMetadataParser, "systemMetadataParser"); this.systemMetadataParser = checkNotNull(systemMetadataParser, "systemMetadataParser");
this.userMetadataParser = checkNotNull(userMetadataParser, "userMetadataParser"); this.userMetadataParser = checkNotNull(userMetadataParser, "userMetadataParser");
this.objectProvider = checkNotNull(objectProvider, "objectProvider"); this.objectProvider = checkNotNull(objectProvider, "objectProvider");
@ -63,9 +69,22 @@ public class ParseObjectFromHeadersAndHttpContent implements Function<HttpRespon
checkNotNull(from, "http response"); checkNotNull(from, "http response");
AtmosObject object = objectProvider.create(systemMetadataParser.apply(from), userMetadataParser.apply(from)); AtmosObject object = objectProvider.create(systemMetadataParser.apply(from), userMetadataParser.apply(from));
object.getContentMetadata().setName(object.getSystemMetadata().getObjectName()); object.getContentMetadata().setName(object.getSystemMetadata().getObjectName());
object.getContentMetadata().setPath(path);
object.getContentMetadata().setUri(uri);
object.getAllHeaders().putAll(from.getHeaders()); object.getAllHeaders().putAll(from.getHeaders());
object.setPayload(from.getPayload()); object.setPayload(from.getPayload());
object.getContentMetadata().setContentLength(attemptToParseSizeAndRangeFromHeaders(from)); object.getContentMetadata().setContentLength(attemptToParseSizeAndRangeFromHeaders(from));
return object; return object;
} }
@Override
public ParseObjectFromHeadersAndHttpContent setContext(HttpRequest request) {
this.uri = request.getEndpoint();
return setPath(GeneratedHttpRequest.class.cast(request).getArgs().get(0).toString());
}
private ParseObjectFromHeadersAndHttpContent setPath(String path) {
this.path = path;
return this;
}
} }

View File

@ -16,27 +16,24 @@
* limitations under the License. * limitations under the License.
* ==================================================================== * ====================================================================
*/ */
package org.jclouds.atmos.blobstore.functions; package org.jclouds.atmos.functions;
import static org.testng.Assert.assertEquals; import static com.google.common.base.Preconditions.checkNotNull;
import org.testng.annotations.Test; import javax.inject.Singleton;
import com.google.inject.Guice; import org.jclouds.http.HttpResponse;
import com.google.inject.Injector;
import com.google.common.base.Function;
/** /**
* Tests behavior of {@code ObjectToBlobMetadata}
*
* @author Adrian Cole * @author Adrian Cole
*/ */
@Test(groups = "unit") @Singleton
public class ObjectToBlobMetadataTest { public class ReturnTrueIfGroupACLIsOtherRead implements Function<HttpResponse, Boolean> {
public void testFromWhenTypeIsDirectory() { public Boolean apply(HttpResponse from) {
Injector injector = Guice.createInjector(); checkNotNull(from, "http response");
injector.getInstance(ObjectToBlobMetadata.class); return from.getHeaders().containsEntry("x-emc-groupacl", "other=READ");
assertEquals("", "");
} }
} }

View File

@ -0,0 +1,65 @@
/**
*
* Copyright (C) 2011 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* 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 org.jclouds.atmos.options;
import org.jclouds.http.options.BaseHttpRequestOptions;
/**
* Contains options supported in the REST API for the PUT operations.
* <p/>
* <h2>
* Usage</h2> The recommended way to instantiate a PutOptions object is to statically import
* PutOptions.Builder.* and invoke a static creation method followed by an instance mutator (if
* needed):
* <p/>
* <code>
* import org.jclouds.atmos.options.PutOptions.Builder.*
* import org.jclouds.atmos.AtmosClient;
*
* AtmosClient connection = // get connection
* connection.createDirectory("directory", publicRead());
* <code>
*
* @author Adrian Cole
*
*/
public class PutOptions extends BaseHttpRequestOptions {
public static final PutOptions NONE = new PutOptions();
/**
* Add public access to all users
*
*/
public PutOptions publicRead() {
this.replaceHeader("x-emc-useracl", "root=FULL_CONTROL");
this.replaceHeader("x-emc-groupacl", "other=READ");
return this;
}
public static class Builder {
/**
* @see PutOptions#publicRead
*/
public static PutOptions publicRead() {
PutOptions options = new PutOptions();
return options.publicRead();
}
}
}

View File

@ -28,6 +28,7 @@ import org.jclouds.atmos.AtmosClient;
import org.jclouds.atmos.blobstore.functions.BlobToObject; import org.jclouds.atmos.blobstore.functions.BlobToObject;
import org.jclouds.atmos.domain.AtmosError; import org.jclouds.atmos.domain.AtmosError;
import org.jclouds.atmos.filters.SignRequest; import org.jclouds.atmos.filters.SignRequest;
import org.jclouds.atmos.options.PutOptions;
import org.jclouds.atmos.xml.ErrorHandler; import org.jclouds.atmos.xml.ErrorHandler;
import org.jclouds.blobstore.domain.Blob; import org.jclouds.blobstore.domain.Blob;
import org.jclouds.crypto.Crypto; import org.jclouds.crypto.Crypto;
@ -56,8 +57,8 @@ public class AtmosUtils {
@Inject @Inject
Provider<ErrorHandler> errorHandlerProvider; Provider<ErrorHandler> errorHandlerProvider;
public AtmosError parseAtmosErrorFromContent(HttpCommand command, HttpResponse response, public AtmosError parseAtmosErrorFromContent(HttpCommand command, HttpResponse response, InputStream content)
InputStream content) throws HttpException { throws HttpException {
AtmosError error = (AtmosError) factory.create(errorHandlerProvider.get()).parse(content); AtmosError error = (AtmosError) factory.create(errorHandlerProvider.get()).parse(content);
if (error.getCode() == 1032) { if (error.getCode() == 1032) {
error.setStringSigned(signer.createStringToSign(command.getCurrentRequest())); error.setStringSigned(signer.createStringToSign(command.getCurrentRequest()));
@ -66,11 +67,11 @@ public class AtmosUtils {
} }
public static String putBlob(final AtmosClient sync, Crypto crypto, BlobToObject blob2Object, public static String putBlob(final AtmosClient sync, Crypto crypto, BlobToObject blob2Object, String container,
String container, Blob blob) { Blob blob, PutOptions options) {
final String path = container + "/" + blob.getMetadata().getName(); final String path = container + "/" + blob.getMetadata().getName();
deleteAndEnsureGone(sync, path); deleteAndEnsureGone(sync, path);
sync.createFile(container, blob2Object.apply(blob)); sync.createFile(container, blob2Object.apply(blob), options);
return path; return path;
} }

View File

@ -21,7 +21,6 @@ package org.jclouds.atmos;
import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEquals;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.Array;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Properties; import java.util.Properties;
@ -35,7 +34,9 @@ import org.jclouds.atmos.functions.ParseDirectoryListFromContentAndHeaders;
import org.jclouds.atmos.functions.ParseObjectFromHeadersAndHttpContent; import org.jclouds.atmos.functions.ParseObjectFromHeadersAndHttpContent;
import org.jclouds.atmos.functions.ParseSystemMetadataFromHeaders; import org.jclouds.atmos.functions.ParseSystemMetadataFromHeaders;
import org.jclouds.atmos.functions.ReturnEndpointIfAlreadyExists; import org.jclouds.atmos.functions.ReturnEndpointIfAlreadyExists;
import org.jclouds.atmos.functions.ReturnTrueIfGroupACLIsOtherRead;
import org.jclouds.atmos.options.ListOptions; import org.jclouds.atmos.options.ListOptions;
import org.jclouds.atmos.options.PutOptions;
import org.jclouds.blobstore.binders.BindBlobToMultipartFormTest; import org.jclouds.blobstore.binders.BindBlobToMultipartFormTest;
import org.jclouds.blobstore.functions.ThrowContainerNotFoundOn404; import org.jclouds.blobstore.functions.ThrowContainerNotFoundOn404;
import org.jclouds.blobstore.functions.ThrowKeyNotFoundOn404; import org.jclouds.blobstore.functions.ThrowKeyNotFoundOn404;
@ -49,6 +50,7 @@ import org.jclouds.rest.ConfiguresRestClient;
import org.jclouds.rest.RestClientTest; import org.jclouds.rest.RestClientTest;
import org.jclouds.rest.RestContextFactory; import org.jclouds.rest.RestContextFactory;
import org.jclouds.rest.RestContextSpec; import org.jclouds.rest.RestContextSpec;
import org.jclouds.rest.functions.ReturnFalseOnNotFoundOr404;
import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404; import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;
import org.jclouds.rest.functions.ReturnVoidOnNotFoundOr404; import org.jclouds.rest.functions.ReturnVoidOnNotFoundOr404;
import org.jclouds.rest.internal.RestAnnotationProcessor; import org.jclouds.rest.internal.RestAnnotationProcessor;
@ -71,8 +73,7 @@ public class AtmosAsyncClientTest extends RestClientTest<AtmosAsyncClient> {
private BlobToObject blobToObject; private BlobToObject blobToObject;
public void testListDirectories() throws SecurityException, NoSuchMethodException, IOException { public void testListDirectories() throws SecurityException, NoSuchMethodException, IOException {
Method method = AtmosAsyncClient.class.getMethod("listDirectories", Array.newInstance(ListOptions.class, 0) Method method = AtmosAsyncClient.class.getMethod("listDirectories", ListOptions[].class);
.getClass());
HttpRequest request = processor.createRequest(method); HttpRequest request = processor.createRequest(method);
assertRequestLineEquals(request, "GET https://accesspoint.atmosonline.com/rest/namespace HTTP/1.1"); assertRequestLineEquals(request, "GET https://accesspoint.atmosonline.com/rest/namespace HTTP/1.1");
@ -87,8 +88,7 @@ public class AtmosAsyncClientTest extends RestClientTest<AtmosAsyncClient> {
} }
public void testListDirectory() throws SecurityException, NoSuchMethodException, IOException { public void testListDirectory() throws SecurityException, NoSuchMethodException, IOException {
Method method = AtmosAsyncClient.class.getMethod("listDirectory", String.class, Array.newInstance( Method method = AtmosAsyncClient.class.getMethod("listDirectory", String.class, ListOptions[].class);
ListOptions.class, 0).getClass());
HttpRequest request = processor.createRequest(method, "directory"); HttpRequest request = processor.createRequest(method, "directory");
assertRequestLineEquals(request, "GET https://accesspoint.atmosonline.com/rest/namespace/directory/ HTTP/1.1"); assertRequestLineEquals(request, "GET https://accesspoint.atmosonline.com/rest/namespace/directory/ HTTP/1.1");
@ -103,8 +103,7 @@ public class AtmosAsyncClientTest extends RestClientTest<AtmosAsyncClient> {
} }
public void testListDirectoriesOptions() throws SecurityException, NoSuchMethodException, IOException { public void testListDirectoriesOptions() throws SecurityException, NoSuchMethodException, IOException {
Method method = AtmosAsyncClient.class.getMethod("listDirectories", Array.newInstance(ListOptions.class, 0) Method method = AtmosAsyncClient.class.getMethod("listDirectories", ListOptions[].class);
.getClass());
HttpRequest request = processor.createRequest(method, new ListOptions().limit(1).token("asda")); HttpRequest request = processor.createRequest(method, new ListOptions().limit(1).token("asda"));
assertRequestLineEquals(request, "GET https://accesspoint.atmosonline.com/rest/namespace HTTP/1.1"); assertRequestLineEquals(request, "GET https://accesspoint.atmosonline.com/rest/namespace HTTP/1.1");
@ -119,8 +118,7 @@ public class AtmosAsyncClientTest extends RestClientTest<AtmosAsyncClient> {
} }
public void testListDirectoryOptions() throws SecurityException, NoSuchMethodException, IOException { public void testListDirectoryOptions() throws SecurityException, NoSuchMethodException, IOException {
Method method = AtmosAsyncClient.class.getMethod("listDirectory", String.class, Array.newInstance( Method method = AtmosAsyncClient.class.getMethod("listDirectory", String.class, ListOptions[].class);
ListOptions.class, 0).getClass());
HttpRequest request = processor.createRequest(method, "directory", new ListOptions().limit(1).token("asda")); HttpRequest request = processor.createRequest(method, "directory", new ListOptions().limit(1).token("asda"));
assertRequestLineEquals(request, "GET https://accesspoint.atmosonline.com/rest/namespace/directory/ HTTP/1.1"); assertRequestLineEquals(request, "GET https://accesspoint.atmosonline.com/rest/namespace/directory/ HTTP/1.1");
@ -135,7 +133,7 @@ public class AtmosAsyncClientTest extends RestClientTest<AtmosAsyncClient> {
} }
public void testCreateDirectory() throws SecurityException, NoSuchMethodException, IOException { public void testCreateDirectory() throws SecurityException, NoSuchMethodException, IOException {
Method method = AtmosAsyncClient.class.getMethod("createDirectory", String.class); Method method = AtmosAsyncClient.class.getMethod("createDirectory", String.class, PutOptions[].class);
HttpRequest request = processor.createRequest(method, "dir"); HttpRequest request = processor.createRequest(method, "dir");
assertRequestLineEquals(request, "POST https://accesspoint.atmosonline.com/rest/namespace/dir/ HTTP/1.1"); assertRequestLineEquals(request, "POST https://accesspoint.atmosonline.com/rest/namespace/dir/ HTTP/1.1");
@ -149,8 +147,25 @@ public class AtmosAsyncClientTest extends RestClientTest<AtmosAsyncClient> {
checkFilters(request); checkFilters(request);
} }
public void testCreateDirectoryOptions() throws SecurityException, NoSuchMethodException, IOException {
Method method = AtmosAsyncClient.class.getMethod("createDirectory", String.class, PutOptions[].class);
HttpRequest request = processor.createRequest(method, "dir", PutOptions.Builder.publicRead());
assertRequestLineEquals(request, "POST https://accesspoint.atmosonline.com/rest/namespace/dir/ HTTP/1.1");
assertNonPayloadHeadersEqual(request, HttpHeaders.ACCEPT
+ ": */*\nx-emc-groupacl: other=READ\nx-emc-useracl: root=FULL_CONTROL\n");
assertPayloadEquals(request, "", "application/octet-stream", false);
assertResponseParserClassEquals(method, request, ParseURIFromListOrLocationHeaderIf20x.class);
assertSaxResponseParserClassEquals(method, null);
assertExceptionParserClassEquals(method, ReturnEndpointIfAlreadyExists.class);
checkFilters(request);
}
public void testCreateFile() throws SecurityException, NoSuchMethodException, IOException { public void testCreateFile() throws SecurityException, NoSuchMethodException, IOException {
Method method = AtmosAsyncClient.class.getMethod("createFile", String.class, AtmosObject.class); Method method = AtmosAsyncClient.class.getMethod("createFile", String.class, AtmosObject.class,
PutOptions[].class);
HttpRequest request = processor.createRequest(method, "dir", blobToObject HttpRequest request = processor.createRequest(method, "dir", blobToObject
.apply(BindBlobToMultipartFormTest.TEST_BLOB)); .apply(BindBlobToMultipartFormTest.TEST_BLOB));
@ -165,8 +180,27 @@ public class AtmosAsyncClientTest extends RestClientTest<AtmosAsyncClient> {
checkFilters(request); checkFilters(request);
} }
public void testCreateFileOptions() throws SecurityException, NoSuchMethodException, IOException {
Method method = AtmosAsyncClient.class.getMethod("createFile", String.class, AtmosObject.class,
PutOptions[].class);
HttpRequest request = processor.createRequest(method, "dir", blobToObject
.apply(BindBlobToMultipartFormTest.TEST_BLOB), PutOptions.Builder.publicRead());
assertRequestLineEquals(request, "POST https://accesspoint.atmosonline.com/rest/namespace/dir/hello HTTP/1.1");
assertNonPayloadHeadersEqual(request, HttpHeaders.ACCEPT
+ ": */*\nx-emc-groupacl: other=READ\nx-emc-useracl: root=FULL_CONTROL\n");
assertPayloadEquals(request, "hello", "text/plain", false);
assertResponseParserClassEquals(method, request, ParseURIFromListOrLocationHeaderIf20x.class);
assertSaxResponseParserClassEquals(method, null);
assertExceptionParserClassEquals(method, null);
checkFilters(request);
}
public void testUpdateFile() throws SecurityException, NoSuchMethodException, IOException { public void testUpdateFile() throws SecurityException, NoSuchMethodException, IOException {
Method method = AtmosAsyncClient.class.getMethod("updateFile", String.class, AtmosObject.class); Method method = AtmosAsyncClient.class.getMethod("updateFile", String.class, AtmosObject.class,
PutOptions[].class);
HttpRequest request = processor.createRequest(method, "dir", blobToObject HttpRequest request = processor.createRequest(method, "dir", blobToObject
.apply(BindBlobToMultipartFormTest.TEST_BLOB)); .apply(BindBlobToMultipartFormTest.TEST_BLOB));
@ -181,6 +215,24 @@ public class AtmosAsyncClientTest extends RestClientTest<AtmosAsyncClient> {
checkFilters(request); checkFilters(request);
} }
public void testUpdateFileOptions() throws SecurityException, NoSuchMethodException, IOException {
Method method = AtmosAsyncClient.class.getMethod("updateFile", String.class, AtmosObject.class,
PutOptions[].class);
HttpRequest request = processor.createRequest(method, "dir", blobToObject
.apply(BindBlobToMultipartFormTest.TEST_BLOB), PutOptions.Builder.publicRead());
assertRequestLineEquals(request, "PUT https://accesspoint.atmosonline.com/rest/namespace/dir/hello HTTP/1.1");
assertNonPayloadHeadersEqual(request, HttpHeaders.ACCEPT
+ ": */*\nx-emc-groupacl: other=READ\nx-emc-useracl: root=FULL_CONTROL\n");
assertPayloadEquals(request, "hello", "text/plain", false);
assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class);
assertSaxResponseParserClassEquals(method, null);
assertExceptionParserClassEquals(method, ThrowKeyNotFoundOn404.class);
checkFilters(request);
}
public void testReadFile() throws SecurityException, NoSuchMethodException, IOException { public void testReadFile() throws SecurityException, NoSuchMethodException, IOException {
Method method = AtmosAsyncClient.class.getMethod("readFile", String.class, GetOptions[].class); Method method = AtmosAsyncClient.class.getMethod("readFile", String.class, GetOptions[].class);
HttpRequest request = processor.createRequest(method, "dir/file"); HttpRequest request = processor.createRequest(method, "dir/file");
@ -226,6 +278,21 @@ public class AtmosAsyncClientTest extends RestClientTest<AtmosAsyncClient> {
checkFilters(request); checkFilters(request);
} }
public void testIsPublic() throws SecurityException, NoSuchMethodException, IOException {
Method method = AtmosAsyncClient.class.getMethod("isPublic", String.class);
HttpRequest request = processor.createRequest(method, "dir/file");
assertRequestLineEquals(request, "HEAD https://accesspoint.atmosonline.com/rest/namespace/dir/file HTTP/1.1");
assertNonPayloadHeadersEqual(request, HttpHeaders.ACCEPT + ": */*\n");
assertPayloadEquals(request, null, null, false);
assertResponseParserClassEquals(method, request, ReturnTrueIfGroupACLIsOtherRead.class);
assertSaxResponseParserClassEquals(method, null);
assertExceptionParserClassEquals(method, ReturnFalseOnNotFoundOr404.class);
checkFilters(request);
}
public void testNewObject() throws SecurityException, NoSuchMethodException, IOException { public void testNewObject() throws SecurityException, NoSuchMethodException, IOException {
Method method = AtmosAsyncClient.class.getMethod("newObject"); Method method = AtmosAsyncClient.class.getMethod("newObject");
assertEquals(method.getReturnType(), AtmosObject.class); assertEquals(method.getReturnType(), AtmosObject.class);

View File

@ -53,7 +53,7 @@ import com.google.common.collect.Sets;
* *
* @author Adrian Cole * @author Adrian Cole
*/ */
@Test(groups = "live", sequential = true) @Test(groups = "live", singleThreaded = true)
public class AtmosClientLiveTest extends BaseBlobStoreIntegrationTest { public class AtmosClientLiveTest extends BaseBlobStoreIntegrationTest {
public AtmosClient getApi() { public AtmosClient getApi() {

View File

@ -42,6 +42,7 @@ import org.jclouds.atmos.domain.DirectoryEntry;
import org.jclouds.atmos.domain.SystemMetadata; import org.jclouds.atmos.domain.SystemMetadata;
import org.jclouds.atmos.domain.UserMetadata; import org.jclouds.atmos.domain.UserMetadata;
import org.jclouds.atmos.options.ListOptions; import org.jclouds.atmos.options.ListOptions;
import org.jclouds.atmos.options.PutOptions;
import org.jclouds.blobstore.TransientAsyncBlobStore; import org.jclouds.blobstore.TransientAsyncBlobStore;
import org.jclouds.blobstore.domain.Blob; import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.domain.BlobMetadata; import org.jclouds.blobstore.domain.BlobMetadata;
@ -87,7 +88,8 @@ public class StubAtmosAsyncClient implements AtmosAsyncClient {
this.service = service; this.service = service;
} }
public ListenableFuture<URI> createDirectory(String directoryName) { @Override
public ListenableFuture<URI> createDirectory(String directoryName, PutOptions... options) {
final String container; final String container;
final String path; final String path;
if (directoryName.indexOf('/') != -1) { if (directoryName.indexOf('/') != -1) {
@ -112,7 +114,8 @@ public class StubAtmosAsyncClient implements AtmosAsyncClient {
}, service); }, service);
} }
public ListenableFuture<URI> createFile(String parent, AtmosObject object) { @Override
public ListenableFuture<URI> createFile(String parent, AtmosObject object, PutOptions... options) {
final String uri = "http://stub/containers/" + parent + "/" + object.getContentMetadata().getName(); final String uri = "http://stub/containers/" + parent + "/" + object.getContentMetadata().getName();
String file = object.getContentMetadata().getName(); String file = object.getContentMetadata().getName();
String container = parent; String container = parent;
@ -132,6 +135,7 @@ public class StubAtmosAsyncClient implements AtmosAsyncClient {
}, service); }, service);
} }
@Override
public ListenableFuture<Void> deletePath(String path) { public ListenableFuture<Void> deletePath(String path) {
if (path.indexOf('/') == path.length() - 1) { if (path.indexOf('/') == path.length() - 1) {
// chop off the trailing slash // chop off the trailing slash
@ -150,10 +154,12 @@ public class StubAtmosAsyncClient implements AtmosAsyncClient {
} }
} }
@Override
public ListenableFuture<SystemMetadata> getSystemMetadata(String path) { public ListenableFuture<SystemMetadata> getSystemMetadata(String path) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override
public ListenableFuture<UserMetadata> getUserMetadata(String path) { public ListenableFuture<UserMetadata> getUserMetadata(String path) {
if (path.indexOf('/') == -1) if (path.indexOf('/') == -1)
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
@ -168,6 +174,7 @@ public class StubAtmosAsyncClient implements AtmosAsyncClient {
} }
} }
@Override
public ListenableFuture<AtmosObject> headFile(String path) { public ListenableFuture<AtmosObject> headFile(String path) {
String container = path.substring(0, path.indexOf('/')); String container = path.substring(0, path.indexOf('/'));
path = path.substring(path.indexOf('/') + 1); path = path.substring(path.indexOf('/') + 1);
@ -178,12 +185,14 @@ public class StubAtmosAsyncClient implements AtmosAsyncClient {
} }
} }
@Override
public ListenableFuture<BoundedSet<? extends DirectoryEntry>> listDirectories(ListOptions... optionsList) { public ListenableFuture<BoundedSet<? extends DirectoryEntry>> listDirectories(ListOptions... optionsList) {
// org.jclouds.blobstore.options.ListOptions options = container2ContainerListOptions // org.jclouds.blobstore.options.ListOptions options = container2ContainerListOptions
// .apply(optionsList); // .apply(optionsList);
return Futures.compose(blobStore.list(), resource2ObjectList, service); return Futures.compose(blobStore.list(), resource2ObjectList, service);
} }
@Override
public ListenableFuture<BoundedSet<? extends DirectoryEntry>> listDirectory(String directoryName, public ListenableFuture<BoundedSet<? extends DirectoryEntry>> listDirectory(String directoryName,
ListOptions... optionsList) { ListOptions... optionsList) {
org.jclouds.blobstore.options.ListContainerOptions options = container2ContainerListOptions.apply(optionsList); org.jclouds.blobstore.options.ListContainerOptions options = container2ContainerListOptions.apply(optionsList);
@ -197,10 +206,12 @@ public class StubAtmosAsyncClient implements AtmosAsyncClient {
return Futures.compose(blobStore.list(container, options), resource2ObjectList, service); return Futures.compose(blobStore.list(container, options), resource2ObjectList, service);
} }
@Override
public AtmosObject newObject() { public AtmosObject newObject() {
return this.objectProvider.create(null); return this.objectProvider.create(null);
} }
@Override
public ListenableFuture<Boolean> pathExists(final String path) { public ListenableFuture<Boolean> pathExists(final String path) {
if (path.indexOf('/') == path.length() - 1) { if (path.indexOf('/') == path.length() - 1) {
// chop off the trailing slash // chop off the trailing slash
@ -218,6 +229,7 @@ public class StubAtmosAsyncClient implements AtmosAsyncClient {
} }
} }
@Override
public ListenableFuture<AtmosObject> readFile(String path, GetOptions... options) { public ListenableFuture<AtmosObject> readFile(String path, GetOptions... options) {
String container = path.substring(0, path.indexOf('/')); String container = path.substring(0, path.indexOf('/'));
String blobName = path.substring(path.indexOf('/') + 1); String blobName = path.substring(path.indexOf('/') + 1);
@ -225,7 +237,13 @@ public class StubAtmosAsyncClient implements AtmosAsyncClient {
return Futures.compose(blobStore.getBlob(container, blobName, getOptions), blob2Object, service); return Futures.compose(blobStore.getBlob(container, blobName, getOptions), blob2Object, service);
} }
public ListenableFuture<Void> updateFile(String parent, AtmosObject object) { @Override
public ListenableFuture<Void> updateFile(String parent, AtmosObject object, PutOptions... options) {
throw new UnsupportedOperationException();
}
@Override
public ListenableFuture<Boolean> isPublic(String path) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }

View File

@ -47,7 +47,9 @@ public class BaseContainerLiveTest extends BaseBlobStoreIntegrationTest {
BlobMetadata metadata = context.getBlobStore().blobMetadata(containerName, "hello"); BlobMetadata metadata = context.getBlobStore().blobMetadata(containerName, "hello");
assertEquals(Strings2.toStringAndClose(metadata.getPublicUri().toURL().openStream()), TEST_STRING); assert metadata.getPublicUri() != null : metadata;
assertEquals(Strings2.toStringAndClose(context.utils().http().get(metadata.getPublicUri())), TEST_STRING);
} finally { } finally {
// this container is now public, so we can't reuse it directly // this container is now public, so we can't reuse it directly