mirror of https://github.com/apache/jclouds.git
Issue 111: first support for atmos online blobstore
git-svn-id: http://jclouds.googlecode.com/svn/trunk@2038 3d8758e0-26b5-11de-8745-db77d3ebf521
This commit is contained in:
parent
830d90874d
commit
6b32efa17f
|
@ -44,5 +44,25 @@
|
|||
<developerConnection>scm:svn:https://jclouds.googlecode.com/svn/trunk/mezo/saas/core</developerConnection>
|
||||
<url>http://jclouds.googlecode.com/svn/trunk/mezo/saas/core</url>
|
||||
</scm>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>integration</id>
|
||||
<phase>integration-test</phase>
|
||||
<goals>
|
||||
<goal>test</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<threadCount>1</threadCount>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
|
|
|
@ -24,33 +24,43 @@
|
|||
package org.jclouds.atmosonline.saas;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.SortedSet;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.DELETE;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.HEAD;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.PUT;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
|
||||
import org.jclouds.atmosonline.saas.binders.BindAtmosObjectToEntityAndMetadataToHeaders;
|
||||
import org.jclouds.atmosonline.saas.domain.AtmosObject;
|
||||
import org.jclouds.atmosonline.saas.domain.BoundedSortedSet;
|
||||
import org.jclouds.atmosonline.saas.domain.DirectoryEntry;
|
||||
import org.jclouds.atmosonline.saas.domain.SystemMetadata;
|
||||
import org.jclouds.atmosonline.saas.domain.UserMetadata;
|
||||
import org.jclouds.atmosonline.saas.filters.SignRequest;
|
||||
import org.jclouds.atmosonline.saas.functions.AtmosObjectName;
|
||||
import org.jclouds.atmosonline.saas.functions.ParseDirectoryListFromContentAndHeaders;
|
||||
import org.jclouds.atmosonline.saas.functions.ParseObjectFromHeadersAndHttpContent;
|
||||
import org.jclouds.atmosonline.saas.xml.ListDirectoryResponseHandler;
|
||||
import org.jclouds.atmosonline.saas.functions.ParseSystemMetadataFromHeaders;
|
||||
import org.jclouds.atmosonline.saas.functions.ReturnEndpointIfAlreadyExists;
|
||||
import org.jclouds.atmosonline.saas.options.ListOptions;
|
||||
import org.jclouds.blobstore.functions.ReturnVoidOnNotFoundOr404;
|
||||
import org.jclouds.blobstore.functions.ThrowKeyNotFoundOn404;
|
||||
import org.jclouds.http.functions.ReturnFalseOn404;
|
||||
import org.jclouds.http.options.GetOptions;
|
||||
import org.jclouds.rest.annotations.BinderParam;
|
||||
import org.jclouds.rest.annotations.Endpoint;
|
||||
import org.jclouds.rest.annotations.ExceptionParser;
|
||||
import org.jclouds.rest.annotations.ParamParser;
|
||||
import org.jclouds.rest.annotations.QueryParams;
|
||||
import org.jclouds.rest.annotations.RequestFilters;
|
||||
import org.jclouds.rest.annotations.ResponseParser;
|
||||
import org.jclouds.rest.annotations.SkipEncoding;
|
||||
import org.jclouds.rest.annotations.XMLResponseParser;
|
||||
|
||||
/**
|
||||
* Provides access to EMC Atmos Online Storage resources via their REST API.
|
||||
|
@ -68,20 +78,23 @@ public interface AtmosStorageClient {
|
|||
|
||||
@GET
|
||||
@Path("/rest/namespace")
|
||||
@XMLResponseParser(ListDirectoryResponseHandler.class)
|
||||
@ResponseParser(ParseDirectoryListFromContentAndHeaders.class)
|
||||
@Consumes(MediaType.TEXT_XML)
|
||||
SortedSet<DirectoryEntry> listDirectories();
|
||||
Future<? extends BoundedSortedSet<? extends DirectoryEntry>> listDirectories(
|
||||
ListOptions... options);
|
||||
|
||||
@GET
|
||||
@Path("/rest/namespace/{directoryName}/")
|
||||
@XMLResponseParser(ListDirectoryResponseHandler.class)
|
||||
@ResponseParser(ParseDirectoryListFromContentAndHeaders.class)
|
||||
@Consumes(MediaType.TEXT_XML)
|
||||
SortedSet<DirectoryEntry> listDirectory(@PathParam("directoryName") String directoryName);
|
||||
Future<? extends BoundedSortedSet<? extends DirectoryEntry>> listDirectory(
|
||||
@PathParam("directoryName") String directoryName, ListOptions... options);
|
||||
|
||||
@POST
|
||||
@Path("/rest/namespace/{directoryName}/")
|
||||
@ExceptionParser(ReturnEndpointIfAlreadyExists.class)
|
||||
@Consumes(MediaType.WILDCARD)
|
||||
URI createDirectory(@PathParam("directoryName") String directoryName);
|
||||
Future<URI> createDirectory(@PathParam("directoryName") String directoryName);
|
||||
|
||||
@POST
|
||||
@Path("/rest/namespace/{parent}/{name}")
|
||||
|
@ -90,12 +103,63 @@ public interface AtmosStorageClient {
|
|||
@PathParam("parent") String parent,
|
||||
@PathParam("name") @ParamParser(AtmosObjectName.class) @BinderParam(BindAtmosObjectToEntityAndMetadataToHeaders.class) AtmosObject object);
|
||||
|
||||
@PUT
|
||||
@Path("/rest/namespace/{parent}/{name}")
|
||||
@ExceptionParser(ThrowKeyNotFoundOn404.class)
|
||||
@Consumes(MediaType.WILDCARD)
|
||||
Future<Void> updateFile(
|
||||
@PathParam("parent") String parent,
|
||||
@PathParam("name") @ParamParser(AtmosObjectName.class) @BinderParam(BindAtmosObjectToEntityAndMetadataToHeaders.class) AtmosObject object);
|
||||
|
||||
@GET
|
||||
@ResponseParser(ParseObjectFromHeadersAndHttpContent.class)
|
||||
@ExceptionParser(ThrowKeyNotFoundOn404.class)
|
||||
@Path("/rest/namespace/{path}")
|
||||
@Consumes(MediaType.WILDCARD)
|
||||
Future<AtmosObject> readFile(@PathParam("path") String path, GetOptions... options);
|
||||
|
||||
@HEAD
|
||||
@ResponseParser(ParseObjectFromHeadersAndHttpContent.class)
|
||||
@ExceptionParser(ThrowKeyNotFoundOn404.class)
|
||||
@Path("/rest/namespace/{path}")
|
||||
@Consumes(MediaType.WILDCARD)
|
||||
AtmosObject headFile(@PathParam("path") String path);
|
||||
|
||||
@HEAD
|
||||
@ResponseParser(ParseSystemMetadataFromHeaders.class)
|
||||
@ExceptionParser(ThrowKeyNotFoundOn404.class)
|
||||
// currently throws 403 errors @QueryParams(keys = "metadata/system")
|
||||
@Path("/rest/namespace/{path}")
|
||||
@Consumes(MediaType.WILDCARD)
|
||||
SystemMetadata getSystemMetadata(@PathParam("path") String path);
|
||||
|
||||
@HEAD
|
||||
@ResponseParser(ParseSystemMetadataFromHeaders.class)
|
||||
@ExceptionParser(ThrowKeyNotFoundOn404.class)
|
||||
@Path("/rest/namespace/{path}")
|
||||
@QueryParams(keys = "metadata/user")
|
||||
@Consumes(MediaType.WILDCARD)
|
||||
UserMetadata getUserMetadata(@PathParam("path") String path);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param path
|
||||
* @return
|
||||
* @throws AtmosStorageResponseException
|
||||
* , if the path is a directory and not empty
|
||||
*/
|
||||
@DELETE
|
||||
@ExceptionParser(ReturnVoidOnNotFoundOr404.class)
|
||||
@Path("/rest/namespace/{path}")
|
||||
@Consumes(MediaType.WILDCARD)
|
||||
Future<Void> deletePath(@PathParam("path") String path);
|
||||
|
||||
@HEAD
|
||||
@ExceptionParser(ReturnFalseOn404.class)
|
||||
@Path("/rest/namespace/{path}")
|
||||
@Consumes(MediaType.WILDCARD)
|
||||
boolean pathExists(@PathParam("path") String path);
|
||||
|
||||
// signature currently doesn't work
|
||||
// @POST
|
||||
// @QueryParams(keys = "acl")
|
||||
|
|
|
@ -0,0 +1,222 @@
|
|||
/**
|
||||
*
|
||||
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||
*
|
||||
* ====================================================================
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.atmosonline.saas.blobstore;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static org.jclouds.blobstore.options.ListContainerOptions.Builder.recursive;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
||||
import org.jclouds.atmosonline.saas.AtmosStorageClient;
|
||||
import org.jclouds.atmosonline.saas.blobstore.functions.BlobStoreListOptionsToListOptions;
|
||||
import org.jclouds.atmosonline.saas.blobstore.functions.BlobToObject;
|
||||
import org.jclouds.atmosonline.saas.blobstore.functions.DirectoryEntryListToResourceMetadataList;
|
||||
import org.jclouds.atmosonline.saas.blobstore.functions.ObjectToBlob;
|
||||
import org.jclouds.atmosonline.saas.blobstore.functions.ObjectToBlobMetadata;
|
||||
import org.jclouds.atmosonline.saas.domain.AtmosObject;
|
||||
import org.jclouds.atmosonline.saas.options.ListOptions;
|
||||
import org.jclouds.blobstore.BlobStore;
|
||||
import org.jclouds.blobstore.attr.ConsistencyModel;
|
||||
import org.jclouds.blobstore.attr.ConsistencyModels;
|
||||
import org.jclouds.blobstore.domain.Blob;
|
||||
import org.jclouds.blobstore.domain.BlobMetadata;
|
||||
import org.jclouds.blobstore.domain.ListContainerResponse;
|
||||
import org.jclouds.blobstore.domain.ListResponse;
|
||||
import org.jclouds.blobstore.domain.ResourceMetadata;
|
||||
import org.jclouds.blobstore.functions.BlobToHttpGetOptions;
|
||||
import org.jclouds.blobstore.reference.BlobStoreConstants;
|
||||
import org.jclouds.blobstore.strategy.ClearListStrategy;
|
||||
import org.jclouds.concurrent.FutureFunctionCallable;
|
||||
import org.jclouds.concurrent.FutureFunctionWrapper;
|
||||
import org.jclouds.http.HttpUtils;
|
||||
import org.jclouds.http.options.GetOptions;
|
||||
import org.jclouds.logging.Logger.LoggerFactory;
|
||||
import org.jclouds.util.Utils;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Supplier;
|
||||
|
||||
@ConsistencyModel(ConsistencyModels.EVENTUAL)
|
||||
public class AtmosBlobStore implements BlobStore {
|
||||
private final AtmosStorageClient connection;
|
||||
private final Blob.Factory blobFactory;
|
||||
private final LoggerFactory logFactory;
|
||||
private final ClearListStrategy clearContainerStrategy;
|
||||
private final ObjectToBlobMetadata object2BlobMd;
|
||||
private final ObjectToBlob object2Blob;
|
||||
private final BlobToObject blob2Object;
|
||||
private final BlobStoreListOptionsToListOptions container2ContainerListOptions;
|
||||
private final BlobToHttpGetOptions blob2ObjectGetOptions;
|
||||
private final DirectoryEntryListToResourceMetadataList container2ResourceList;
|
||||
private final ExecutorService service;
|
||||
|
||||
@Inject(optional = true)
|
||||
@Named(BlobStoreConstants.PROPERTY_BLOBSTORE_TIMEOUT)
|
||||
protected long requestTimeoutMilliseconds = 30000;
|
||||
|
||||
@Inject
|
||||
private AtmosBlobStore(AtmosStorageClient connection, Blob.Factory blobFactory,
|
||||
LoggerFactory logFactory, ClearListStrategy clearContainerStrategy,
|
||||
ObjectToBlobMetadata object2BlobMd, ObjectToBlob object2Blob, BlobToObject blob2Object,
|
||||
BlobStoreListOptionsToListOptions container2ContainerListOptions,
|
||||
BlobToHttpGetOptions blob2ObjectGetOptions,
|
||||
DirectoryEntryListToResourceMetadataList container2ResourceList, ExecutorService service) {
|
||||
this.connection = checkNotNull(connection, "connection");
|
||||
this.blobFactory = checkNotNull(blobFactory, "blobFactory");
|
||||
this.logFactory = checkNotNull(logFactory, "logFactory");
|
||||
this.clearContainerStrategy = checkNotNull(clearContainerStrategy, "clearContainerStrategy");
|
||||
this.object2BlobMd = checkNotNull(object2BlobMd, "object2BlobMd");
|
||||
this.object2Blob = checkNotNull(object2Blob, "object2Blob");
|
||||
this.blob2Object = checkNotNull(blob2Object, "blob2Object");
|
||||
this.container2ContainerListOptions = checkNotNull(container2ContainerListOptions,
|
||||
"container2ContainerListOptions");
|
||||
this.blob2ObjectGetOptions = checkNotNull(blob2ObjectGetOptions, "blob2ObjectGetOptions");
|
||||
this.container2ResourceList = checkNotNull(container2ResourceList, "container2ResourceList");
|
||||
this.service = checkNotNull(service, "service");
|
||||
}
|
||||
|
||||
protected <F, T> Future<T> wrapFuture(Future<? extends F> future, Function<F, T> function) {
|
||||
return new FutureFunctionWrapper<F, T>(future, function, logFactory.getLogger(function
|
||||
.getClass().getName()));
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation uses the AtmosStorage HEAD Object command to return the result
|
||||
*/
|
||||
public BlobMetadata blobMetadata(String container, String key) {
|
||||
return object2BlobMd.apply(connection.headFile(container + "/" + key));
|
||||
}
|
||||
|
||||
public Future<Void> clearContainer(final String container) {
|
||||
return service.submit(new Callable<Void>() {
|
||||
|
||||
public Void call() throws Exception {
|
||||
clearContainerStrategy.execute(container, recursive());
|
||||
return null;
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
public Future<Boolean> createContainer(String container) {
|
||||
return wrapFuture(connection.createDirectory(container), new Function<URI, Boolean>() {
|
||||
|
||||
public Boolean apply(URI from) {
|
||||
return true;// no etag
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
public Future<Void> deleteContainer(final String container) {
|
||||
return service.submit(new Callable<Void>() {
|
||||
|
||||
public Void call() throws Exception {
|
||||
clearContainerStrategy.execute(container, recursive());
|
||||
connection.deletePath(container).get();
|
||||
if (!Utils.enventuallyTrue(new Supplier<Boolean>() {
|
||||
public Boolean get() {
|
||||
return !connection.pathExists(container);
|
||||
}
|
||||
}, requestTimeoutMilliseconds)) {
|
||||
throw new IllegalStateException(container + " still exists after deleting!");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
public boolean exists(String container) {
|
||||
return connection.pathExists(container);
|
||||
}
|
||||
|
||||
public Future<Blob> getBlob(String container, String key,
|
||||
org.jclouds.blobstore.options.GetOptions... optionsList) {
|
||||
GetOptions httpOptions = blob2ObjectGetOptions.apply(optionsList);
|
||||
Future<AtmosObject> returnVal = connection.readFile(container + "/" + key, httpOptions);
|
||||
return wrapFuture(returnVal, object2Blob);
|
||||
}
|
||||
|
||||
public Future<? extends ListResponse<? extends ResourceMetadata>> list() {
|
||||
return wrapFuture(connection.listDirectories(), container2ResourceList);
|
||||
}
|
||||
|
||||
public Future<? extends ListContainerResponse<? extends ResourceMetadata>> list(
|
||||
String container, org.jclouds.blobstore.options.ListContainerOptions... optionsList) {
|
||||
if (optionsList.length == 1) {
|
||||
if (!optionsList[0].isRecursive()) {
|
||||
throw new UnsupportedOperationException("recursive not currently supported in emcsaas");
|
||||
}
|
||||
if (optionsList[0].getPath() != null) {
|
||||
container = container + "/" + optionsList[0].getPath();
|
||||
}
|
||||
}
|
||||
ListOptions nativeOptions = container2ContainerListOptions.apply(optionsList);
|
||||
return wrapFuture(connection.listDirectory(container, nativeOptions), container2ResourceList);
|
||||
}
|
||||
|
||||
public Future<String> putBlob(final String container, final Blob blob) {
|
||||
final String path = container + "/" + blob.getMetadata().getName();
|
||||
|
||||
Callable<String> valueCallable = new FutureFunctionCallable<Void, String>(connection
|
||||
.deletePath(path), new Function<Void, String>() {
|
||||
|
||||
public String apply(Void from) {
|
||||
boolean exists = connection.pathExists(path);
|
||||
if (!exists)
|
||||
try {
|
||||
if (blob.getMetadata().getContentMD5() != null)
|
||||
blob.getMetadata().getUserMetadata().put("content-md5",
|
||||
HttpUtils.toHexString(blob.getMetadata().getContentMD5()));
|
||||
connection.createFile(container, blob2Object.apply(blob)).get();
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (ExecutionException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
});
|
||||
return service.submit(valueCallable);
|
||||
|
||||
}
|
||||
|
||||
public Future<Void> removeBlob(String container, String key) {
|
||||
return connection.deletePath(container + "/" + key);
|
||||
}
|
||||
|
||||
public Blob newBlob() {
|
||||
return blobFactory.create(null);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
/**
|
||||
*
|
||||
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||
*
|
||||
* ====================================================================
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.atmosonline.saas.blobstore;
|
||||
|
||||
import static org.jclouds.atmosonline.saas.reference.AtmosStorageConstants.PROPERTY_EMCSAAS_RETRY;
|
||||
import static org.jclouds.atmosonline.saas.reference.AtmosStorageConstants.PROPERTY_EMCSAAS_TIMEOUT;
|
||||
import static org.jclouds.blobstore.reference.BlobStoreConstants.PROPERTY_BLOBSTORE_RETRY;
|
||||
import static org.jclouds.blobstore.reference.BlobStoreConstants.PROPERTY_USER_METADATA_PREFIX;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
import org.jclouds.atmosonline.saas.AtmosStorageClient;
|
||||
import org.jclouds.atmosonline.saas.blobstore.config.AtmosBlobStoreContextModule;
|
||||
import org.jclouds.atmosonline.saas.config.AtmosStorageRestClientModule;
|
||||
import org.jclouds.blobstore.BlobStoreContextBuilder;
|
||||
import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule;
|
||||
import org.jclouds.logging.jdk.config.JDKLoggingModule;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.Module;
|
||||
import com.google.inject.TypeLiteral;
|
||||
|
||||
/**
|
||||
* Creates {@link AtmosBlobStoreContext} or {@link Injector} instances based on the most commonly
|
||||
* requested arguments.
|
||||
* <p/>
|
||||
* Note that Threadsafe objects will be bound as singletons to the Injector or Context provided.
|
||||
* <p/>
|
||||
* <p/>
|
||||
* If no <code>Module</code>s are specified, the default {@link JDKLoggingModule logging} and
|
||||
* {@link JavaUrlHttpCommandExecutorServiceModule http transports} will be installed.
|
||||
*
|
||||
* @author Adrian Cole, Andrew Newdigate
|
||||
* @see AtmosBlobStoreContext
|
||||
*/
|
||||
public class AtmosBlobStoreContextBuilder extends BlobStoreContextBuilder<AtmosStorageClient> {
|
||||
|
||||
public AtmosBlobStoreContextBuilder(Properties props) {
|
||||
super(new TypeLiteral<AtmosStorageClient>() {
|
||||
}, convert(props));
|
||||
}
|
||||
|
||||
private static Properties convert(Properties props) {
|
||||
for (Entry<String, String> entry : ImmutableMap.of(PROPERTY_EMCSAAS_RETRY,
|
||||
PROPERTY_BLOBSTORE_RETRY, PROPERTY_EMCSAAS_TIMEOUT, PROPERTY_USER_METADATA_PREFIX)
|
||||
.entrySet()) {
|
||||
if (props.containsKey(entry.getKey()))
|
||||
props.setProperty(entry.getValue(), props.getProperty(entry.getKey()));
|
||||
}
|
||||
return props;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AtmosBlobStoreContextBuilder withExecutorService(ExecutorService service) {
|
||||
return (AtmosBlobStoreContextBuilder) super.withExecutorService(service);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AtmosBlobStoreContextBuilder withModules(Module... modules) {
|
||||
return (AtmosBlobStoreContextBuilder) super.withModules(modules);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addContextModule(List<Module> modules) {
|
||||
modules.add(new AtmosBlobStoreContextModule());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addClientModule(List<Module> modules) {
|
||||
modules.add(new AtmosStorageRestClientModule());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/**
|
||||
*
|
||||
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||
*
|
||||
* ====================================================================
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.atmosonline.saas.blobstore;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.jclouds.atmosonline.saas.AtmosStorageClient;
|
||||
import org.jclouds.atmosonline.saas.AtmosStoragePropertiesBuilder;
|
||||
import org.jclouds.blobstore.BlobStoreContext;
|
||||
import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule;
|
||||
import org.jclouds.logging.jdk.config.JDKLoggingModule;
|
||||
|
||||
import com.google.inject.Module;
|
||||
|
||||
/**
|
||||
* Creates {@link AtmosBlobStoreContext} instances based on the most commonly requested arguments.
|
||||
* <p/>
|
||||
* Note that Threadsafe objects will be bound as singletons to the Injector or Context provided.
|
||||
* <p/>
|
||||
* <p/>
|
||||
* If no <code>Module</code>s are specified, the default {@link JDKLoggingModule logging} and
|
||||
* {@link JavaUrlHttpCommandExecutorServiceModule http transports} will be installed.
|
||||
*
|
||||
* @author Adrian Cole
|
||||
* @see AtmosBlobStoreContext
|
||||
*/
|
||||
public class AtmosBlobStoreContextFactory {
|
||||
public static BlobStoreContext<AtmosStorageClient> createContext(Properties properties,
|
||||
Module... modules) {
|
||||
return new AtmosBlobStoreContextBuilder(new AtmosStoragePropertiesBuilder(properties).build())
|
||||
.withModules(modules).buildContext();
|
||||
}
|
||||
|
||||
public static BlobStoreContext<AtmosStorageClient> createContext(String uid, String key,
|
||||
Module... modules) {
|
||||
return new AtmosBlobStoreContextBuilder(new AtmosStoragePropertiesBuilder(uid, key).build())
|
||||
.withModules(modules).buildContext();
|
||||
}
|
||||
|
||||
public static BlobStoreContext<AtmosStorageClient> createContext(URI endpoint, String uid,
|
||||
String key, Module... modules) {
|
||||
return new AtmosBlobStoreContextBuilder(new AtmosStoragePropertiesBuilder(uid, key)
|
||||
.withEndpoint(endpoint).build()).withModules(modules).buildContext();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
/**
|
||||
*
|
||||
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||
*
|
||||
* ====================================================================
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.atmosonline.saas.blobstore.config;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.jclouds.atmosonline.saas.AtmosStorage;
|
||||
import org.jclouds.atmosonline.saas.AtmosStorageClient;
|
||||
import org.jclouds.atmosonline.saas.blobstore.AtmosBlobStore;
|
||||
import org.jclouds.atmosonline.saas.blobstore.strategy.FindMD5InUserMetadata;
|
||||
import org.jclouds.atmosonline.saas.blobstore.strategy.RecursiveRemove;
|
||||
import org.jclouds.atmosonline.saas.config.AtmosObjectModule;
|
||||
import org.jclouds.atmosonline.saas.reference.AtmosStorageConstants;
|
||||
import org.jclouds.blobstore.BlobMap;
|
||||
import org.jclouds.blobstore.BlobStore;
|
||||
import org.jclouds.blobstore.BlobStoreContext;
|
||||
import org.jclouds.blobstore.InputStreamMap;
|
||||
import org.jclouds.blobstore.config.BlobStoreMapModule;
|
||||
import org.jclouds.blobstore.config.BlobStoreObjectModule;
|
||||
import org.jclouds.blobstore.internal.BlobStoreContextImpl;
|
||||
import org.jclouds.blobstore.strategy.ClearContainerStrategy;
|
||||
import org.jclouds.blobstore.strategy.ClearListStrategy;
|
||||
import org.jclouds.blobstore.strategy.ContainsValueInListStrategy;
|
||||
import org.jclouds.lifecycle.Closer;
|
||||
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Provides;
|
||||
|
||||
/**
|
||||
* Configures the {@link AtmosBlobStoreContext}; requires {@link AtmosBlobStore} bound.
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
public class AtmosBlobStoreContextModule extends AbstractModule {
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
install(new BlobStoreObjectModule());
|
||||
install(new BlobStoreMapModule());
|
||||
install(new AtmosObjectModule());
|
||||
bind(BlobStore.class).to(AtmosBlobStore.class).asEagerSingleton();
|
||||
bind(ContainsValueInListStrategy.class).to(FindMD5InUserMetadata.class);
|
||||
bind(ClearListStrategy.class).to(RecursiveRemove.class);
|
||||
bind(ClearContainerStrategy.class).to(RecursiveRemove.class);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
BlobStoreContext<AtmosStorageClient> provideContext(BlobMap.Factory blobMapFactory,
|
||||
InputStreamMap.Factory inputStreamMapFactory, Closer closer, BlobStore blobStore,
|
||||
AtmosStorageClient defaultApi, @AtmosStorage URI endPoint,
|
||||
@Named(AtmosStorageConstants.PROPERTY_EMCSAAS_UID) String account) {
|
||||
return new BlobStoreContextImpl<AtmosStorageClient>(blobMapFactory, inputStreamMapFactory,
|
||||
closer, blobStore, defaultApi, endPoint, account);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package org.jclouds.atmosonline.saas.blobstore.functions;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.jclouds.atmosonline.saas.domain.AtmosObject;
|
||||
import org.jclouds.atmosonline.saas.domain.UserMetadata;
|
||||
import org.jclouds.blobstore.domain.BlobMetadata;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
|
||||
/**
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Singleton
|
||||
public class BlobMetadataToObject implements Function<BlobMetadata, AtmosObject> {
|
||||
private final AtmosObject.Factory factory;
|
||||
private final BlobToContentMetadata blob2ContentMd;
|
||||
private final BlobToSystemMetadata blob2SysMd;
|
||||
|
||||
@Inject
|
||||
protected BlobMetadataToObject(AtmosObject.Factory factory,
|
||||
BlobToContentMetadata blob2ContentMd, BlobToSystemMetadata blob2SysMd) {
|
||||
this.factory = factory;
|
||||
this.blob2ContentMd = blob2ContentMd;
|
||||
this.blob2SysMd = blob2SysMd;
|
||||
}
|
||||
|
||||
public AtmosObject apply(BlobMetadata base) {
|
||||
UserMetadata userMd = new UserMetadata();
|
||||
if (base.getUserMetadata() != null)
|
||||
userMd.getMetadata().putAll(base.getUserMetadata());
|
||||
return factory.create(blob2ContentMd.apply(base), blob2SysMd.apply(base), userMd);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package org.jclouds.atmosonline.saas.blobstore.functions;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.jclouds.blobstore.options.ListContainerOptions;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
|
||||
/**
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Singleton
|
||||
public class BlobStoreListOptionsToListOptions implements Function<ListContainerOptions[], org.jclouds.atmosonline.saas.options.ListOptions> {
|
||||
public org.jclouds.atmosonline.saas.options.ListOptions apply(ListContainerOptions[] optionsList) {
|
||||
org.jclouds.atmosonline.saas.options.ListOptions httpOptions = new org.jclouds.atmosonline.saas.options.ListOptions();
|
||||
if (optionsList.length != 0) {
|
||||
if (optionsList[0].getMarker() != null) {
|
||||
httpOptions.token(optionsList[0].getMarker());
|
||||
}
|
||||
if (optionsList[0].getMaxResults() != null) {
|
||||
httpOptions.limit(optionsList[0].getMaxResults());
|
||||
}
|
||||
}
|
||||
return httpOptions;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package org.jclouds.atmosonline.saas.blobstore.functions;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.jclouds.atmosonline.saas.domain.MutableContentMetadata;
|
||||
import org.jclouds.blobstore.domain.BlobMetadata;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
|
||||
/**
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Singleton
|
||||
public class BlobToContentMetadata implements Function<BlobMetadata, MutableContentMetadata> {
|
||||
public MutableContentMetadata apply(BlobMetadata base) {
|
||||
MutableContentMetadata to = new MutableContentMetadata();
|
||||
to.setContentType(base.getContentType());
|
||||
to.setContentMD5(base.getContentMD5());
|
||||
to.setName(base.getName());
|
||||
if (base.getSize() != null)
|
||||
to.setContentLength(base.getSize());
|
||||
return to;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package org.jclouds.atmosonline.saas.blobstore.functions;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.jclouds.atmosonline.saas.domain.AtmosObject;
|
||||
import org.jclouds.blobstore.domain.Blob;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
|
||||
/**
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Singleton
|
||||
public class BlobToObject implements Function<Blob, AtmosObject> {
|
||||
private final BlobMetadataToObject blobMd2Object;
|
||||
|
||||
@Inject
|
||||
BlobToObject(BlobMetadataToObject blobMd2Object) {
|
||||
this.blobMd2Object = blobMd2Object;
|
||||
}
|
||||
|
||||
public AtmosObject apply(Blob from) {
|
||||
AtmosObject object = blobMd2Object.apply(from.getMetadata());
|
||||
object.setData(from.getData());
|
||||
object.setAllHeaders(from.getAllHeaders());
|
||||
return object;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package org.jclouds.atmosonline.saas.blobstore.functions;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.jclouds.atmosonline.saas.domain.FileType;
|
||||
import org.jclouds.atmosonline.saas.domain.SystemMetadata;
|
||||
import org.jclouds.blobstore.domain.BlobMetadata;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
|
||||
/**
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Singleton
|
||||
public class BlobToSystemMetadata implements Function<BlobMetadata, SystemMetadata> {
|
||||
public SystemMetadata apply(BlobMetadata base) {
|
||||
return new SystemMetadata(null, base.getLastModified(), null, null, null, 1, null, base
|
||||
.getName(), null, (base.getSize() != null) ? base.getSize() : 0, FileType.REGULAR,
|
||||
"root");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
package org.jclouds.atmosonline.saas.blobstore.functions;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.jclouds.atmosonline.saas.domain.BoundedSortedSet;
|
||||
import org.jclouds.atmosonline.saas.domain.DirectoryEntry;
|
||||
import org.jclouds.atmosonline.saas.domain.FileType;
|
||||
import org.jclouds.blobstore.domain.ListContainerResponse;
|
||||
import org.jclouds.blobstore.domain.ResourceMetadata;
|
||||
import org.jclouds.blobstore.domain.ResourceType;
|
||||
import org.jclouds.blobstore.domain.internal.BlobMetadataImpl;
|
||||
import org.jclouds.blobstore.domain.internal.ListContainerResponseImpl;
|
||||
import org.jclouds.blobstore.domain.internal.ResourceMetadataImpl;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
/**
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Singleton
|
||||
public class DirectoryEntryListToResourceMetadataList
|
||||
implements
|
||||
Function<BoundedSortedSet<? extends DirectoryEntry>, ListContainerResponse<? extends ResourceMetadata>> {
|
||||
|
||||
public ListContainerResponse<? extends ResourceMetadata> apply(
|
||||
BoundedSortedSet<? extends DirectoryEntry> from) {
|
||||
|
||||
return new ListContainerResponseImpl<ResourceMetadata>(Iterables.transform(from,
|
||||
new Function<DirectoryEntry, ResourceMetadata>() {
|
||||
|
||||
public ResourceMetadata apply(DirectoryEntry from) {
|
||||
ResourceType type = from.getType() == FileType.DIRECTORY ? ResourceType.FOLDER
|
||||
: ResourceType.BLOB;
|
||||
if (type == ResourceType.FOLDER)
|
||||
return new ResourceMetadataImpl(type, from.getObjectID(), from
|
||||
.getObjectName(), null, null, null, null, Maps
|
||||
.<String, String> newHashMap());
|
||||
else
|
||||
return new BlobMetadataImpl(from.getObjectID(), from.getObjectName(), null,
|
||||
null, null, null, Maps.<String, String> newHashMap(), null, null);
|
||||
}
|
||||
|
||||
}), null, from.getToken(),
|
||||
|
||||
null, from.getToken() != null);
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package org.jclouds.atmosonline.saas.blobstore.functions;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.jclouds.blobstore.options.ListContainerOptions;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
|
||||
/**
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Singleton
|
||||
public class ListOptionsToBlobStoreListOptions implements
|
||||
Function<org.jclouds.atmosonline.saas.options.ListOptions[], ListContainerOptions> {
|
||||
public ListContainerOptions apply(org.jclouds.atmosonline.saas.options.ListOptions[] optionsList) {
|
||||
ListContainerOptions options = new ListContainerOptions();
|
||||
if (optionsList.length != 0) {
|
||||
if (optionsList[0].getToken() != null) {
|
||||
options.afterMarker(optionsList[0].getToken());
|
||||
}
|
||||
if (optionsList[0].getLimit() != null) {
|
||||
options.maxResults(optionsList[0].getLimit());
|
||||
}
|
||||
}
|
||||
return options;
|
||||
}
|
||||
}
|
|
@ -34,10 +34,6 @@ public class ObjectToBlobMetadata implements Function<AtmosObject, MutableBlobMe
|
|||
to.setSize(from.getSystemMetadata().getSize());
|
||||
to.setType(ResourceType.BLOB);
|
||||
to.setUserMetadata(from.getUserMetadata().getMetadata());
|
||||
if (from.getContentMetadata().getContentType() != null
|
||||
&& from.getContentMetadata().getContentType().equals("application/directory")) {
|
||||
to.setType(ResourceType.RELATIVE_PATH);
|
||||
}
|
||||
return to;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package org.jclouds.atmosonline.saas.blobstore.functions;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.jclouds.atmosonline.saas.domain.BoundedSortedSet;
|
||||
import org.jclouds.atmosonline.saas.domain.DirectoryEntry;
|
||||
import org.jclouds.atmosonline.saas.domain.FileType;
|
||||
import org.jclouds.atmosonline.saas.domain.internal.BoundedTreeSet;
|
||||
import org.jclouds.blobstore.domain.ResourceMetadata;
|
||||
import org.jclouds.blobstore.domain.ResourceType;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
/**
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Singleton
|
||||
public class ResourceMetadataListToDirectoryEntryList
|
||||
implements
|
||||
Function<org.jclouds.blobstore.domain.ListResponse<? extends ResourceMetadata>, BoundedSortedSet<? extends DirectoryEntry>> {
|
||||
|
||||
public BoundedSortedSet<DirectoryEntry> apply(
|
||||
org.jclouds.blobstore.domain.ListResponse<? extends ResourceMetadata> from) {
|
||||
|
||||
return new BoundedTreeSet<DirectoryEntry>(Iterables.transform(from,
|
||||
new Function<ResourceMetadata, DirectoryEntry>() {
|
||||
public DirectoryEntry apply(ResourceMetadata from) {
|
||||
FileType type = (from.getType() == ResourceType.FOLDER || from.getType() == ResourceType.RELATIVE_PATH) ? FileType.DIRECTORY
|
||||
: FileType.REGULAR;
|
||||
return new DirectoryEntry(from.getId(), type, from.getName());
|
||||
}
|
||||
|
||||
}), from.getMarker());
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
package org.jclouds.atmosonline.saas.blobstore.strategy;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.jclouds.atmosonline.saas.AtmosStorageClient;
|
||||
import org.jclouds.atmosonline.saas.domain.UserMetadata;
|
||||
import org.jclouds.blobstore.domain.BlobMetadata;
|
||||
import org.jclouds.blobstore.functions.ObjectMD5;
|
||||
import org.jclouds.blobstore.internal.BlobRuntimeException;
|
||||
import org.jclouds.blobstore.options.ListContainerOptions;
|
||||
import org.jclouds.blobstore.strategy.ContainsValueInListStrategy;
|
||||
import org.jclouds.blobstore.strategy.ListBlobMetadataStrategy;
|
||||
import org.jclouds.http.HttpUtils;
|
||||
import org.jclouds.util.Utils;
|
||||
|
||||
/**
|
||||
* Searches Content-MD5 tag for the value associated with the value
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Singleton
|
||||
public class FindMD5InUserMetadata implements ContainsValueInListStrategy {
|
||||
|
||||
protected final ObjectMD5 objectMD5;
|
||||
protected final ListBlobMetadataStrategy getAllBlobMetadata;
|
||||
private final AtmosStorageClient client;
|
||||
|
||||
@Inject
|
||||
private FindMD5InUserMetadata(ObjectMD5 objectMD5,
|
||||
ListBlobMetadataStrategy getAllBlobMetadata, AtmosStorageClient client) {
|
||||
this.objectMD5 = objectMD5;
|
||||
this.getAllBlobMetadata = getAllBlobMetadata;
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
public boolean execute(String containerName, Object value, ListContainerOptions options) {
|
||||
try {
|
||||
byte[] toSearch = objectMD5.apply(value);
|
||||
String hex = HttpUtils.toHexString(toSearch);
|
||||
for (BlobMetadata metadata : getAllBlobMetadata.execute(containerName, options)) {
|
||||
UserMetadata properties = client.getUserMetadata(containerName+"/"+metadata.getName());
|
||||
if (hex.equals(properties.getMetadata().get("content-md5")))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (Exception e) {
|
||||
Utils.<BlobRuntimeException> rethrowIfRuntimeOrSameType(e);
|
||||
throw new BlobRuntimeException(String.format(
|
||||
"Error searching for ETAG of value: [%2$s] in container:%1$s", containerName,
|
||||
value), e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
package org.jclouds.atmosonline.saas.blobstore.strategy;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.jclouds.atmosonline.saas.AtmosStorageClient;
|
||||
import org.jclouds.atmosonline.saas.domain.DirectoryEntry;
|
||||
import org.jclouds.atmosonline.saas.domain.FileType;
|
||||
import org.jclouds.blobstore.internal.BlobRuntimeException;
|
||||
import org.jclouds.blobstore.options.ListContainerOptions;
|
||||
import org.jclouds.blobstore.reference.BlobStoreConstants;
|
||||
import org.jclouds.blobstore.strategy.ClearContainerStrategy;
|
||||
import org.jclouds.blobstore.strategy.ClearListStrategy;
|
||||
import org.jclouds.concurrent.FutureFunctionWrapper;
|
||||
import org.jclouds.logging.Logger;
|
||||
import org.jclouds.util.Utils;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
/**
|
||||
* Recursively remove a path.
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Singleton
|
||||
public class RecursiveRemove implements ClearListStrategy, ClearContainerStrategy {
|
||||
/**
|
||||
* maximum duration of an blob Request
|
||||
*/
|
||||
@Inject(optional = true)
|
||||
@Named(BlobStoreConstants.PROPERTY_BLOBSTORE_TIMEOUT)
|
||||
protected long requestTimeoutMilliseconds = 30000;
|
||||
protected final AtmosStorageClient connection;
|
||||
|
||||
@Resource
|
||||
protected Logger logger = Logger.NULL;
|
||||
|
||||
@Inject
|
||||
public RecursiveRemove(AtmosStorageClient connection) {
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
public void execute(String containerName) {
|
||||
logger.debug("clearing container ", containerName);
|
||||
execute(containerName, new ListContainerOptions().recursive());
|
||||
logger.trace("cleared container " + containerName);
|
||||
}
|
||||
|
||||
private Future<Void> rm(final String fullPath, FileType type, boolean recursive)
|
||||
throws InterruptedException, ExecutionException, TimeoutException {
|
||||
Set<Future<Void>> deletes = Sets.newHashSet();
|
||||
if ((type == FileType.DIRECTORY) && recursive) {
|
||||
for (DirectoryEntry child : connection.listDirectory(fullPath).get(10, TimeUnit.SECONDS)) {
|
||||
deletes.add(rm(fullPath + "/" + child.getObjectName(), child.getType(), true));
|
||||
}
|
||||
}
|
||||
for (Future<Void> isdeleted : deletes) {
|
||||
isdeleted.get(requestTimeoutMilliseconds, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
return new FutureFunctionWrapper<Void, Void>(connection.deletePath(fullPath),
|
||||
new Function<Void, Void>() {
|
||||
|
||||
public Void apply(Void from) {
|
||||
try {
|
||||
if (!Utils.enventuallyTrue(new Supplier<Boolean>() {
|
||||
public Boolean get() {
|
||||
return !connection.pathExists(fullPath);
|
||||
}
|
||||
}, requestTimeoutMilliseconds)) {
|
||||
throw new IllegalStateException(fullPath + " still exists after deleting!");
|
||||
}
|
||||
return null;
|
||||
} catch (InterruptedException e) {
|
||||
throw new IllegalStateException(fullPath + " still exists after deleting!",e);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
public void execute(final String containerName, ListContainerOptions options) {
|
||||
String path = containerName;
|
||||
if (options.getPath() != null)
|
||||
path += "/" + options.getPath();
|
||||
Set<Future<Void>> deletes = Sets.newHashSet();
|
||||
try {
|
||||
for (DirectoryEntry md : connection.listDirectory(path).get(requestTimeoutMilliseconds,
|
||||
TimeUnit.MILLISECONDS)) {
|
||||
deletes.add(rm(path + "/" + md.getObjectName(), md.getType(), options.isRecursive()));
|
||||
}
|
||||
for (Future<Void> isdeleted : deletes) {
|
||||
isdeleted.get(requestTimeoutMilliseconds, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Utils.<BlobRuntimeException> rethrowIfRuntimeOrSameType(e);
|
||||
throw new BlobRuntimeException("Error deleting path: " + path, e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -51,6 +51,12 @@ public class AtmosObjectModule extends AbstractModule {
|
|||
return new AtmosObjectImpl(generateMD5Result, generateMD5, calculateSize, metadataProvider
|
||||
.get(), systemMetadata, userMetadata);
|
||||
}
|
||||
|
||||
public AtmosObject create(MutableContentMetadata contentMetadata,
|
||||
SystemMetadata systemMetadata, UserMetadata userMetadata) {
|
||||
return new AtmosObjectImpl(generateMD5Result, generateMD5, calculateSize, contentMetadata,
|
||||
systemMetadata, userMetadata);
|
||||
}
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
|
|
@ -20,8 +20,12 @@ public interface AtmosObject extends Comparable<AtmosObject> {
|
|||
AtmosObject create(@Nullable MutableContentMetadata contentMetadata);
|
||||
|
||||
AtmosObject create(SystemMetadata systemMetadata, UserMetadata userMetadata);
|
||||
|
||||
AtmosObject create(MutableContentMetadata contentMetadata, SystemMetadata systemMetadata,
|
||||
UserMetadata userMetadata);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* generate an MD5 Hash for the current data.
|
||||
* <p/>
|
||||
|
@ -50,7 +54,7 @@ public interface AtmosObject extends Comparable<AtmosObject> {
|
|||
Object getData();
|
||||
|
||||
MutableContentMetadata getContentMetadata();
|
||||
|
||||
|
||||
/**
|
||||
* @return System and User metadata relevant to this object.
|
||||
*/
|
||||
|
|
|
@ -38,8 +38,8 @@ public class AtmosStorageError {
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AtmosStorageError [code=" + code + ", message=" + message + ", stringSigned="
|
||||
+ stringSigned + "]";
|
||||
return "AtmosStorageError [code=" + code + ", message=" + message
|
||||
+ (stringSigned != null ? (", stringSigned=" + stringSigned) : "") + "]";
|
||||
}
|
||||
|
||||
public AtmosStorageError(int code, String message) {
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
package org.jclouds.atmosonline.saas.domain;
|
||||
|
||||
import java.util.SortedSet;
|
||||
|
||||
import org.jclouds.atmosonline.saas.domain.internal.BoundedTreeSet;
|
||||
|
||||
import com.google.inject.ImplementedBy;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*
|
||||
*/
|
||||
@ImplementedBy(BoundedTreeSet.class)
|
||||
public interface BoundedSortedSet<T> extends SortedSet<T> {
|
||||
|
||||
String getToken();
|
||||
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package org.jclouds.atmosonline.saas.domain.internal;
|
||||
|
||||
import java.util.TreeSet;
|
||||
|
||||
import org.jclouds.atmosonline.saas.domain.BoundedSortedSet;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*
|
||||
*/
|
||||
public class BoundedTreeSet<T> extends TreeSet<T> implements BoundedSortedSet<T> {
|
||||
|
||||
/** The serialVersionUID */
|
||||
private static final long serialVersionUID = -7133632087734650835L;
|
||||
protected final String token;
|
||||
|
||||
public BoundedTreeSet(Iterable<T> contents, String token) {
|
||||
Iterables.addAll(this, contents);
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
public String getToken() {
|
||||
return token;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
package org.jclouds.atmosonline.saas.functions;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Provider;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.jclouds.atmosonline.saas.domain.BoundedSortedSet;
|
||||
import org.jclouds.atmosonline.saas.domain.DirectoryEntry;
|
||||
import org.jclouds.atmosonline.saas.domain.internal.BoundedTreeSet;
|
||||
import org.jclouds.atmosonline.saas.reference.AtmosStorageHeaders;
|
||||
import org.jclouds.atmosonline.saas.xml.ListDirectoryResponseHandler;
|
||||
import org.jclouds.http.HttpResponse;
|
||||
import org.jclouds.http.functions.ParseSax;
|
||||
import org.jclouds.http.functions.ParseSax.Factory;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
|
||||
/**
|
||||
* This parses {@link BoundedSortedSet} from HTTP headers and xml content.
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Singleton
|
||||
public class ParseDirectoryListFromContentAndHeaders implements
|
||||
Function<HttpResponse, BoundedSortedSet<DirectoryEntry>> {
|
||||
|
||||
private final ParseSax.Factory factory;
|
||||
private final Provider<ListDirectoryResponseHandler> listHandlerProvider;
|
||||
|
||||
@Inject
|
||||
private ParseDirectoryListFromContentAndHeaders(Factory factory,
|
||||
Provider<ListDirectoryResponseHandler> orgHandlerProvider) {
|
||||
this.factory = factory;
|
||||
this.listHandlerProvider = orgHandlerProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* parses the http response headers to create a new {@link BoundedSortedSet} object.
|
||||
*/
|
||||
public BoundedSortedSet<DirectoryEntry> apply(HttpResponse from) {
|
||||
String token = from.getFirstHeaderOrNull(AtmosStorageHeaders.TOKEN);
|
||||
return new BoundedTreeSet<DirectoryEntry>(factory.create(listHandlerProvider.get()).parse(
|
||||
from.getContent()), token);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package org.jclouds.atmosonline.saas.functions;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import org.jclouds.blobstore.KeyAlreadyExistsException;
|
||||
import org.jclouds.rest.InvocationContext;
|
||||
import org.jclouds.rest.internal.GeneratedHttpRequest;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
public class ReturnEndpointIfAlreadyExists implements Function<Exception, URI>, InvocationContext {
|
||||
|
||||
private URI endpoint;
|
||||
|
||||
public URI apply(Exception from) {
|
||||
if (from instanceof KeyAlreadyExistsException) {
|
||||
return endpoint;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setContext(GeneratedHttpRequest<?> request) {
|
||||
this.endpoint = request == null?null:request.getEndpoint();
|
||||
}
|
||||
|
||||
}
|
|
@ -23,12 +23,15 @@
|
|||
*/
|
||||
package org.jclouds.atmosonline.saas.handlers;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.jclouds.atmosonline.saas.AtmosStorageResponseException;
|
||||
import org.jclouds.atmosonline.saas.domain.AtmosStorageError;
|
||||
import org.jclouds.atmosonline.saas.util.AtmosStorageUtils;
|
||||
import org.jclouds.blobstore.KeyAlreadyExistsException;
|
||||
import org.jclouds.http.HttpCommand;
|
||||
import org.jclouds.http.HttpErrorHandler;
|
||||
import org.jclouds.http.HttpResponse;
|
||||
|
@ -64,7 +67,15 @@ public class ParseAtmosStorageErrorFromXmlContent implements HttpErrorHandler {
|
|||
if (content.indexOf('<') >= 0) {
|
||||
AtmosStorageError error = utils.parseAtmosStorageErrorFromContent(command,
|
||||
response, content);
|
||||
command.setException(new AtmosStorageResponseException(command, response, error));
|
||||
AtmosStorageResponseException exception = new AtmosStorageResponseException(
|
||||
command, response, error);
|
||||
if (error.getCode() == 1016) {
|
||||
File file = new File(command.getRequest().getEndpoint().getPath());
|
||||
command.setException(new KeyAlreadyExistsException(file.getParentFile()
|
||||
.getAbsolutePath(), file.getName(), exception));
|
||||
} else {
|
||||
command.setException(exception);
|
||||
}
|
||||
} else {
|
||||
command.setException(new HttpResponseException(command, response, content));
|
||||
}
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
package org.jclouds.atmosonline.saas.options;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
|
||||
import org.jclouds.http.options.BaseHttpRequestOptions;
|
||||
|
||||
/**
|
||||
* Options used to control paginated results (aka list commands).
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
public class ListOptions extends BaseHttpRequestOptions {
|
||||
public static final ListOptions NONE = new ListOptions();
|
||||
|
||||
/**
|
||||
* specifies the position to resume listing
|
||||
* <p/>
|
||||
* note this is an opaque value and should not be interpreted.
|
||||
*/
|
||||
public ListOptions token(String token) {
|
||||
this.headers.put("x-emc-token", checkNotNull(token, "x-emc-token"));
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getToken() {
|
||||
return getFirstHeaderOrNull("x-emc-token");
|
||||
}
|
||||
|
||||
/**
|
||||
* theÊmaximumÊnumberÊofÊitemsÊ thatÊshouldÊbeÊreturned. IfÊthisÊisÊÊnotÊspecified,ÊthereÊisÊno
|
||||
* limit.
|
||||
*/
|
||||
public ListOptions limit(int maxresults) {
|
||||
checkState(maxresults >= 0, "maxresults must be >= 0");
|
||||
checkState(maxresults <= 10000, "maxresults must be <= 5000");
|
||||
headers.put("x-emc-limit", Integer.toString(maxresults));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Integer getLimit() {
|
||||
String maxresults = getFirstHeaderOrNull("x-emc-limit");
|
||||
return (maxresults != null) ? new Integer(maxresults) : null;
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
||||
/**
|
||||
* @see ListOptions#token(String)
|
||||
*/
|
||||
public static ListOptions token(String token) {
|
||||
ListOptions options = new ListOptions();
|
||||
return options.token(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ListOptions#limit(int)
|
||||
*/
|
||||
public static ListOptions limit(int maxKeys) {
|
||||
ListOptions options = new ListOptions();
|
||||
return options.limit(maxKeys);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -36,4 +36,13 @@ public interface AtmosStorageConstants {
|
|||
* how long do we wait before obtaining a new timestamp for requests. Clocks must be within 5m of Atmos.
|
||||
*/
|
||||
public static final String PROPERTY_EMCSAAS_SESSIONINTERVAL = "jclouds.emcsaas.sessioninterval";
|
||||
|
||||
/**
|
||||
* longest time a single synchronous operation can take before throwing an exception.
|
||||
*/
|
||||
public static final String PROPERTY_EMCSAAS_TIMEOUT = "jclouds.emcsaas.timeout";
|
||||
/**
|
||||
* time to pause before retrying a transient failure
|
||||
*/
|
||||
public static final String PROPERTY_EMCSAAS_RETRY = "jclouds.emcsaas.retry";
|
||||
}
|
||||
|
|
|
@ -41,4 +41,6 @@ public interface AtmosStorageHeaders {
|
|||
public static final String DATE = "x-emc-date";
|
||||
public static final String GROUP_ACL = "x-emc-groupacl";
|
||||
public static final String UID = "x-emc-uid";
|
||||
public static final String TOKEN = "x-emc-token";
|
||||
|
||||
}
|
||||
|
|
|
@ -31,19 +31,30 @@ import java.io.InputStream;
|
|||
import java.lang.reflect.UndeclaredThrowableException;
|
||||
import java.net.URI;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.SortedSet;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.jclouds.atmosonline.saas.blobstore.strategy.RecursiveRemove;
|
||||
import org.jclouds.atmosonline.saas.domain.AtmosObject;
|
||||
import org.jclouds.atmosonline.saas.domain.BoundedSortedSet;
|
||||
import org.jclouds.atmosonline.saas.domain.DirectoryEntry;
|
||||
import org.jclouds.atmosonline.saas.domain.FileType;
|
||||
import org.jclouds.atmosonline.saas.domain.SystemMetadata;
|
||||
import org.jclouds.atmosonline.saas.options.ListOptions;
|
||||
import org.jclouds.blobstore.KeyAlreadyExistsException;
|
||||
import org.jclouds.blobstore.KeyNotFoundException;
|
||||
import org.jclouds.blobstore.integration.internal.BaseBlobStoreIntegrationTest;
|
||||
import org.jclouds.blobstore.strategy.ClearContainerStrategy;
|
||||
import org.jclouds.http.HttpResponseException;
|
||||
import org.jclouds.logging.log4j.config.Log4JLoggingModule;
|
||||
import org.jclouds.util.Utils;
|
||||
import org.testng.annotations.BeforeGroups;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import com.google.common.base.Supplier;
|
||||
|
||||
/**
|
||||
* Tests behavior of {@code AtmosStorageClient}
|
||||
*
|
||||
|
@ -52,6 +63,50 @@ import org.testng.annotations.Test;
|
|||
@Test(groups = "live", sequential = true, testName = "emcsaas.AtmosStorageClientLiveTest")
|
||||
public class AtmosStorageClientLiveTest {
|
||||
|
||||
private static final class HeadMatches implements Runnable {
|
||||
private final AtmosStorageClient connection;
|
||||
private final String name;
|
||||
private final String metadataValue;
|
||||
|
||||
private HeadMatches(AtmosStorageClient connection, String name, String metadataValue) {
|
||||
this.connection = connection;
|
||||
this.name = name;
|
||||
this.metadataValue = metadataValue;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
verifyHeadObject(connection, name, metadataValue);
|
||||
} catch (Exception e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final class ObjectMatches implements Runnable {
|
||||
private final AtmosStorageClient connection;
|
||||
private final String name;
|
||||
private final String metadataValue;
|
||||
private final String compare;
|
||||
|
||||
private ObjectMatches(AtmosStorageClient connection, String name, String metadataValue,
|
||||
String compare) {
|
||||
this.connection = connection;
|
||||
this.name = name;
|
||||
this.metadataValue = metadataValue;
|
||||
this.compare = compare;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
verifyObject(connection, name, compare, metadataValue);
|
||||
} catch (Exception e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final int INCONSISTENCY_WINDOW = 5000;
|
||||
protected AtmosStorageClient connection;
|
||||
private String containerPrefix = BaseBlobStoreIntegrationTest.CONTAINER_PREFIX;
|
||||
|
||||
|
@ -59,17 +114,24 @@ public class AtmosStorageClientLiveTest {
|
|||
URI container2;
|
||||
|
||||
@BeforeGroups(groups = { "live" })
|
||||
public void setupClient() {
|
||||
public void setupClient() throws InterruptedException, ExecutionException, TimeoutException {
|
||||
String uid = checkNotNull(System.getProperty("jclouds.test.user"), "jclouds.test.user");
|
||||
String key = checkNotNull(System.getProperty("jclouds.test.key"), "jclouds.test.key");
|
||||
|
||||
connection = new AtmosStorageContextBuilder(new AtmosStoragePropertiesBuilder(uid, key)
|
||||
.build()).withModules(new Log4JLoggingModule()).buildContext().getApi();
|
||||
ClearContainerStrategy clearer = new RecursiveRemove(connection);
|
||||
for (DirectoryEntry entry : connection.listDirectories().get(10, TimeUnit.SECONDS)) {
|
||||
if (entry.getObjectName().startsWith(containerPrefix)) {
|
||||
clearer.execute(entry.getObjectName());
|
||||
deleteConfirmed(entry.getObjectName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListDirectorys() throws Exception {
|
||||
SortedSet<DirectoryEntry> response = connection.listDirectories();
|
||||
BoundedSortedSet<? extends DirectoryEntry> response = connection.listDirectories().get(10,
|
||||
TimeUnit.SECONDS);
|
||||
assert null != response;
|
||||
}
|
||||
|
||||
|
@ -83,7 +145,7 @@ public class AtmosStorageClientLiveTest {
|
|||
while (!created) {
|
||||
privateDirectory = containerPrefix + new SecureRandom().nextInt();
|
||||
try {
|
||||
created = connection.createDirectory(privateDirectory) != null;
|
||||
created = connection.createDirectory(privateDirectory).get(10, TimeUnit.SECONDS) != null;
|
||||
} catch (UndeclaredThrowableException e) {
|
||||
HttpResponseException htpe = (HttpResponseException) e.getCause().getCause();
|
||||
if (htpe.getResponse().getStatusCode() == 409)
|
||||
|
@ -91,41 +153,267 @@ public class AtmosStorageClientLiveTest {
|
|||
throw e;
|
||||
}
|
||||
}
|
||||
SortedSet<DirectoryEntry> response = connection.listDirectories();
|
||||
assert response.size() > 0;
|
||||
BoundedSortedSet<? extends DirectoryEntry> response = connection.listDirectories().get(10,
|
||||
TimeUnit.SECONDS);
|
||||
for (DirectoryEntry id : response) {
|
||||
SortedSet<DirectoryEntry> r2 = connection.listDirectory(id.getObjectName());
|
||||
BoundedSortedSet<? extends DirectoryEntry> r2 = connection.listDirectory(id.getObjectName()).get(10,
|
||||
TimeUnit.SECONDS);
|
||||
assert r2 != null;
|
||||
}
|
||||
}
|
||||
|
||||
@Test(timeOut = 5 * 60 * 1000, dependsOnMethods = { "testCreateDirectory" })
|
||||
public void testFileOperations() throws Exception {
|
||||
String data = "Here is my data";
|
||||
public void testListOptions() throws Exception {
|
||||
createOrReplaceObject("object2", "here is my data!", "meta-value1");
|
||||
createOrReplaceObject("object3", "here is my data!", "meta-value1");
|
||||
createOrReplaceObject("object4", "here is my data!", "meta-value1");
|
||||
BoundedSortedSet<? extends DirectoryEntry> r2 = connection.listDirectory(privateDirectory,
|
||||
ListOptions.Builder.limit(1)).get(10, TimeUnit.SECONDS);
|
||||
// test bug exists:
|
||||
assertEquals(r2.size(), 3);
|
||||
// assertEquals(r2.size(), 1);
|
||||
// assert r2.getToken() != null;
|
||||
// assertEquals(r2.last().getObjectName(),"object2");
|
||||
// r2 = connection.listDirectory(privateDirectory,
|
||||
// ListOptions.Builder.token(r2.getToken())).get(10,
|
||||
// TimeUnit.SECONDS);
|
||||
// assertEquals(r2.size(), 2);
|
||||
// assert r2.getToken() == null;
|
||||
// assertEquals(r2.last().getObjectName(),"object4");
|
||||
|
||||
}
|
||||
|
||||
@Test(timeOut = 5 * 60 * 1000, dependsOnMethods = { "testListOptions" })
|
||||
public void testFileOperations() throws Exception {
|
||||
// create the object
|
||||
createOrReplaceObject("object", "here is my data!", "meta-value1");
|
||||
assertEventuallyObjectMatches("object", "here is my data!", "meta-value1");
|
||||
assertEventuallyHeadMatches("object", "meta-value1");
|
||||
// try overwriting the object
|
||||
createOrReplaceObject("object", "here is my data?", "meta-value?");
|
||||
assertEventuallyObjectMatches("object", "here is my data?", "meta-value?");
|
||||
|
||||
// loop to gather metrics
|
||||
for (boolean stream : new Boolean[] { true, false }) {
|
||||
for (int i = 0; i < 30; i++) {
|
||||
System.err.printf("upload/delete/create attempt %d type %s%n", i + 1, stream ? "stream"
|
||||
: "string");
|
||||
// try updating
|
||||
createOrUpdateWithErrorLoop(stream, "there is my data", "2");
|
||||
|
||||
deleteConfirmed(privateDirectory + "/object");
|
||||
// now create
|
||||
createOrUpdateWithErrorLoop(stream, "where is my data", "3");
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void createOrUpdateWithErrorLoop(boolean stream, String data, String metadataValue)
|
||||
throws Exception {
|
||||
createOrReplaceObject("object", makeData(data, stream), metadataValue);
|
||||
assertEventuallyObjectMatches("object", data, metadataValue);
|
||||
}
|
||||
|
||||
Object makeData(String in, boolean stream) {
|
||||
return stream ? IOUtils.toInputStream(in) : in;
|
||||
}
|
||||
|
||||
private void createOrReplaceObject(String name, Object data, String metadataValue)
|
||||
throws Exception {
|
||||
// Test PUT with string data, ETag hash, and a piece of metadata
|
||||
AtmosObject object = connection.newObject();
|
||||
object.getContentMetadata().setName("object");
|
||||
object.getContentMetadata().setName(name);
|
||||
object.setData(data);
|
||||
object.getContentMetadata().setContentLength(data.length());
|
||||
object.getContentMetadata().setContentLength(16);
|
||||
object.generateMD5();
|
||||
object.getContentMetadata().setContentType("text/plain");
|
||||
object.getUserMetadata().getMetadata().put("Metadata", "metadata-value");
|
||||
URI uri = connection.createFile(privateDirectory, object).get(30, TimeUnit.SECONDS);
|
||||
object.getUserMetadata().getMetadata().put("Metadata", metadataValue);
|
||||
replaceObject(object);
|
||||
}
|
||||
|
||||
AtmosObject getBlob = connection.readFile(privateDirectory + "/object").get(120,
|
||||
TimeUnit.SECONDS);
|
||||
assertEquals(IOUtils.toString((InputStream) getBlob.getData()), data);
|
||||
// TODO assertEquals(getBlob.getName(), object.getName());
|
||||
assertEquals(getBlob.getContentMetadata().getContentLength(), new Long(data.length()));
|
||||
/**
|
||||
* Due to eventual consistency, container commands may not return correctly immediately. Hence,
|
||||
* we will try up to the inconsistency window to see if the assertion completes.
|
||||
*/
|
||||
protected static void assertEventually(Runnable assertion) throws InterruptedException {
|
||||
long start = System.currentTimeMillis();
|
||||
AssertionError error = null;
|
||||
for (int i = 0; i < 30; i++) {
|
||||
try {
|
||||
assertion.run();
|
||||
if (i > 0)
|
||||
System.err.printf("%d attempts and %dms asserting %s%n", i + 1, System
|
||||
.currentTimeMillis()
|
||||
- start, assertion.getClass().getSimpleName());
|
||||
return;
|
||||
} catch (AssertionError e) {
|
||||
error = e;
|
||||
}
|
||||
Thread.sleep(INCONSISTENCY_WINDOW / 30);
|
||||
}
|
||||
if (error != null)
|
||||
throw error;
|
||||
|
||||
}
|
||||
|
||||
protected void assertEventuallyObjectMatches(final String name, final String compare,
|
||||
final String metadataValue) throws InterruptedException {
|
||||
assertEventually(new ObjectMatches(connection, privateDirectory + "/" + name, metadataValue,
|
||||
compare));
|
||||
}
|
||||
|
||||
protected void assertEventuallyHeadMatches(final String name, final String metadataValue)
|
||||
throws InterruptedException {
|
||||
assertEventually(new HeadMatches(connection, privateDirectory + "/" + name, metadataValue));
|
||||
}
|
||||
|
||||
private static void verifyHeadObject(AtmosStorageClient connection, String path,
|
||||
String metadataValue) throws InterruptedException, ExecutionException,
|
||||
TimeoutException, IOException {
|
||||
AtmosObject getBlob = connection.headFile(path);
|
||||
assertEquals(IOUtils.toString((InputStream) getBlob.getData()), "");
|
||||
verifyMetadata(metadataValue, getBlob);
|
||||
}
|
||||
|
||||
private static void verifyObject(AtmosStorageClient connection, String path, String compare,
|
||||
String metadataValue) throws InterruptedException, ExecutionException,
|
||||
TimeoutException, IOException {
|
||||
AtmosObject getBlob = connection.readFile(path).get(120, TimeUnit.SECONDS);
|
||||
assertEquals(getBlob.getData() instanceof String ? getBlob.getData() : IOUtils
|
||||
.toString((InputStream) getBlob.getData()), compare);
|
||||
verifyMetadata(metadataValue, getBlob);
|
||||
}
|
||||
|
||||
private static void verifyMetadata(String metadataValue, AtmosObject getBlob) {
|
||||
assertEquals(getBlob.getContentMetadata().getContentLength(), new Long(16));
|
||||
assert getBlob.getContentMetadata().getContentType().startsWith("text/plain");
|
||||
assertEquals(getBlob.getUserMetadata().getMetadata().get("Metadata"), "metadata-value");
|
||||
assertEquals(getBlob.getUserMetadata().getMetadata().get("Metadata"), metadataValue);
|
||||
SystemMetadata md = getBlob.getSystemMetadata();
|
||||
assertEquals(md.getSize(), 16);
|
||||
assert md.getGroupID() != null;
|
||||
assertEquals(md.getHardLinkCount(), 1);
|
||||
assert md.getInceptionTime() != null;
|
||||
assert md.getLastAccessTime() != null;
|
||||
assert md.getLastMetadataModification() != null;
|
||||
assert md.getLastUserDataModification() != null;
|
||||
assert md.getObjectID() != null;
|
||||
assertEquals(md.getObjectName(), "object");
|
||||
assert md.getPolicyName() != null;
|
||||
assertEquals(md.getType(), FileType.REGULAR);
|
||||
assert md.getUserID() != null;
|
||||
|
||||
try {
|
||||
Utils.toStringAndClose(uri.toURL().openStream());
|
||||
Utils.toStringAndClose(URI.create(
|
||||
"http://accesspoint.emccis.com/rest/objects/"
|
||||
+ getBlob.getSystemMetadata().getObjectID()).toURL().openStream());
|
||||
assert false : "shouldn't have worked, since it is private";
|
||||
} catch (IOException e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void replaceObject(AtmosObject object) throws Exception {
|
||||
alwaysDeleteFirstReplaceStrategy(object);
|
||||
// retryAndCheckSystemMetadataAndPutIfPresentReplaceStrategy(object); // HEAD 200 followed by
|
||||
// PUT = 404!
|
||||
}
|
||||
|
||||
private void alwaysDeleteFirstReplaceStrategy(AtmosObject object) throws Exception {
|
||||
deleteConfirmed(privateDirectory + "/" + object.getContentMetadata().getName());
|
||||
long time = System.currentTimeMillis();
|
||||
try {
|
||||
connection.createFile(privateDirectory, object).get(30, TimeUnit.SECONDS);
|
||||
System.err.printf("%s %s; %dms%n", "created",
|
||||
object.getData() instanceof InputStream ? "stream" : "string", System
|
||||
.currentTimeMillis()
|
||||
- time);
|
||||
} catch (Exception e) {
|
||||
String message = (e.getCause().getCause() != null) ? e.getCause().getCause().getMessage()
|
||||
: e.getCause().getMessage();
|
||||
System.err.printf("failure %s %s; %dms: [%s]%n", "creating",
|
||||
object.getData() instanceof InputStream ? "stream" : "string", System
|
||||
.currentTimeMillis()
|
||||
- time, message);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteConfirmed(final String path) throws InterruptedException, ExecutionException,
|
||||
TimeoutException {
|
||||
long time = System.currentTimeMillis();
|
||||
deleteImmediateAndVerifyWithHead(path);
|
||||
System.err.printf("confirmed deletion after %dms%n", System.currentTimeMillis() - time);
|
||||
}
|
||||
|
||||
private void deleteImmediateAndVerifyWithHead(final String path) throws InterruptedException,
|
||||
ExecutionException, TimeoutException {
|
||||
try {
|
||||
connection.deletePath(path).get(10, TimeUnit.SECONDS);
|
||||
} catch (KeyNotFoundException ex) {
|
||||
}
|
||||
assert !connection.pathExists(path);
|
||||
}
|
||||
|
||||
protected void deleteConsistencyAware(final String path) throws InterruptedException,
|
||||
ExecutionException, TimeoutException {
|
||||
try {
|
||||
connection.deletePath(path).get(10, TimeUnit.SECONDS);
|
||||
} catch (KeyNotFoundException ex) {
|
||||
}
|
||||
assert Utils.enventuallyTrue(new Supplier<Boolean>() {
|
||||
public Boolean get() {
|
||||
return !connection.pathExists(path);
|
||||
}
|
||||
}, INCONSISTENCY_WINDOW);
|
||||
}
|
||||
|
||||
protected void retryAndCheckSystemMetadataAndPutIfPresentReplaceStrategy(AtmosObject object)
|
||||
throws Exception {
|
||||
|
||||
int failures = 0;
|
||||
while (true) {
|
||||
try {
|
||||
checkSystemMetadataAndPutIfPresentReplaceStrategy(object);
|
||||
break;
|
||||
} catch (ExecutionException e1) {// bug
|
||||
if (!(e1.getCause() instanceof KeyAlreadyExistsException))
|
||||
throw e1;
|
||||
else
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
if (failures > 0)
|
||||
System.err.printf("%d failures create/replacing %s%n", failures,
|
||||
object.getData() instanceof InputStream ? "stream" : "string");
|
||||
}
|
||||
|
||||
private void checkSystemMetadataAndPutIfPresentReplaceStrategy(AtmosObject object)
|
||||
throws Exception {
|
||||
long time = System.currentTimeMillis();
|
||||
boolean update = true;
|
||||
try {
|
||||
connection.getSystemMetadata(privateDirectory + "/object");
|
||||
} catch (KeyNotFoundException ex) {
|
||||
update = false;
|
||||
}
|
||||
try {
|
||||
if (update)
|
||||
connection.updateFile(privateDirectory, object).get(30, TimeUnit.SECONDS);
|
||||
else
|
||||
connection.createFile(privateDirectory, object).get(30, TimeUnit.SECONDS);
|
||||
System.err.printf("%s %s; %dms%n", update ? "updated" : "created",
|
||||
object.getData() instanceof InputStream ? "stream" : "string", System
|
||||
.currentTimeMillis()
|
||||
- time);
|
||||
} catch (Exception e) {
|
||||
String message = (e.getCause().getCause() != null) ? e.getCause().getCause().getMessage()
|
||||
: e.getCause().getMessage();
|
||||
System.err.printf("failure %s %s; %dms: [%s]%n", update ? "updating" : "creating", object
|
||||
.getData() instanceof InputStream ? "stream" : "string", System
|
||||
.currentTimeMillis()
|
||||
- time, message);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,34 +25,43 @@ package org.jclouds.atmosonline.saas;
|
|||
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URI;
|
||||
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
|
||||
import org.jclouds.atmosonline.saas.config.AtmosStorageRestClientModule;
|
||||
import org.jclouds.atmosonline.saas.blobstore.functions.BlobToObject;
|
||||
import org.jclouds.atmosonline.saas.config.AtmosObjectModule;
|
||||
import org.jclouds.atmosonline.saas.domain.AtmosObject;
|
||||
import org.jclouds.atmosonline.saas.filters.SignRequest;
|
||||
import org.jclouds.atmosonline.saas.functions.ParseDirectoryListFromContentAndHeaders;
|
||||
import org.jclouds.atmosonline.saas.functions.ParseObjectFromHeadersAndHttpContent;
|
||||
import org.jclouds.atmosonline.saas.functions.ParseSystemMetadataFromHeaders;
|
||||
import org.jclouds.atmosonline.saas.functions.ReturnEndpointIfAlreadyExists;
|
||||
import org.jclouds.atmosonline.saas.options.ListOptions;
|
||||
import org.jclouds.atmosonline.saas.reference.AtmosStorageConstants;
|
||||
import org.jclouds.atmosonline.saas.xml.ListDirectoryResponseHandler;
|
||||
import org.jclouds.concurrent.WithinThreadExecutorService;
|
||||
import org.jclouds.concurrent.config.ExecutorServiceModule;
|
||||
import org.jclouds.blobstore.binders.BindBlobToMultipartFormTest;
|
||||
import org.jclouds.blobstore.config.BlobStoreObjectModule;
|
||||
import org.jclouds.blobstore.functions.ReturnVoidOnNotFoundOr404;
|
||||
import org.jclouds.blobstore.functions.ThrowKeyNotFoundOn404;
|
||||
import org.jclouds.http.HttpUtils;
|
||||
import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule;
|
||||
import org.jclouds.http.functions.ParseSax;
|
||||
import org.jclouds.http.functions.ParseURIFromListOrLocationHeaderIf20x;
|
||||
import org.jclouds.http.functions.ReturnVoidIf2xx;
|
||||
import org.jclouds.http.options.GetOptions;
|
||||
import org.jclouds.logging.Logger;
|
||||
import org.jclouds.logging.Logger.LoggerFactory;
|
||||
import org.jclouds.rest.config.RestModule;
|
||||
import org.jclouds.rest.RestClientTest;
|
||||
import org.jclouds.rest.internal.GeneratedHttpRequest;
|
||||
import org.jclouds.rest.internal.RestAnnotationProcessor;
|
||||
import org.jclouds.util.Jsr330;
|
||||
import org.jclouds.util.TimeStamp;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.Key;
|
||||
import com.google.inject.Module;
|
||||
import com.google.inject.TypeLiteral;
|
||||
|
||||
/**
|
||||
|
@ -60,48 +69,233 @@ import com.google.inject.TypeLiteral;
|
|||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Test(groups = "unit", testName = "azureblob.AtmosStorageClientTest")
|
||||
public class AtmosStorageClientTest {
|
||||
@Test(groups = "unit", testName = "emcsaas.AtmosStorageClientTest")
|
||||
public class AtmosStorageClientTest extends RestClientTest<AtmosStorageClient> {
|
||||
|
||||
public void testListDirectories() throws SecurityException, NoSuchMethodException {
|
||||
Method method = AtmosStorageClient.class.getMethod("listDirectories");
|
||||
private BlobToObject blobToObject;
|
||||
|
||||
GeneratedHttpRequest<AtmosStorageClient> httpMethod = processor.createRequest(method,
|
||||
new Object[] {});
|
||||
assertEquals(httpMethod.getRequestLine(),
|
||||
public void testListDirectories() throws SecurityException, NoSuchMethodException, IOException {
|
||||
Method method = AtmosStorageClient.class.getMethod("listDirectories", Array.newInstance(
|
||||
ListOptions.class, 0).getClass());
|
||||
GeneratedHttpRequest<AtmosStorageClient> httpMethod = processor.createRequest(method);
|
||||
|
||||
assertRequestLineEquals(httpMethod,
|
||||
"GET http://accesspoint.emccis.com/rest/namespace HTTP/1.1");
|
||||
assertEquals(httpMethod.getHeaders().size(), 1);
|
||||
assertEquals(httpMethod.getFirstHeaderOrNull(HttpHeaders.ACCEPT), "text/xml");
|
||||
assertEquals(processor.createExceptionParserOrNullIfNotFound(method), null);
|
||||
assertEquals(processor.createResponseParser(method, httpMethod).getClass(),
|
||||
ParseSax.class);
|
||||
assertEquals(RestAnnotationProcessor.getSaxResponseParserClassOrNull(method), ListDirectoryResponseHandler.class);
|
||||
assertHeadersEqual(httpMethod, HttpHeaders.ACCEPT + ": text/xml\n");
|
||||
assertEntityEquals(httpMethod, null);
|
||||
|
||||
assertResponseParserClassEquals(method, httpMethod,
|
||||
ParseDirectoryListFromContentAndHeaders.class);
|
||||
assertSaxResponseParserClassEquals(method, null);
|
||||
assertExceptionParserClassEquals(method, null);
|
||||
|
||||
checkFilters(httpMethod);
|
||||
}
|
||||
|
||||
public void testListDirectory() throws SecurityException, NoSuchMethodException, IOException {
|
||||
Method method = AtmosStorageClient.class.getMethod("listDirectory", String.class, Array
|
||||
.newInstance(ListOptions.class, 0).getClass());
|
||||
GeneratedHttpRequest<AtmosStorageClient> httpMethod = processor.createRequest(method,
|
||||
"directory");
|
||||
|
||||
assertRequestLineEquals(httpMethod,
|
||||
"GET http://accesspoint.emccis.com/rest/namespace/directory/ HTTP/1.1");
|
||||
assertHeadersEqual(httpMethod, HttpHeaders.ACCEPT + ": text/xml\n");
|
||||
assertEntityEquals(httpMethod, null);
|
||||
|
||||
assertResponseParserClassEquals(method, httpMethod,
|
||||
ParseDirectoryListFromContentAndHeaders.class);
|
||||
assertSaxResponseParserClassEquals(method, null);
|
||||
assertExceptionParserClassEquals(method, null);
|
||||
|
||||
checkFilters(httpMethod);
|
||||
}
|
||||
|
||||
public void testListDirectoriesOptions() throws SecurityException, NoSuchMethodException,
|
||||
IOException {
|
||||
Method method = AtmosStorageClient.class.getMethod("listDirectories", Array.newInstance(
|
||||
ListOptions.class, 0).getClass());
|
||||
GeneratedHttpRequest<AtmosStorageClient> httpMethod = processor.createRequest(method,
|
||||
new ListOptions().limit(1).token("asda"));
|
||||
|
||||
assertRequestLineEquals(httpMethod,
|
||||
"GET http://accesspoint.emccis.com/rest/namespace HTTP/1.1");
|
||||
assertHeadersEqual(httpMethod, HttpHeaders.ACCEPT
|
||||
+ ": text/xml\nx-emc-limit: 1\nx-emc-token: asda\n");
|
||||
assertEntityEquals(httpMethod, null);
|
||||
|
||||
assertResponseParserClassEquals(method, httpMethod,
|
||||
ParseDirectoryListFromContentAndHeaders.class);
|
||||
assertSaxResponseParserClassEquals(method, null);
|
||||
assertExceptionParserClassEquals(method, null);
|
||||
|
||||
checkFilters(httpMethod);
|
||||
}
|
||||
|
||||
public void testListDirectoryOptions() throws SecurityException, NoSuchMethodException,
|
||||
IOException {
|
||||
Method method = AtmosStorageClient.class.getMethod("listDirectory", String.class, Array
|
||||
.newInstance(ListOptions.class, 0).getClass());
|
||||
GeneratedHttpRequest<AtmosStorageClient> httpMethod = processor.createRequest(method,
|
||||
"directory", new ListOptions().limit(1).token("asda"));
|
||||
|
||||
assertRequestLineEquals(httpMethod,
|
||||
"GET http://accesspoint.emccis.com/rest/namespace/directory/ HTTP/1.1");
|
||||
assertHeadersEqual(httpMethod, HttpHeaders.ACCEPT
|
||||
+ ": text/xml\nx-emc-limit: 1\nx-emc-token: asda\n");
|
||||
assertEntityEquals(httpMethod, null);
|
||||
|
||||
assertResponseParserClassEquals(method, httpMethod,
|
||||
ParseDirectoryListFromContentAndHeaders.class);
|
||||
assertSaxResponseParserClassEquals(method, null);
|
||||
assertExceptionParserClassEquals(method, null);
|
||||
|
||||
checkFilters(httpMethod);
|
||||
}
|
||||
|
||||
public void testCreateDirectory() throws SecurityException, NoSuchMethodException, IOException {
|
||||
Method method = AtmosStorageClient.class.getMethod("createDirectory", String.class);
|
||||
GeneratedHttpRequest<AtmosStorageClient> httpMethod = processor.createRequest(method, "dir");
|
||||
|
||||
assertRequestLineEquals(httpMethod,
|
||||
"POST http://accesspoint.emccis.com/rest/namespace/dir/ HTTP/1.1");
|
||||
assertHeadersEqual(httpMethod, HttpHeaders.ACCEPT + ": */*\n");
|
||||
assertEntityEquals(httpMethod, null);
|
||||
|
||||
assertResponseParserClassEquals(method, httpMethod,
|
||||
ParseURIFromListOrLocationHeaderIf20x.class);
|
||||
assertSaxResponseParserClassEquals(method, null);
|
||||
assertExceptionParserClassEquals(method, ReturnEndpointIfAlreadyExists.class);
|
||||
|
||||
checkFilters(httpMethod);
|
||||
}
|
||||
|
||||
public void testCreateFile() throws SecurityException, NoSuchMethodException, IOException {
|
||||
Method method = AtmosStorageClient.class.getMethod("createFile", String.class,
|
||||
AtmosObject.class);
|
||||
GeneratedHttpRequest<AtmosStorageClient> httpMethod = processor.createRequest(method, "dir",
|
||||
blobToObject.apply(BindBlobToMultipartFormTest.TEST_BLOB));
|
||||
|
||||
assertRequestLineEquals(httpMethod,
|
||||
"POST http://accesspoint.emccis.com/rest/namespace/dir/hello HTTP/1.1");
|
||||
assertHeadersEqual(httpMethod, HttpHeaders.ACCEPT
|
||||
+ ": */*\nContent-Length: 5\nContent-Type: text/plain\n");
|
||||
assertEntityEquals(httpMethod, "hello");
|
||||
|
||||
assertResponseParserClassEquals(method, httpMethod,
|
||||
ParseURIFromListOrLocationHeaderIf20x.class);
|
||||
assertSaxResponseParserClassEquals(method, null);
|
||||
assertExceptionParserClassEquals(method, null);
|
||||
|
||||
checkFilters(httpMethod);
|
||||
}
|
||||
|
||||
public void testUpdateFile() throws SecurityException, NoSuchMethodException, IOException {
|
||||
Method method = AtmosStorageClient.class.getMethod("updateFile", String.class,
|
||||
AtmosObject.class);
|
||||
GeneratedHttpRequest<AtmosStorageClient> httpMethod = processor.createRequest(method, "dir",
|
||||
blobToObject.apply(BindBlobToMultipartFormTest.TEST_BLOB));
|
||||
|
||||
assertRequestLineEquals(httpMethod,
|
||||
"PUT http://accesspoint.emccis.com/rest/namespace/dir/hello HTTP/1.1");
|
||||
assertHeadersEqual(httpMethod, HttpHeaders.ACCEPT
|
||||
+ ": */*\nContent-Length: 5\nContent-Type: text/plain\n");
|
||||
assertEntityEquals(httpMethod, "hello");
|
||||
|
||||
assertResponseParserClassEquals(method, httpMethod, ReturnVoidIf2xx.class);
|
||||
assertSaxResponseParserClassEquals(method, null);
|
||||
assertExceptionParserClassEquals(method, ThrowKeyNotFoundOn404.class);
|
||||
|
||||
checkFilters(httpMethod);
|
||||
}
|
||||
|
||||
public void testReadFile() throws SecurityException, NoSuchMethodException, IOException {
|
||||
Method method = AtmosStorageClient.class.getMethod("readFile", String.class, Array
|
||||
.newInstance(GetOptions.class, 0).getClass());
|
||||
GeneratedHttpRequest<AtmosStorageClient> httpMethod = processor.createRequest(method,
|
||||
"dir/file");
|
||||
|
||||
assertRequestLineEquals(httpMethod,
|
||||
"GET http://accesspoint.emccis.com/rest/namespace/dir/file HTTP/1.1");
|
||||
assertHeadersEqual(httpMethod, HttpHeaders.ACCEPT + ": */*\n");
|
||||
assertEntityEquals(httpMethod, null);
|
||||
|
||||
assertResponseParserClassEquals(method, httpMethod,
|
||||
ParseObjectFromHeadersAndHttpContent.class);
|
||||
assertSaxResponseParserClassEquals(method, null);
|
||||
assertExceptionParserClassEquals(method, ThrowKeyNotFoundOn404.class);
|
||||
|
||||
checkFilters(httpMethod);
|
||||
}
|
||||
|
||||
public void testGetSystemMetadata() throws SecurityException, NoSuchMethodException, IOException {
|
||||
Method method = AtmosStorageClient.class.getMethod("getSystemMetadata", String.class);
|
||||
GeneratedHttpRequest<AtmosStorageClient> httpMethod = processor.createRequest(method,
|
||||
"dir/file");
|
||||
|
||||
assertRequestLineEquals(httpMethod,
|
||||
"HEAD http://accesspoint.emccis.com/rest/namespace/dir/file HTTP/1.1");
|
||||
assertHeadersEqual(httpMethod, HttpHeaders.ACCEPT + ": */*\n");
|
||||
assertEntityEquals(httpMethod, null);
|
||||
|
||||
assertResponseParserClassEquals(method, httpMethod, ParseSystemMetadataFromHeaders.class);
|
||||
assertSaxResponseParserClassEquals(method, null);
|
||||
assertExceptionParserClassEquals(method, ThrowKeyNotFoundOn404.class);
|
||||
|
||||
checkFilters(httpMethod);
|
||||
}
|
||||
|
||||
public void testDeletePath() throws SecurityException, NoSuchMethodException, IOException {
|
||||
Method method = AtmosStorageClient.class.getMethod("deletePath", String.class);
|
||||
GeneratedHttpRequest<AtmosStorageClient> httpMethod = processor.createRequest(method,
|
||||
"dir/file");
|
||||
|
||||
assertRequestLineEquals(httpMethod,
|
||||
"DELETE http://accesspoint.emccis.com/rest/namespace/dir/file HTTP/1.1");
|
||||
assertHeadersEqual(httpMethod, HttpHeaders.ACCEPT + ": */*\n");
|
||||
assertEntityEquals(httpMethod, null);
|
||||
|
||||
assertResponseParserClassEquals(method, httpMethod, ReturnVoidIf2xx.class);
|
||||
assertSaxResponseParserClassEquals(method, null);
|
||||
assertExceptionParserClassEquals(method, ReturnVoidOnNotFoundOr404.class);
|
||||
|
||||
checkFilters(httpMethod);
|
||||
}
|
||||
|
||||
public void testNewObject() throws SecurityException, NoSuchMethodException, IOException {
|
||||
Method method = AtmosStorageClient.class.getMethod("newObject");
|
||||
assertEquals(method.getReturnType(), AtmosObject.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void checkFilters(GeneratedHttpRequest<AtmosStorageClient> httpMethod) {
|
||||
assertEquals(httpMethod.getFilters().size(), 1);
|
||||
assertEquals(httpMethod.getFilters().get(0).getClass(), SignRequest.class);
|
||||
}
|
||||
|
||||
public void testCreateDirectory() throws SecurityException, NoSuchMethodException {
|
||||
Method method = AtmosStorageClient.class.getMethod("createDirectory", String.class);
|
||||
|
||||
GeneratedHttpRequest<AtmosStorageClient> httpMethod = processor.createRequest(method,
|
||||
"dir");
|
||||
assertEquals(httpMethod.getRequestLine(),
|
||||
"POST http://accesspoint.emccis.com/rest/namespace/dir/ HTTP/1.1");
|
||||
assertEquals(httpMethod.getHeaders().size(), 1);
|
||||
assertEquals(httpMethod.getFirstHeaderOrNull(HttpHeaders.ACCEPT), MediaType.WILDCARD);
|
||||
assertEquals(processor.createExceptionParserOrNullIfNotFound(method), null);
|
||||
assertEquals(processor.createResponseParser(method, httpMethod).getClass(),
|
||||
ParseURIFromListOrLocationHeaderIf20x.class);
|
||||
assertEquals(RestAnnotationProcessor.getSaxResponseParserClassOrNull(method), null);
|
||||
assertEquals(httpMethod.getFilters().size(), 1);
|
||||
assertEquals(httpMethod.getFilters().get(0).getClass(), SignRequest.class);
|
||||
@Override
|
||||
protected TypeLiteral<RestAnnotationProcessor<AtmosStorageClient>> createTypeLiteral() {
|
||||
return new TypeLiteral<RestAnnotationProcessor<AtmosStorageClient>>() {
|
||||
};
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
void setupFactory() {
|
||||
Injector injector = Guice.createInjector(new AbstractModule() {
|
||||
@Override
|
||||
protected void setupFactory() {
|
||||
super.setupFactory();
|
||||
blobToObject = injector.getInstance(BlobToObject.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Module createModule() {
|
||||
return new AbstractModule() {
|
||||
@Override
|
||||
protected void configure() {
|
||||
install(new BlobStoreObjectModule());
|
||||
install(new AtmosObjectModule());
|
||||
bind(URI.class).annotatedWith(AtmosStorage.class).toInstance(
|
||||
URI.create("http://accesspoint.emccis.com"));
|
||||
bind(String.class).annotatedWith(TimeStamp.class).toInstance("timestamp");
|
||||
bindConstant().annotatedWith(
|
||||
Jsr330.named(AtmosStorageConstants.PROPERTY_EMCSAAS_ENDPOINT)).to(
|
||||
"http://accesspoint.emccis.com");
|
||||
|
@ -116,13 +310,9 @@ public class AtmosStorageClientTest {
|
|||
});
|
||||
bindConstant().annotatedWith(
|
||||
Jsr330.named(AtmosStorageConstants.PROPERTY_EMCSAAS_SESSIONINTERVAL)).to(1l);
|
||||
}
|
||||
}, new AtmosStorageRestClientModule(), new RestModule(), new ExecutorServiceModule(
|
||||
new WithinThreadExecutorService()), new JavaUrlHttpCommandExecutorServiceModule());
|
||||
processor = injector.getInstance(Key
|
||||
.get(new TypeLiteral<RestAnnotationProcessor<AtmosStorageClient>>() {
|
||||
}));
|
||||
}
|
||||
|
||||
RestAnnotationProcessor<AtmosStorageClient> processor;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
/**
|
||||
*
|
||||
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||
*
|
||||
* ====================================================================
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.atmosonline.saas.blobstore;
|
||||
|
||||
import static org.jclouds.blobstore.reference.BlobStoreConstants.PROPERTY_USER_METADATA_PREFIX;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.jclouds.atmosonline.saas.AtmosStorageClient;
|
||||
import org.jclouds.atmosonline.saas.AtmosStoragePropertiesBuilder;
|
||||
import org.jclouds.atmosonline.saas.blobstore.config.AtmosBlobStoreContextModule;
|
||||
import org.jclouds.atmosonline.saas.config.AtmosStorageRestClientModule;
|
||||
import org.jclouds.atmosonline.saas.config.AtmosStorageStubClientModule;
|
||||
import org.jclouds.atmosonline.saas.domain.AtmosObject;
|
||||
import org.jclouds.atmosonline.saas.domain.internal.AtmosObjectImpl;
|
||||
import org.jclouds.atmosonline.saas.internal.StubAtmosStorageClient;
|
||||
import org.jclouds.atmosonline.saas.reference.AtmosStorageConstants;
|
||||
import org.jclouds.blobstore.BlobStoreContext;
|
||||
import org.jclouds.blobstore.domain.Blob;
|
||||
import org.jclouds.blobstore.domain.internal.BlobImpl;
|
||||
import org.jclouds.blobstore.internal.BlobStoreContextImpl;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.Key;
|
||||
import com.google.inject.Module;
|
||||
import com.google.inject.TypeLiteral;
|
||||
|
||||
/**
|
||||
* Tests behavior of modules configured in AtmosStorageContextBuilder
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Test(groups = "unit", testName = "emcsaas.AtmosStorageContextBuilderTest")
|
||||
public class AtmosBlobStoreContextBuilderTest {
|
||||
|
||||
public void testNewBuilder() {
|
||||
AtmosBlobStoreContextBuilder builder = newBuilder();
|
||||
assertEquals(builder.getProperties().getProperty(PROPERTY_USER_METADATA_PREFIX), null);
|
||||
assertEquals(builder.getProperties().getProperty(AtmosStorageConstants.PROPERTY_EMCSAAS_UID),
|
||||
"id");
|
||||
assertEquals(builder.getProperties().getProperty(AtmosStorageConstants.PROPERTY_EMCSAAS_KEY),
|
||||
"secret");
|
||||
}
|
||||
|
||||
private AtmosBlobStoreContextBuilder newBuilder() {
|
||||
return new AtmosBlobStoreContextBuilder(new AtmosStoragePropertiesBuilder("id", "secret")
|
||||
.build()).withModules(new AtmosStorageStubClientModule());
|
||||
}
|
||||
|
||||
public void testBuildContext() {
|
||||
BlobStoreContext<AtmosStorageClient> context = newBuilder().buildContext();
|
||||
assertEquals(context.getClass(), BlobStoreContextImpl.class);
|
||||
assertEquals(context.getApi().getClass(), StubAtmosStorageClient.class);
|
||||
assertEquals(context.getBlobStore().getClass(), AtmosBlobStore.class);
|
||||
assertEquals(context.getApi().newObject().getClass(), AtmosObjectImpl.class);
|
||||
assertEquals(context.getBlobStore().newBlob().getClass(), BlobImpl.class);
|
||||
assertEquals(context.getAccount(), "id");
|
||||
assertEquals(context.getEndPoint(), URI.create("https://localhost/azurestub"));
|
||||
}
|
||||
|
||||
public void testBuildInjector() {
|
||||
Injector i = newBuilder().buildInjector();
|
||||
assert i.getInstance(Key.get(new TypeLiteral<BlobStoreContext<AtmosStorageClient>>() {
|
||||
})) != null;
|
||||
assert i.getInstance(AtmosObject.class) != null;
|
||||
assert i.getInstance(Blob.class) != null;
|
||||
}
|
||||
|
||||
protected void testAddContextModule() {
|
||||
List<Module> modules = new ArrayList<Module>();
|
||||
AtmosBlobStoreContextBuilder builder = newBuilder();
|
||||
builder.addContextModule(modules);
|
||||
assertEquals(modules.size(), 1);
|
||||
assertEquals(modules.get(0).getClass(), AtmosBlobStoreContextModule.class);
|
||||
}
|
||||
|
||||
protected void addClientModule() {
|
||||
List<Module> modules = new ArrayList<Module>();
|
||||
AtmosBlobStoreContextBuilder builder = newBuilder();
|
||||
builder.addClientModule(modules);
|
||||
assertEquals(modules.size(), 1);
|
||||
assertEquals(modules.get(0).getClass(), AtmosStorageRestClientModule.class);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
/**
|
||||
*
|
||||
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||
*
|
||||
* ====================================================================
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.atmosonline.saas.blobstore.config;
|
||||
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
import org.jclouds.atmosonline.saas.AtmosStorageClient;
|
||||
import org.jclouds.atmosonline.saas.blobstore.strategy.FindMD5InUserMetadata;
|
||||
import org.jclouds.atmosonline.saas.config.AtmosStorageStubClientModule;
|
||||
import org.jclouds.atmosonline.saas.reference.AtmosStorageConstants;
|
||||
import org.jclouds.blobstore.BlobStoreContext;
|
||||
import org.jclouds.blobstore.internal.BlobStoreContextImpl;
|
||||
import org.jclouds.blobstore.strategy.ContainsValueInListStrategy;
|
||||
import org.jclouds.concurrent.WithinThreadExecutorService;
|
||||
import org.jclouds.concurrent.config.ExecutorServiceModule;
|
||||
import org.jclouds.logging.jdk.config.JDKLoggingModule;
|
||||
import org.jclouds.util.Jsr330;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.Key;
|
||||
import com.google.inject.TypeLiteral;
|
||||
|
||||
/**
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Test(groups = "unit", testName = "emcsaas.AtmosBlobStoreModuleTest")
|
||||
public class AtmosBlobStoreModuleTest {
|
||||
|
||||
Injector createInjector() {
|
||||
return Guice.createInjector(new ExecutorServiceModule(new WithinThreadExecutorService()),
|
||||
new JDKLoggingModule(), new AtmosStorageStubClientModule(),
|
||||
new AtmosBlobStoreContextModule() {
|
||||
@Override
|
||||
protected void configure() {
|
||||
bindConstant().annotatedWith(
|
||||
Jsr330.named(AtmosStorageConstants.PROPERTY_EMCSAAS_UID)).to("user");
|
||||
bindConstant().annotatedWith(
|
||||
Jsr330.named(AtmosStorageConstants.PROPERTY_EMCSAAS_KEY)).to("key");
|
||||
bindConstant().annotatedWith(
|
||||
Jsr330.named(AtmosStorageConstants.PROPERTY_EMCSAAS_ENDPOINT)).to(
|
||||
"http://localhost");
|
||||
super.configure();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testContextImpl() {
|
||||
|
||||
Injector injector = createInjector();
|
||||
BlobStoreContext<AtmosStorageClient> handler = injector.getInstance(Key
|
||||
.get(new TypeLiteral<BlobStoreContext<AtmosStorageClient>>() {
|
||||
}));
|
||||
assertEquals(handler.getClass(), BlobStoreContextImpl.class);
|
||||
ContainsValueInListStrategy valueList = injector
|
||||
.getInstance(ContainsValueInListStrategy.class);
|
||||
|
||||
assertEquals(valueList.getClass(), FindMD5InUserMetadata.class);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
/**
|
||||
*
|
||||
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||
*
|
||||
* ====================================================================
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.atmosonline.saas.blobstore.integration;
|
||||
|
||||
import org.jclouds.atmosonline.saas.AtmosStorageClient;
|
||||
import org.jclouds.blobstore.integration.internal.BaseContainerIntegrationTest;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
/**
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Test(groups = { "integration", "live" }, testName = "emcsaas.AtmosStorageContainerIntegrationTest")
|
||||
public class AtmosStorageContainerIntegrationTest extends
|
||||
BaseContainerIntegrationTest<AtmosStorageClient> {
|
||||
|
||||
@Override
|
||||
@Test(enabled=false)
|
||||
// some reason this fails on the stub.
|
||||
public void testClearWhenContentsUnderPath() throws Exception {
|
||||
super.testClearWhenContentsUnderPath();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/**
|
||||
*
|
||||
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||
*
|
||||
* ====================================================================
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.atmosonline.saas.blobstore.integration;
|
||||
|
||||
import org.jclouds.atmosonline.saas.AtmosStorageClient;
|
||||
import org.jclouds.blobstore.integration.internal.BaseContainerLiveTest;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
/**
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Test(groups = { "live" }, testName = "emcsaas.AtmosStorageContainerLiveTest")
|
||||
public class AtmosStorageContainerLiveTest extends BaseContainerLiveTest<AtmosStorageClient> {
|
||||
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/**
|
||||
*
|
||||
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||
*
|
||||
* ====================================================================
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.atmosonline.saas.blobstore.integration;
|
||||
|
||||
import org.jclouds.atmosonline.saas.AtmosStorageClient;
|
||||
import org.jclouds.blobstore.integration.internal.BaseInputStreamMapIntegrationTest;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
/**
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Test(groups = { "integration", "live" }, testName = "emcsaas.AtmosStorageInputStreamMapIntegrationTest")
|
||||
public class AtmosStorageInputStreamMapIntegrationTest extends
|
||||
BaseInputStreamMapIntegrationTest<AtmosStorageClient> {
|
||||
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/**
|
||||
*
|
||||
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||
*
|
||||
* ====================================================================
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.atmosonline.saas.blobstore.integration;
|
||||
|
||||
import org.jclouds.atmosonline.saas.AtmosStorageClient;
|
||||
import org.jclouds.blobstore.integration.internal.BaseBlobIntegrationTest;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Test(groups = { "integration", "live" }, testName = "emcsaas.AtmosStorageIntegrationTest")
|
||||
public class AtmosStorageIntegrationTest extends BaseBlobIntegrationTest<AtmosStorageClient> {
|
||||
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/**
|
||||
*
|
||||
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||
*
|
||||
* ====================================================================
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.atmosonline.saas.blobstore.integration;
|
||||
|
||||
import org.jclouds.atmosonline.saas.AtmosStorageClient;
|
||||
import org.jclouds.blobstore.integration.internal.BaseBlobLiveTest;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Test(groups = { "live" }, testName = "emcsaas.AtmosStorageLiveTest")
|
||||
public class AtmosStorageLiveTest extends BaseBlobLiveTest<AtmosStorageClient> {
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/**
|
||||
*
|
||||
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||
*
|
||||
* ====================================================================
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.atmosonline.saas.blobstore.integration;
|
||||
|
||||
import org.jclouds.atmosonline.saas.AtmosStorageClient;
|
||||
import org.jclouds.blobstore.integration.internal.BaseBlobMapIntegrationTest;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
/**
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Test(groups = { "integration", "live" }, testName = "emcsaas.AtmosStorageMapIntegrationTest")
|
||||
public class AtmosStorageMapIntegrationTest extends BaseBlobMapIntegrationTest<AtmosStorageClient> {
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/**
|
||||
*
|
||||
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||
*
|
||||
* ====================================================================
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.atmosonline.saas.blobstore.integration;
|
||||
|
||||
import org.jclouds.atmosonline.saas.AtmosStorageClient;
|
||||
import org.jclouds.blobstore.integration.internal.BaseServiceIntegrationTest;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
/**
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Test(groups = { "integration", "live" }, testName = "emcsaas.AtmosStorageServiceIntegrationTest")
|
||||
public class AtmosStorageServiceIntegrationTest extends BaseServiceIntegrationTest<AtmosStorageClient> {
|
||||
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/**
|
||||
*
|
||||
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||
*
|
||||
* ====================================================================
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.atmosonline.saas.blobstore.integration;
|
||||
|
||||
import org.jclouds.atmosonline.saas.AtmosStorageClient;
|
||||
import org.jclouds.atmosonline.saas.AtmosStoragePropertiesBuilder;
|
||||
import org.jclouds.atmosonline.saas.blobstore.AtmosBlobStoreContextBuilder;
|
||||
import org.jclouds.atmosonline.saas.blobstore.AtmosBlobStoreContextFactory;
|
||||
import org.jclouds.atmosonline.saas.config.AtmosStorageStubClientModule;
|
||||
import org.jclouds.blobstore.BlobStoreContext;
|
||||
import org.jclouds.blobstore.integration.internal.BaseTestInitializer;
|
||||
import org.jclouds.logging.log4j.config.Log4JLoggingModule;
|
||||
|
||||
import com.google.inject.Module;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
public class AtmosStorageTestInitializer extends BaseTestInitializer<AtmosStorageClient> {
|
||||
|
||||
@Override
|
||||
protected BlobStoreContext<AtmosStorageClient> createLiveContext(Module configurationModule,
|
||||
String url, String app, String account, String key) {
|
||||
return new AtmosBlobStoreContextBuilder(new AtmosStoragePropertiesBuilder(
|
||||
account, key).relaxSSLHostname().build()).withModules(configurationModule,
|
||||
new Log4JLoggingModule()).buildContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BlobStoreContext<AtmosStorageClient> createStubContext() {
|
||||
return AtmosBlobStoreContextFactory.createContext("user", "pass",
|
||||
new AtmosStorageStubClientModule());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package org.jclouds.atmosonline.saas.config;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import org.jclouds.atmosonline.saas.AtmosStorage;
|
||||
import org.jclouds.atmosonline.saas.AtmosStorageClient;
|
||||
import org.jclouds.atmosonline.saas.internal.StubAtmosStorageClient;
|
||||
import org.jclouds.blobstore.integration.config.StubBlobStoreModule;
|
||||
import org.jclouds.rest.ConfiguresRestClient;
|
||||
|
||||
import com.google.inject.AbstractModule;
|
||||
|
||||
/**
|
||||
* adds a stub alternative to invoking AtmosStorage
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@ConfiguresRestClient
|
||||
public class AtmosStorageStubClientModule extends AbstractModule {
|
||||
|
||||
protected void configure() {
|
||||
install(new StubBlobStoreModule());
|
||||
bind(AtmosStorageClient.class).to(StubAtmosStorageClient.class).asEagerSingleton();
|
||||
bind(URI.class).annotatedWith(AtmosStorage.class).toInstance(URI.create("https://localhost/azurestub"));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,208 @@
|
|||
package org.jclouds.atmosonline.saas.internal;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.jclouds.atmosonline.saas.AtmosStorageClient;
|
||||
import org.jclouds.atmosonline.saas.blobstore.functions.BlobMetadataToObject;
|
||||
import org.jclouds.atmosonline.saas.blobstore.functions.BlobToObject;
|
||||
import org.jclouds.atmosonline.saas.blobstore.functions.ListOptionsToBlobStoreListOptions;
|
||||
import org.jclouds.atmosonline.saas.blobstore.functions.ObjectToBlob;
|
||||
import org.jclouds.atmosonline.saas.blobstore.functions.ResourceMetadataListToDirectoryEntryList;
|
||||
import org.jclouds.atmosonline.saas.domain.AtmosObject;
|
||||
import org.jclouds.atmosonline.saas.domain.BoundedSortedSet;
|
||||
import org.jclouds.atmosonline.saas.domain.DirectoryEntry;
|
||||
import org.jclouds.atmosonline.saas.domain.SystemMetadata;
|
||||
import org.jclouds.atmosonline.saas.domain.UserMetadata;
|
||||
import org.jclouds.atmosonline.saas.options.ListOptions;
|
||||
import org.jclouds.blobstore.KeyNotFoundException;
|
||||
import org.jclouds.blobstore.attr.ConsistencyModel;
|
||||
import org.jclouds.blobstore.attr.ConsistencyModels;
|
||||
import org.jclouds.blobstore.domain.BlobMetadata;
|
||||
import org.jclouds.blobstore.functions.HttpGetOptionsListToGetOptions;
|
||||
import org.jclouds.blobstore.integration.internal.StubBlobStore;
|
||||
import org.jclouds.concurrent.FutureFunctionWrapper;
|
||||
import org.jclouds.http.options.GetOptions;
|
||||
import org.jclouds.logging.Logger.LoggerFactory;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
|
||||
/**
|
||||
* Implementation of {@link AtmosStorageClient} which keeps all data in a local Map object.
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@ConsistencyModel(ConsistencyModels.STRICT)
|
||||
public class StubAtmosStorageClient implements AtmosStorageClient {
|
||||
private final HttpGetOptionsListToGetOptions httpGetOptionsConverter;
|
||||
private final StubBlobStore blobStore;
|
||||
private final LoggerFactory logFactory;
|
||||
private final AtmosObject.Factory objectProvider;
|
||||
private final ObjectToBlob object2Blob;
|
||||
private final BlobToObject blob2Object;
|
||||
private final BlobMetadataToObject blob2ObjectInfo;
|
||||
private final ListOptionsToBlobStoreListOptions container2ContainerListOptions;
|
||||
private final ResourceMetadataListToDirectoryEntryList resource2ObjectList;
|
||||
|
||||
@Inject
|
||||
private StubAtmosStorageClient(StubBlobStore blobStore, LoggerFactory logFactory,
|
||||
AtmosObject.Factory objectProvider,
|
||||
HttpGetOptionsListToGetOptions httpGetOptionsConverter, ObjectToBlob object2Blob,
|
||||
BlobToObject blob2Object, BlobMetadataToObject blob2ObjectInfo,
|
||||
ListOptionsToBlobStoreListOptions container2ContainerListOptions,
|
||||
ResourceMetadataListToDirectoryEntryList resource2ContainerList) {
|
||||
this.logFactory = logFactory;
|
||||
this.blobStore = blobStore;
|
||||
this.objectProvider = objectProvider;
|
||||
this.httpGetOptionsConverter = httpGetOptionsConverter;
|
||||
this.object2Blob = checkNotNull(object2Blob, "object2Blob");
|
||||
this.blob2Object = checkNotNull(blob2Object, "blob2Object");
|
||||
this.blob2ObjectInfo = checkNotNull(blob2ObjectInfo, "blob2ObjectInfo");
|
||||
this.container2ContainerListOptions = checkNotNull(container2ContainerListOptions,
|
||||
"container2ContainerListOptions");
|
||||
this.resource2ObjectList = checkNotNull(resource2ContainerList, "resource2ContainerList");
|
||||
}
|
||||
|
||||
protected <F, T> Future<T> wrapFuture(Future<? extends F> future, Function<F, T> function) {
|
||||
return new FutureFunctionWrapper<F, T>(future, function, logFactory.getLogger(function
|
||||
.getClass().getName()));
|
||||
}
|
||||
|
||||
public Future<URI> createDirectory(String directoryName) {
|
||||
final String container;
|
||||
if (directoryName.indexOf('/') != -1)
|
||||
container = directoryName.substring(0, directoryName.indexOf('/'));
|
||||
else
|
||||
container = directoryName;
|
||||
return wrapFuture(blobStore.createContainer(container), new Function<Boolean, URI>() {
|
||||
|
||||
public URI apply(Boolean from) {
|
||||
return URI.create("http://stub/containers/" + container);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
public Future<URI> createFile(String parent, AtmosObject object) {
|
||||
final String uri = "http://stub/containers/" + parent + "/"
|
||||
+ object.getContentMetadata().getName();
|
||||
String file = object.getContentMetadata().getName();
|
||||
String container = parent;
|
||||
if (parent.indexOf('/') != -1) {
|
||||
container = parent.substring(0, parent.indexOf('/'));
|
||||
String path = parent.substring(parent.indexOf('/') + 1);
|
||||
if (!path.equals(""))
|
||||
object.getContentMetadata().setName(path + "/" + file);
|
||||
}
|
||||
return wrapFuture(blobStore.putBlob(container, object2Blob.apply(object)),
|
||||
new Function<String, URI>() {
|
||||
|
||||
public URI apply(String from) {
|
||||
return URI.create(uri);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
public Future<Void> deletePath(String path) {
|
||||
if (path.indexOf('/') == -1)
|
||||
return wrapFuture(blobStore.deleteContainerImpl(path), new Function<Boolean, Void>() {
|
||||
|
||||
public Void apply(Boolean from) {
|
||||
return null;
|
||||
}
|
||||
|
||||
});
|
||||
else {
|
||||
String container = path.substring(0, path.indexOf('/'));
|
||||
path = path.substring(path.indexOf('/') + 1);
|
||||
return blobStore.removeBlob(container, path);
|
||||
}
|
||||
}
|
||||
|
||||
public SystemMetadata getSystemMetadata(String path) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public UserMetadata getUserMetadata(String path) {
|
||||
if (path.indexOf('/') == -1)
|
||||
throw new UnsupportedOperationException();
|
||||
else {
|
||||
String container = path.substring(0, path.indexOf('/'));
|
||||
path = path.substring(path.indexOf('/') + 1);
|
||||
return new Function<BlobMetadata, UserMetadata>() {
|
||||
|
||||
public UserMetadata apply(BlobMetadata from) {
|
||||
return blob2ObjectInfo.apply(from).getUserMetadata();
|
||||
}
|
||||
|
||||
}.apply(blobStore.blobMetadata(container, path));
|
||||
}
|
||||
}
|
||||
|
||||
public AtmosObject headFile(String path) {
|
||||
String container = path.substring(0, path.indexOf('/'));
|
||||
path = path.substring(path.indexOf('/') + 1);
|
||||
try {
|
||||
return this.blob2Object.apply(blobStore.getBlob(container, path).get());
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public Future<? extends BoundedSortedSet<? extends DirectoryEntry>> listDirectories(
|
||||
ListOptions... optionsList) {
|
||||
// org.jclouds.blobstore.options.ListOptions options = container2ContainerListOptions
|
||||
// .apply(optionsList);
|
||||
return wrapFuture(blobStore.list(), resource2ObjectList);
|
||||
}
|
||||
|
||||
public Future<? extends BoundedSortedSet<? extends DirectoryEntry>> listDirectory(
|
||||
String directoryName, ListOptions... optionsList) {
|
||||
org.jclouds.blobstore.options.ListContainerOptions options = container2ContainerListOptions
|
||||
.apply(optionsList);
|
||||
String container = directoryName;
|
||||
if (directoryName.indexOf('/') != -1) {
|
||||
container = directoryName.substring(0, directoryName.indexOf('/'));
|
||||
String path = directoryName.substring(directoryName.indexOf('/') + 1);
|
||||
if (!path.equals(""))
|
||||
options.underPath(path);
|
||||
}
|
||||
return wrapFuture(blobStore.list(container, options), resource2ObjectList);
|
||||
}
|
||||
|
||||
public AtmosObject newObject() {
|
||||
return this.objectProvider.create(null);
|
||||
}
|
||||
|
||||
public boolean pathExists(String path) {
|
||||
if (path.indexOf('/') == -1 || (path.endsWith("/")))
|
||||
return blobStore.exists(path);
|
||||
else {
|
||||
String container = path.substring(0, path.indexOf('/'));
|
||||
String blobName = path.substring(path.indexOf('/') + 1);
|
||||
try {
|
||||
blobStore.blobMetadata(container, blobName);
|
||||
return true;
|
||||
} catch (KeyNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Future<AtmosObject> readFile(String path, GetOptions... options) {
|
||||
String container = path.substring(0, path.indexOf('/'));
|
||||
String blobName = path.substring(path.indexOf('/') + 1);
|
||||
org.jclouds.blobstore.options.GetOptions getOptions = httpGetOptionsConverter.apply(options);
|
||||
return wrapFuture(blobStore.getBlob(container, blobName, getOptions), blob2Object);
|
||||
}
|
||||
|
||||
public Future<Void> updateFile(String parent, AtmosObject object) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package org.jclouds.atmosonline.saas.options;
|
||||
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
/**
|
||||
* Tests behavior of {@code ListOptions}
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Test(groups = "unit", testName = "emcsaas.ListOptionsTest")
|
||||
public class ListOptionsTest {
|
||||
|
||||
public void testToken() {
|
||||
ListOptions options = new ListOptions().token("a");
|
||||
assertEquals(ImmutableList.of("a"), options.buildRequestHeaders().get("x-emc-token"));
|
||||
}
|
||||
|
||||
public void testLimit() {
|
||||
int limit = 1;
|
||||
ListOptions options = new ListOptions().limit(limit);
|
||||
assertEquals(ImmutableList.of("1"), options.buildRequestHeaders().get("x-emc-limit"));
|
||||
}
|
||||
|
||||
public void testTokenStatic() {
|
||||
ListOptions options = ListOptions.Builder.token("a");
|
||||
assertEquals(ImmutableList.of("a"), options.buildRequestHeaders().get("x-emc-token"));
|
||||
}
|
||||
|
||||
public void testLimitStatic() {
|
||||
int limit = 1;
|
||||
ListOptions options = ListOptions.Builder.limit(limit);
|
||||
assertEquals(ImmutableList.of("1"), options.buildRequestHeaders().get("x-emc-limit"));
|
||||
}
|
||||
}
|
|
@ -96,17 +96,17 @@
|
|||
<priority value="DEBUG" />
|
||||
<appender-ref ref="ASYNCWIRE" />
|
||||
</category>
|
||||
|
||||
<category name="jclouds.http.wire">
|
||||
<priority value="DEBUG" />
|
||||
<appender-ref ref="ASYNCWIRE" />
|
||||
</category>
|
||||
|
||||
<category name="jclouds.signature">
|
||||
<priority value="DEBUG" />
|
||||
<appender-ref ref="ASYNCWIRE" />
|
||||
</category>
|
||||
<!--
|
||||
|
||||
<category name="jclouds.http.wire"> <priority value="DEBUG" />
|
||||
<appender-ref ref="ASYNCWIRE" /> </category> <category
|
||||
name="jclouds.signature"> <priority value="DEBUG" />
|
||||
<appender-ref ref="ASYNCWIRE" /> </category>
|
||||
-->
|
||||
|
||||
<!-- ======================= -->
|
||||
<!-- Setup the Root category -->
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
<module>core</module>
|
||||
</modules>
|
||||
<properties>
|
||||
<jclouds.test.initializer>org.jclouds.atmosonline.saas.integration.AtmosStorageTestInitializer</jclouds.test.initializer>
|
||||
<jclouds.test.initializer>org.jclouds.atmosonline.saas.blobstore.integration.AtmosStorageTestInitializer</jclouds.test.initializer>
|
||||
<jclouds.test.user>${jclouds.emcsaas.uid}</jclouds.test.user>
|
||||
<jclouds.test.key>${jclouds.emcsaas.key}</jclouds.test.key>
|
||||
</properties>
|
||||
|
|
Loading…
Reference in New Issue