Issue 111: first code for atmos online storage

git-svn-id: http://jclouds.googlecode.com/svn/trunk@2003 3d8758e0-26b5-11de-8745-db77d3ebf521
This commit is contained in:
adrian.f.cole 2009-10-30 03:22:04 +00:00
parent 7171f2c431
commit cfab82d775
50 changed files with 3605 additions and 0 deletions

68
atmosonline/pom.xml Normal file
View File

@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
$HeadURL$
$Revision$
$Date$
Copyright (C) 2009 Adrian Cole <adrian@jclouds.org>
====================================================================
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.html
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.
====================================================================
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>jclouds-project</artifactId>
<groupId>org.jclouds</groupId>
<version>1.0-SNAPSHOT</version>
<relativePath>../project/pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jclouds-atmosonline-project</artifactId>
<packaging>pom</packaging>
<name>jclouds atmosonline project</name>
<modules>
<module>saas</module>
</modules>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>jclouds-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>jclouds-core</artifactId>
<version>${project.version}</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.14</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>jclouds-log4j</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
$HeadURL$
$Revision$
$Date$
Copyright (C) 2009 Adrian Cole <adrian@jclouds.org>
====================================================================
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.html
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.
====================================================================
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>org.jclouds</groupId>
<artifactId>jclouds-emcsaas-project</artifactId>
<version>1.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.jclouds</groupId>
<artifactId>jclouds-emcsaas</artifactId>
<name>jclouds atmosonline storage service core</name>
<packaging>jar</packaging>
<description>jclouds Core components to access atmosonline saas</description>
<scm>
<connection>scm:svn:http://jclouds.googlecode.com/svn/trunk/mezo/saas/core</connection>
<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>
</project>

View File

@ -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;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.inject.Qualifier;
/**
* Related to a EMC Atmos Online Storage resource.
*
* @author Adrian Cole
*
*/
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = { ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Qualifier
public @interface AtmosStorage {
}

View File

@ -0,0 +1,98 @@
/**
*
* 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;
import java.net.URI;
import java.util.SortedSet;
import java.util.concurrent.Future;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
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.DirectoryEntry;
import org.jclouds.atmosonline.saas.filters.SignRequest;
import org.jclouds.atmosonline.saas.functions.AtmosObjectName;
import org.jclouds.atmosonline.saas.functions.ParseObjectFromHeadersAndHttpContent;
import org.jclouds.atmosonline.saas.xml.ListDirectoryResponseHandler;
import org.jclouds.blobstore.functions.ThrowKeyNotFoundOn404;
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.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.
* <p/>
*
* @see <a href="https://community.emc.com/community/labs/atmos_online" />
* @author Adrian Cole
*/
@Endpoint(AtmosStorage.class)
@RequestFilters(SignRequest.class)
@SkipEncoding( { '/' })
public interface AtmosStorageClient {
AtmosObject newObject();
@GET
@Path("/rest/namespace")
@XMLResponseParser(ListDirectoryResponseHandler.class)
@Consumes(MediaType.TEXT_XML)
SortedSet<DirectoryEntry> listDirectories();
@GET
@Path("/rest/namespace/{directoryName}/")
@XMLResponseParser(ListDirectoryResponseHandler.class)
@Consumes(MediaType.TEXT_XML)
SortedSet<DirectoryEntry> listDirectory(@PathParam("directoryName") String directoryName);
@POST
@Path("/rest/namespace/{directoryName}/")
@Consumes(MediaType.WILDCARD)
URI createDirectory(@PathParam("directoryName") String directoryName);
@POST
@Path("/rest/namespace/{parent}/{name}")
@Consumes(MediaType.WILDCARD)
Future<URI> createFile(
@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}")
Future<AtmosObject> readFile(@PathParam("path") String path, GetOptions... options);
}

View File

@ -0,0 +1,68 @@
/**
*
* 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;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.ExecutorService;
import org.jclouds.atmosonline.saas.config.AtmosStorageContextModule;
import org.jclouds.atmosonline.saas.config.AtmosStorageRestClientModule;
import org.jclouds.rest.RestContextBuilder;
import com.google.inject.Module;
import com.google.inject.TypeLiteral;
/**
*
* @author Adrian Cole
*/
public class AtmosStorageContextBuilder extends RestContextBuilder<AtmosStorageClient> {
public AtmosStorageContextBuilder(Properties props) {
super(new TypeLiteral<AtmosStorageClient>() {
}, props);
}
@Override
protected void addClientModule(List<Module> modules) {
modules.add(new AtmosStorageRestClientModule());
}
@Override
protected void addContextModule(List<Module> modules) {
modules.add(new AtmosStorageContextModule());
}
@Override
public AtmosStorageContextBuilder withExecutorService(ExecutorService service) {
return (AtmosStorageContextBuilder) super.withExecutorService(service);
}
@Override
public AtmosStorageContextBuilder withModules(Module... modules) {
return (AtmosStorageContextBuilder) super.withModules(modules);
}
}

View File

@ -0,0 +1,75 @@
/**
*
* 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;
import static com.google.common.base.Preconditions.checkNotNull;
import java.net.URI;
import java.util.Properties;
import org.jclouds.atmosonline.saas.reference.AtmosStorageConstants;
import org.jclouds.http.HttpPropertiesBuilder;
/**
* Builds properties used in AtmosStorage Connections
*
* @author Adrian Cole
*/
public class AtmosStoragePropertiesBuilder extends HttpPropertiesBuilder {
@Override
protected Properties defaultProperties() {
Properties properties = super.defaultProperties();
properties.setProperty(AtmosStorageConstants.PROPERTY_EMCSAAS_ENDPOINT,
"http://accesspoint.emccis.com");
properties.setProperty(AtmosStorageConstants.PROPERTY_EMCSAAS_SESSIONINTERVAL, "60");
return properties;
}
public AtmosStoragePropertiesBuilder(Properties properties) {
super(properties);
}
public AtmosStoragePropertiesBuilder(String uid, String key) {
super();
withCredentials(uid, key);
}
public AtmosStoragePropertiesBuilder withCredentials(String uid, String key) {
properties.setProperty(AtmosStorageConstants.PROPERTY_EMCSAAS_UID, checkNotNull(uid, "uid"));
properties.setProperty(AtmosStorageConstants.PROPERTY_EMCSAAS_KEY, checkNotNull(key, "key"));
return this;
}
public AtmosStoragePropertiesBuilder withEndpoint(URI endpoint) {
properties.setProperty(AtmosStorageConstants.PROPERTY_EMCSAAS_ENDPOINT, checkNotNull(
endpoint, "endpoint").toString());
return this;
}
public AtmosStoragePropertiesBuilder withTimeStampExpiration(long seconds) {
properties.setProperty(AtmosStorageConstants.PROPERTY_EMCSAAS_SESSIONINTERVAL, seconds + "");
return this;
}
}

View File

@ -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;
import org.jclouds.atmosonline.saas.domain.AtmosStorageError;
import org.jclouds.atmosonline.saas.handlers.ParseAtmosStorageErrorFromXmlContent;
import org.jclouds.http.HttpCommand;
import org.jclouds.http.HttpResponse;
import org.jclouds.http.HttpResponseException;
/**
* Encapsulates an Error from Atmos Storage Services.
*
* @see AtmosStorageError
* @see ParseAtmosStorageErrorFromXmlContent
* @author Adrian Cole
*
*/
public class AtmosStorageResponseException extends HttpResponseException {
private static final long serialVersionUID = 1L;
private AtmosStorageError error;
public AtmosStorageResponseException(HttpCommand command, HttpResponse response,
AtmosStorageError error) {
super(String.format("command %s failed with code %s, error: %s", command.toString(), response
.getStatusCode(), error.toString()), command, response);
this.setError(error);
}
public AtmosStorageResponseException(HttpCommand command, HttpResponse response,
AtmosStorageError error, Throwable cause) {
super(String.format("command %1$s failed with error: %2$s", command.toString(), error
.toString()), command, response, cause);
this.setError(error);
}
public AtmosStorageResponseException(String message, HttpCommand command, HttpResponse response,
AtmosStorageError error) {
super(message, command, response);
this.setError(error);
}
public AtmosStorageResponseException(String message, HttpCommand command, HttpResponse response,
AtmosStorageError error, Throwable cause) {
super(message, command, response, cause);
this.setError(error);
}
public void setError(AtmosStorageError error) {
this.error = error;
}
public AtmosStorageError getError() {
return error;
}
}

View File

@ -0,0 +1,39 @@
package org.jclouds.atmosonline.saas.binders;
import static com.google.common.base.Preconditions.checkNotNull;
import javax.inject.Inject;
import javax.ws.rs.core.HttpHeaders;
import org.jclouds.atmosonline.saas.domain.AtmosObject;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpUtils;
import org.jclouds.rest.Binder;
public class BindAtmosObjectToEntityAndMetadataToHeaders implements Binder {
private final BindUserMetadataToHeaders metaBinder;
@Inject
protected BindAtmosObjectToEntityAndMetadataToHeaders(BindUserMetadataToHeaders metaBinder) {
this.metaBinder = metaBinder;
}
public void bindToRequest(HttpRequest request, Object entity) {
AtmosObject object = (AtmosObject) entity;
request.setEntity(checkNotNull(object.getData(), "object.getContent()"));
request.getHeaders().put(
HttpHeaders.CONTENT_TYPE,
checkNotNull(object.getContentMetadata().getContentType(),
"object.metadata.contentType()"));
request.getHeaders().put(HttpHeaders.CONTENT_LENGTH,
object.getContentMetadata().getContentLength() + "");
if (object.getContentMetadata().getContentMD5() != null) {
request.getHeaders().put("Content-MD5",
HttpUtils.toBase64String(object.getContentMetadata().getContentMD5()));
}
metaBinder.bindToRequest(request, object.getUserMetadata());
}
}

View File

@ -0,0 +1,52 @@
package org.jclouds.atmosonline.saas.binders;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import org.jclouds.atmosonline.saas.domain.UserMetadata;
import org.jclouds.http.HttpRequest;
import org.jclouds.rest.Binder;
public class BindUserMetadataToHeaders implements Binder {
public void bindToRequest(HttpRequest request, Object entity) {
UserMetadata md = (UserMetadata) checkNotNull(entity, "entity");
if (md.getMetadata().size() > 0) {
String header = join(md.getMetadata());
request.getHeaders().put("x-emc-meta", header);
}
if (md.getListableMetadata().size() > 0) {
String header = join(md.getListableMetadata());
request.getHeaders().put("x-emc-listable-meta", header);
}
if (md.getTags().size() > 0) {
String header = join(md.getTags());
request.getHeaders().put("x-emc-tags", header);
}
if (md.getListableTags().size() > 0) {
String header = join(md.getListableTags());
request.getHeaders().put("x-emc-listable-tags", header);
}
}
private String join(Set<String> set) {
StringBuffer header = new StringBuffer();
for (String entry : set) {
header.append(entry).append(",");
}
header.deleteCharAt(header.length() - 1);
return header.toString();
}
private String join(Map<String, String> map) {
StringBuffer header = new StringBuffer();
for (Entry<String, String> entry : map.entrySet()) {
header.append(entry.getKey()).append("=").append(entry.getValue()).append(",");
}
header.deleteCharAt(header.length() - 1);
return header.toString();
}
}

View File

@ -0,0 +1,34 @@
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 org.jclouds.blobstore.domain.Blob.Factory;
import com.google.common.base.Function;
/**
* @author Adrian Cole
*/
@Singleton
public class ObjectToBlob implements Function<AtmosObject, Blob> {
private final Blob.Factory blobFactory;
private final ObjectToBlobMetadata object2BlobMd;
@Inject
ObjectToBlob(Factory blobFactory, ObjectToBlobMetadata object2BlobMd) {
this.blobFactory = blobFactory;
this.object2BlobMd = object2BlobMd;
}
public Blob apply(AtmosObject from) {
Blob blob = blobFactory.create(object2BlobMd.apply(from));
if (from.getContentMetadata().getContentLength() != null)
blob.setContentLength(from.getContentMetadata().getContentLength());
blob.setData(from.getData());
blob.setAllHeaders(from.getAllHeaders());
return blob;
}
}

View File

@ -0,0 +1,43 @@
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.functions.AtmosObjectName;
import org.jclouds.blobstore.domain.MutableBlobMetadata;
import org.jclouds.blobstore.domain.ResourceType;
import org.jclouds.blobstore.domain.internal.MutableBlobMetadataImpl;
import com.google.common.base.Function;
/**
* @author Adrian Cole
*/
@Singleton
public class ObjectToBlobMetadata implements Function<AtmosObject, MutableBlobMetadata> {
private final AtmosObjectName objectName;
@Inject
protected ObjectToBlobMetadata(AtmosObjectName objectName) {
this.objectName = objectName;
}
public MutableBlobMetadata apply(AtmosObject from) {
MutableBlobMetadata to = new MutableBlobMetadataImpl();
to.setId(from.getSystemMetadata().getObjectID());
to.setLastModified(from.getSystemMetadata().getLastUserDataModification());
to.setContentMD5(from.getContentMetadata().getContentMD5());
if (from.getContentMetadata().getContentType() != null)
to.setContentType(from.getContentMetadata().getContentType());
to.setName(objectName.apply(from));
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;
}
}

View File

@ -0,0 +1,61 @@
package org.jclouds.atmosonline.saas.config;
import javax.inject.Inject;
import javax.inject.Provider;
import org.jclouds.atmosonline.saas.domain.AtmosObject;
import org.jclouds.atmosonline.saas.domain.MutableContentMetadata;
import org.jclouds.atmosonline.saas.domain.SystemMetadata;
import org.jclouds.atmosonline.saas.domain.UserMetadata;
import org.jclouds.atmosonline.saas.domain.internal.AtmosObjectImpl;
import org.jclouds.blobstore.functions.CalculateSize;
import org.jclouds.blobstore.functions.GenerateMD5;
import org.jclouds.blobstore.functions.GenerateMD5Result;
import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import com.google.inject.Scopes;
/**
* Configures the domain object mappings needed for all Atmos implementations
*
* @author Adrian Cole
*/
public class AtmosObjectModule extends AbstractModule {
/**
* explicit factories are created here as it has been shown that Assisted Inject is extremely
* inefficient. http://code.google.com/p/google-guice/issues/detail?id=435
*/
@Override
protected void configure() {
bind(AtmosObject.Factory.class).to(AtmosObjectFactory.class).in(Scopes.SINGLETON);
}
private static class AtmosObjectFactory implements AtmosObject.Factory {
@Inject
GenerateMD5Result generateMD5Result;
@Inject
GenerateMD5 generateMD5;
@Inject
CalculateSize calculateSize;
@Inject
Provider<MutableContentMetadata> metadataProvider;
public AtmosObject create(MutableContentMetadata contentMetadata) {
return new AtmosObjectImpl(generateMD5Result, generateMD5, calculateSize,
contentMetadata != null ? contentMetadata : metadataProvider.get());
}
public AtmosObject create(SystemMetadata systemMetadata, UserMetadata userMetadata) {
return new AtmosObjectImpl(generateMD5Result, generateMD5, calculateSize, metadataProvider
.get(), systemMetadata, userMetadata);
}
}
@Provides
AtmosObject provideAtmosObject(AtmosObject.Factory factory) {
return factory.create((MutableContentMetadata) null);
}
}

View File

@ -0,0 +1,61 @@
/**
*
* 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.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.reference.AtmosStorageConstants;
import org.jclouds.blobstore.config.BlobStoreObjectModule;
import org.jclouds.http.RequiresHttp;
import org.jclouds.lifecycle.Closer;
import org.jclouds.rest.RestContext;
import org.jclouds.rest.internal.RestContextImpl;
import com.google.inject.AbstractModule;
import com.google.inject.Provides;
@RequiresHttp
public class AtmosStorageContextModule extends AbstractModule {
@Override
protected void configure() {
// for converters to work.
install(new BlobStoreObjectModule());
install(new AtmosObjectModule());
}
@Provides
@Singleton
RestContext<AtmosStorageClient> provideContext(Closer closer, AtmosStorageClient defaultApi,
@AtmosStorage URI endPoint,
@Named(AtmosStorageConstants.PROPERTY_EMCSAAS_UID) String account) {
return new RestContextImpl<AtmosStorageClient>(closer, defaultApi, endPoint, account);
}
}

View File

@ -0,0 +1,118 @@
/**
*
* 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.config;
import static org.jclouds.atmosonline.saas.reference.AtmosStorageConstants.PROPERTY_EMCSAAS_ENDPOINT;
import static org.jclouds.atmosonline.saas.reference.AtmosStorageConstants.PROPERTY_EMCSAAS_SESSIONINTERVAL;
import java.net.URI;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
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.handlers.AtmosStorageClientErrorRetryHandler;
import org.jclouds.atmosonline.saas.handlers.ParseAtmosStorageErrorFromXmlContent;
import org.jclouds.http.HttpErrorHandler;
import org.jclouds.http.HttpRetryHandler;
import org.jclouds.http.RequiresHttp;
import org.jclouds.http.annotation.ClientError;
import org.jclouds.http.annotation.Redirection;
import org.jclouds.http.annotation.ServerError;
import org.jclouds.rest.ConfiguresRestClient;
import org.jclouds.rest.RestClientFactory;
import org.jclouds.util.DateService;
import org.jclouds.util.TimeStamp;
import com.google.common.base.Function;
import com.google.common.collect.MapMaker;
import com.google.inject.AbstractModule;
import com.google.inject.Provides;
/**
* Configures the EMC Atmos Online Storage authentication service connection, including logging and
* http transport.
*
* @author Adrian Cole
*/
@ConfiguresRestClient
@RequiresHttp
public class AtmosStorageRestClientModule extends AbstractModule {
@Provides
@Singleton
@AtmosStorage
protected URI provideAuthenticationURI(@Named(PROPERTY_EMCSAAS_ENDPOINT) String endpoint) {
return URI.create(endpoint);
}
@Override
protected void configure() {
bindErrorHandlers();
bindRetryHandlers();
}
@Provides
protected AtmosStorageClient provideClient(RestClientFactory factory) {
return factory.create(AtmosStorageClient.class);
}
@Provides
@TimeStamp
protected String provideTimeStamp(@TimeStamp ConcurrentMap<String, String> cache) {
return cache.get("doesn't matter");
}
/**
* borrowing concurrency code to ensure that caching takes place properly
*/
@Provides
@TimeStamp
ConcurrentMap<String, String> provideTimeStampCache(
@Named(PROPERTY_EMCSAAS_SESSIONINTERVAL) long seconds, final DateService dateService) {
return new MapMaker().expiration(seconds, TimeUnit.SECONDS).makeComputingMap(
new Function<String, String>() {
public String apply(String key) {
return dateService.rfc822DateFormat();
}
});
}
protected void bindErrorHandlers() {
bind(HttpErrorHandler.class).annotatedWith(Redirection.class).to(
ParseAtmosStorageErrorFromXmlContent.class);
bind(HttpErrorHandler.class).annotatedWith(ClientError.class).to(
ParseAtmosStorageErrorFromXmlContent.class);
bind(HttpErrorHandler.class).annotatedWith(ServerError.class).to(
ParseAtmosStorageErrorFromXmlContent.class);
}
protected void bindRetryHandlers() {
bind(HttpRetryHandler.class).annotatedWith(ClientError.class).to(
AtmosStorageClientErrorRetryHandler.class);
}
}

View File

@ -0,0 +1,64 @@
package org.jclouds.atmosonline.saas.domain;
import java.io.IOException;
import com.google.common.collect.Multimap;
import com.google.inject.internal.Nullable;
/**
* Amazon Atmos is designed to store objects. Objects are stored in buckets and consist of a
* {@link ObjectMetadataAtmosObject#getData() value}, a {@link ObjectMetadata#getKey key},
* {@link ObjectMetadata#getUserMetadata() metadata}, and an access control policy.
*
* @author Adrian Cole
* @see <a
* href="http://docs.amazonwebservices.com/AmazonAtmos/2006-03-01/index.html?UsingObjects.html"
* />
*/
public interface AtmosObject extends Comparable<AtmosObject> {
public interface Factory {
AtmosObject create(@Nullable MutableContentMetadata contentMetadata);
AtmosObject create(SystemMetadata systemMetadata, UserMetadata userMetadata);
}
/**
* generate an MD5 Hash for the current data.
* <p/>
* <h2>Note</h2>
* <p/>
* If this is an InputStream, it will be converted to a byte array first.
*
* @throws IOException
* if there is a problem generating the hash.
*/
void generateMD5() throws IOException;
/**
* Sets entity for the request or the content from the response. If size isn't set, this will
* attempt to discover it.
*
* @param data
* typically InputStream for downloads, or File, byte [], String, or InputStream for
* uploads.
*/
void setData(Object data);
/**
* @return InputStream, if downloading, or whatever was set during {@link #setData(Object)}
*/
Object getData();
MutableContentMetadata getContentMetadata();
/**
* @return System and User metadata relevant to this object.
*/
SystemMetadata getSystemMetadata();
UserMetadata getUserMetadata();
Multimap<String, String> getAllHeaders();
void setAllHeaders(Multimap<String, String> allHeaders);
}

View File

@ -0,0 +1,69 @@
/**
*
* 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.domain;
/**
* When an Atmos Storage request is in error, the client receives an error response.
*
* Provides access to EMC Atmos Online Storage resources via their REST API.
*
* @author Adrian Cole
*
*/
public class AtmosStorageError {
private final int code;
private final String message;
private String stringSigned;
@Override
public String toString() {
return "AtmosStorageError [code=" + code + ", message=" + message + ", stringSigned="
+ stringSigned + "]";
}
public AtmosStorageError(int code, String message) {
this.code = code;
this.message = message;
}
public int getCode() {
return code;
}
public String getMessage() {
return message;
}
public void setStringSigned(String stringSigned) {
this.stringSigned = stringSigned;
}
/**
* @return what jclouds signed before sending the request.
*/
public String getStringSigned() {
return stringSigned;
}
}

View File

@ -0,0 +1,96 @@
/**
*
* 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.domain;
/**
* Metadata of a Atmos Online object
*
* @author Adrian Cole
*/
public class DirectoryEntry implements Comparable<DirectoryEntry> {
private final String objectid;
private final FileType type;
private final String objname;
public DirectoryEntry(String objectid, FileType type, String objname) {
this.objectid = objectid;
this.objname = objname;
this.type = type;
}
public String getObjectID() {
return objectid;
}
public String getObjectName() {
return objname;
}
public FileType getType() {
return type;
}
public int compareTo(DirectoryEntry o) {
if (getObjectName() == null)
return -1;
return (this == o) ? 0 : getObjectName().compareTo(o.getObjectName());
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((objectid == null) ? 0 : objectid.hashCode());
result = prime * result + ((objname == null) ? 0 : objname.hashCode());
result = prime * result + ((type == null) ? 0 : type.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
DirectoryEntry other = (DirectoryEntry) obj;
if (objectid == null) {
if (other.objectid != null)
return false;
} else if (!objectid.equals(other.objectid))
return false;
if (objname == null) {
if (other.objname != null)
return false;
} else if (!objname.equals(other.objname))
return false;
if (type == null) {
if (other.type != null)
return false;
} else if (!type.equals(other.type))
return false;
return true;
}
}

View File

@ -0,0 +1,38 @@
/**
*
* 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.domain;
public enum FileType {
DIRECTORY, REGULAR;
public String value() {
return name().toLowerCase();
}
public static FileType fromValue(String v) {
return valueOf(v.toUpperCase());
}
}

View File

@ -0,0 +1,105 @@
/**
*
* 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.domain;
import javax.ws.rs.core.MediaType;
/**
* metadata of the object
*
* @author Adrian Cole
*/
public class MutableContentMetadata {
private String name;
private Long contentLength;
private String contentType = MediaType.APPLICATION_OCTET_STREAM;
private byte[] contentMD5;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
/**
* Returns the total size of the downloaded object, or the chunk that's available.
* <p/>
* Chunking is only used when org.jclouds.http.GetOptions is called with options like tail,
* range, or startAt.
*
* @return the length in bytes that can be be obtained from {@link #getData()}
* @see org.jclouds.http.HttpHeaders#CONTENT_LENGTH
* @see GetObjectOptions
*/
public Long getContentLength() {
return contentLength;
}
/**
* @see #getContentLength
*/
public void setContentLength(long contentLength) {
this.contentLength = contentLength;
}
/**
*
* A standard MIME type describing the format of the contents. If none is provided, the default
* is binary/octet-stream.
*
* @see <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17"/>
*/
public String getContentType() {
return contentType;
}
/**
* @see #getContentType
*/
public void setContentType(String contentType) {
this.contentType = contentType;
}
/**
* The 128-bit MD5 digest of the message (without the headers) according to RFC 1864. This header
* can be used as a message integrity check to verify that the data is the same data that was
* originally sent. Although it is optional, we recommend using the Content-MD5 mechanism as an
* end-to-end integrity check.
*
*/
public byte[] getContentMD5() {
return contentMD5;
}
/**
* @see #getContentMD5
*/
public void setContentMD5(byte[] contentMD5) {
this.contentMD5 = contentMD5;
}
}

View File

@ -0,0 +1,163 @@
/**
*
* 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.domain;
import org.joda.time.DateTime;
/**
* Metadata of a Atmos Online object
*
* @author Adrian Cole
*/
public class SystemMetadata extends DirectoryEntry {
private final DateTime atime;
private final DateTime ctime;
private final String gid;
private final DateTime itime;
private final DateTime mtime;
private final int nlink;
private final String policyname;
private final long size;
private final String uid;
public SystemMetadata(DateTime atime, DateTime ctime, String gid, DateTime itime,
DateTime mtime, int nlink, String objectid, String objname, String policyname,
long size, FileType type, String uid) {
super(objectid, type, objname);
this.atime = atime;
this.ctime = ctime;
this.gid = gid;
this.itime = itime;
this.mtime = mtime;
this.nlink = nlink;
this.policyname = policyname;
this.size = size;
this.uid = uid;
}
public String getGroupID() {
return gid;
}
public int getHardLinkCount() {
return nlink;
}
public DateTime getInceptionTime() {
return itime;
}
public DateTime getLastAccessTime() {
return atime;
}
public DateTime getLastMetadataModification() {
return mtime;
}
public DateTime getLastUserDataModification() {
return ctime;
}
public String getPolicyName() {
return policyname;
}
public long getSize() {
return size;
}
public String getUserID() {
return uid;
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((atime == null) ? 0 : atime.hashCode());
result = prime * result + ((ctime == null) ? 0 : ctime.hashCode());
result = prime * result + ((gid == null) ? 0 : gid.hashCode());
result = prime * result + ((itime == null) ? 0 : itime.hashCode());
result = prime * result + ((mtime == null) ? 0 : mtime.hashCode());
result = prime * result + nlink;
result = prime * result + ((policyname == null) ? 0 : policyname.hashCode());
result = prime * result + (int) (size ^ (size >>> 32));
result = prime * result + ((uid == null) ? 0 : uid.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!super.equals(obj))
return false;
if (getClass() != obj.getClass())
return false;
SystemMetadata other = (SystemMetadata) obj;
if (atime == null) {
if (other.atime != null)
return false;
} else if (!atime.equals(other.atime))
return false;
if (ctime == null) {
if (other.ctime != null)
return false;
} else if (!ctime.equals(other.ctime))
return false;
if (gid == null) {
if (other.gid != null)
return false;
} else if (!gid.equals(other.gid))
return false;
if (itime == null) {
if (other.itime != null)
return false;
} else if (!itime.equals(other.itime))
return false;
if (mtime == null) {
if (other.mtime != null)
return false;
} else if (!mtime.equals(other.mtime))
return false;
if (nlink != other.nlink)
return false;
if (policyname == null) {
if (other.policyname != null)
return false;
} else if (!policyname.equals(other.policyname))
return false;
if (size != other.size)
return false;
if (uid == null) {
if (other.uid != null)
return false;
} else if (!uid.equals(other.uid))
return false;
return true;
}
}

View File

@ -0,0 +1,45 @@
/**
*
* 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.domain;
import java.net.URI;
public class UploadInfo {
private final String token;
private final URI host;
public UploadInfo(String token, URI host) {
this.token = token;
this.host = host;
}
public String getToken() {
return token;
}
public URI getHost() {
return host;
}
}

View File

@ -0,0 +1,61 @@
/**
*
* 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.domain;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
/**
* User metadata
*
* @author Adrian Cole
*/
public class UserMetadata {
private final SortedMap<String, String> metadata = Maps.newTreeMap();
private final SortedMap<String, String> listableMetadata = Maps.newTreeMap();
private final SortedSet<String> tags = Sets.newTreeSet();
private final SortedSet<String> listableTags = Sets.newTreeSet();
public Map<String, String> getMetadata() {
return metadata;
}
public Map<String, String> getListableMetadata() {
return listableMetadata;
}
public Set<String> getTags() {
return tags;
}
public Set<String> getListableTags() {
return listableTags;
}
}

View File

@ -0,0 +1,128 @@
package org.jclouds.atmosonline.saas.domain.internal;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import java.io.InputStream;
import org.jclouds.atmosonline.saas.domain.AtmosObject;
import org.jclouds.atmosonline.saas.domain.MutableContentMetadata;
import org.jclouds.atmosonline.saas.domain.SystemMetadata;
import org.jclouds.atmosonline.saas.domain.UserMetadata;
import org.jclouds.blobstore.domain.MD5InputStreamResult;
import org.jclouds.blobstore.functions.CalculateSize;
import org.jclouds.blobstore.functions.GenerateMD5;
import org.jclouds.blobstore.functions.GenerateMD5Result;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
/**
* Default Implementation of {@link AtmosObject}.
*
* @author Adrian Cole
*/
public class AtmosObjectImpl implements AtmosObject, Comparable<AtmosObject> {
private final UserMetadata userMetadata;
private final GenerateMD5Result generateMD5Result;
private final GenerateMD5 generateMD5;
private final CalculateSize calculateSize;
private final MutableContentMetadata contentMetadata;
private final SystemMetadata systemMetadata;
private Object data;
private Multimap<String, String> allHeaders = HashMultimap.create();
public AtmosObjectImpl(GenerateMD5Result generateMD5Result, GenerateMD5 generateMD5,
CalculateSize calculateSize, MutableContentMetadata contentMetadata) {
this(generateMD5Result, generateMD5, calculateSize, contentMetadata, null, new UserMetadata());
}
public AtmosObjectImpl(GenerateMD5Result generateMD5Result, GenerateMD5 generateMD5,
CalculateSize calculateSize, MutableContentMetadata contentMetadata,
SystemMetadata systemMetadata, UserMetadata userMetadata) {
this.generateMD5Result = generateMD5Result;
this.generateMD5 = generateMD5;
this.calculateSize = calculateSize;
this.contentMetadata = contentMetadata;
this.systemMetadata = systemMetadata;
this.userMetadata = userMetadata;
}
/**
* {@inheritDoc}
*/
public void generateMD5() {
checkState(data != null, "data");
if (data instanceof InputStream) {
MD5InputStreamResult result = generateMD5Result.apply((InputStream) data);
getContentMetadata().setContentMD5(result.md5);
getContentMetadata().setContentLength(result.length);
setData(result.data);
} else {
getContentMetadata().setContentMD5(generateMD5.apply(data));
}
}
/**
* {@inheritDoc}
*/
public Object getData() {
return data;
}
/**
* {@inheritDoc}
*/
public void setData(Object data) {
this.data = checkNotNull(data, "data");
if (getContentMetadata().getContentLength() == null) {
Long size = calculateSize.apply(data);
if (size != null)
getContentMetadata().setContentLength(size);
}
}
/**
* {@inheritDoc}
*/
public MutableContentMetadata getContentMetadata() {
return contentMetadata;
}
/**
* {@inheritDoc}
*/
public Multimap<String, String> getAllHeaders() {
return allHeaders;
}
/**
* {@inheritDoc}
*/
public void setAllHeaders(Multimap<String, String> allHeaders) {
this.allHeaders = checkNotNull(allHeaders, "allHeaders");
}
/**
* {@inheritDoc}
*/
public int compareTo(AtmosObject o) {
String name = getContentMetadata().getName() != null ? getContentMetadata().getName()
: getSystemMetadata().getObjectName();
if (name == null)
return -1;
String otherName = o.getContentMetadata().getName() != null ? o.getContentMetadata()
.getName() : o.getSystemMetadata().getObjectName();
return (this == o) ? 0 : name.compareTo(otherName);
}
public SystemMetadata getSystemMetadata() {
return systemMetadata;
}
public UserMetadata getUserMetadata() {
return userMetadata;
}
}

View File

@ -0,0 +1,158 @@
/**
*
* 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.filters;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
import java.util.TreeSet;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Provider;
import javax.inject.Singleton;
import javax.ws.rs.core.HttpHeaders;
import org.jclouds.atmosonline.saas.reference.AtmosStorageConstants;
import org.jclouds.atmosonline.saas.reference.AtmosStorageHeaders;
import org.jclouds.http.HttpException;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpRequestFilter;
import org.jclouds.http.HttpUtils;
import org.jclouds.util.TimeStamp;
import com.google.common.annotations.VisibleForTesting;
/**
* Signs the EMC Atmos Online Storage request.
*
* @see <a href="https://community.emc.com/community/labs/atmos_online" />
* @author Adrian Cole
*
*/
@Singleton
public class SignRequest implements HttpRequestFilter {
private final String uid;
private final byte[] key;
private final Provider<String> timeStampProvider;
@Inject
public SignRequest(@Named(AtmosStorageConstants.PROPERTY_EMCSAAS_UID) String uid,
@Named(AtmosStorageConstants.PROPERTY_EMCSAAS_KEY) String encodedKey,
@TimeStamp Provider<String> timeStampProvider) {
this.uid = uid;
this.key = HttpUtils.fromBase64String(encodedKey);
this.timeStampProvider = timeStampProvider;
}
public void filter(HttpRequest request) throws HttpException {
String toSign = replaceUIDHeader(request).replaceDateHeader(request).createStringToSign(
request);
calculateAndReplaceAuthHeader(request, toSign);
}
public String createStringToSign(HttpRequest request) {
StringBuilder buffer = new StringBuilder();
// re-sign the request
appendMethod(request, buffer);
appendHttpHeaders(request, buffer);
appendCanonicalizedResource(request, buffer);
appendCanonicalizedHeaders(request, buffer);
return buffer.toString();
}
private void calculateAndReplaceAuthHeader(HttpRequest request, String toSign)
throws HttpException {
String signature = signString(toSign);
request.getHeaders().replaceValues(AtmosStorageHeaders.SIGNATURE,
Collections.singletonList(signature));
}
public String signString(String toSign) {
String signature;
try {
signature = HttpUtils.hmacSha1Base64(toSign, key);
} catch (Exception e) {
throw new HttpException("error signing request", e);
}
return signature;
}
private void appendMethod(HttpRequest request, StringBuilder toSign) {
toSign.append(request.getMethod()).append("\n");
}
SignRequest replaceUIDHeader(HttpRequest request) {
request.getHeaders().replaceValues(AtmosStorageHeaders.UID, Collections.singletonList(uid));
return this;
}
SignRequest replaceDateHeader(HttpRequest request) {
request.getHeaders().replaceValues(HttpHeaders.DATE,
Collections.singletonList(timeStampProvider.get()));
return this;
}
private void appendCanonicalizedHeaders(HttpRequest request, StringBuilder toSign) {
// TreeSet == SortÊtheÊheadersÊalphabetically.
Set<String> headers = new TreeSet<String>(request.getHeaders().keySet());
for (String header : headers) {
if (header.startsWith("x-emc-")) {
// ConvertÊallÊheaderÊnamesÊtoÊlowercase.
toSign.append(header.toLowerCase()).append(":");
// ForÊheadersÊwithÊvaluesÊthatÊspanÊmultipleÊlines,ÊconvertÊthemÊintoÊoneÊlineÊbyÊreplacingÊanyÊ
// newlineÊcharactersÊandÊextraÊembeddedÊwhiteÊspacesÊinÊtheÊvalue.
for (String value : request.getHeaders().get(header))
toSign.append(value.replaceAll("\r?\n", "").replaceAll(" ", " ")).append(" ");
toSign.deleteCharAt(toSign.lastIndexOf(" "));
// ConcatenateÊallÊheadersÊtogether,ÊusingÊnewlinesÊ(\n)ÊseparatingÊeachÊheaderÊfromÊtheÊnextÊone.Ê
toSign.append("\n");
}
}
// ThereÊshouldÊbeÊnoÊterminatingÊnewlineÊcharacterÊatÊtheÊendÊofÊtheÊlastÊheader.
if (toSign.charAt(toSign.length() - 1) == '\n')
toSign.deleteCharAt(toSign.length() - 1);
}
@VisibleForTesting
void appendHttpHeaders(HttpRequest request, StringBuilder toSign) {
// OnlyÊtheÊvalueÊisÊused,ÊnotÊtheÊheaderÊ
// name.ÊIfÊaÊrequestÊdoesÊnotÊincludeÊtheÊheader,ÊthisÊisÊanÊemptyÊstring.
for (String header : new String[] { HttpHeaders.CONTENT_TYPE, "Range" })
toSign.append(valueOrEmpty(request.getHeaders().get(header)).toLowerCase()).append("\n");
// StandardÊHTTPÊheader,ÊinÊUTCÊformat.ÊOnlyÊtheÊdateÊvalueÊisÊused, notÊtheÊheaderÊname.
toSign.append(request.getHeaders().get(HttpHeaders.DATE).iterator().next()).append("\n");
}
@VisibleForTesting
void appendCanonicalizedResource(HttpRequest request, StringBuilder toSign) {
// PathÊportionÊofÊtheÊHTTPÊrequestÊURI,ÊinÊlowercase.
toSign.append(request.getEndpoint().getRawPath().toLowerCase()).append("\n");
}
private String valueOrEmpty(Collection<String> collection) {
return (collection != null && collection.size() >= 1) ? collection.iterator().next() : "";
}
}

View File

@ -0,0 +1,19 @@
package org.jclouds.atmosonline.saas.functions;
import org.jclouds.atmosonline.saas.domain.AtmosObject;
import com.google.common.base.Function;
/**
*
* @author Adrian Cole
*/
public class AtmosObjectName implements Function<Object, String> {
public String apply(Object in) {
AtmosObject from = (AtmosObject) in;
return from.getContentMetadata().getName() != null ? from.getContentMetadata().getName()
: from.getSystemMetadata().getObjectName();
}
}

View File

@ -0,0 +1,62 @@
package org.jclouds.atmosonline.saas.functions;
import javax.inject.Inject;
import javax.ws.rs.core.HttpHeaders;
import org.jclouds.atmosonline.saas.domain.AtmosObject;
import org.jclouds.blobstore.functions.ParseSystemAndUserMetadataFromHeaders;
import org.jclouds.http.HttpResponse;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
/**
* Parses response headers and creates a new AtmosObject from them and the HTTP content.
*
* @see ParseMetadataFromHeaders
* @author Adrian Cole
*/
public class ParseObjectFromHeadersAndHttpContent implements Function<HttpResponse, AtmosObject> {
private final ParseSystemMetadataFromHeaders systemMetadataParser;
private final ParseUserMetadataFromHeaders userMetadataParser;
private final AtmosObject.Factory objectProvider;
@Inject
public ParseObjectFromHeadersAndHttpContent(ParseSystemMetadataFromHeaders metadataParser,
ParseUserMetadataFromHeaders userMetadataParser, AtmosObject.Factory objectProvider) {
this.systemMetadataParser = metadataParser;
this.userMetadataParser = userMetadataParser;
this.objectProvider = objectProvider;
}
/**
* First, calls {@link ParseSystemAndUserMetadataFromHeaders}.
*
* Then, sets the object size based on the Content-Length header and adds the content to the
* {@link AtmosObject} result.
*
* @throws org.jclouds.http.HttpException
*/
public AtmosObject apply(HttpResponse from) {
AtmosObject object = objectProvider.create(systemMetadataParser.apply(from),
userMetadataParser.apply(from));
addAllHeadersTo(from, object);
object.setData(from.getContent());
String contentLength = from.getFirstHeaderOrNull(HttpHeaders.CONTENT_LENGTH);
if (contentLength != null) {
object.getContentMetadata().setContentLength(Long.parseLong(contentLength));
}
String contentType = from.getFirstHeaderOrNull(HttpHeaders.CONTENT_TYPE);
if (contentType != null) {
object.getContentMetadata().setContentType(contentType);
}
return object;
}
@VisibleForTesting
void addAllHeadersTo(HttpResponse from, AtmosObject object) {
object.getAllHeaders().putAll(from.getHeaders());
}
}

View File

@ -0,0 +1,74 @@
/**
*
* 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.functions;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Map;
import javax.inject.Inject;
import org.jclouds.atmosonline.saas.domain.FileType;
import org.jclouds.atmosonline.saas.domain.SystemMetadata;
import org.jclouds.atmosonline.saas.reference.AtmosStorageHeaders;
import org.jclouds.http.HttpResponse;
import org.jclouds.util.DateService;
import com.google.common.base.Function;
import com.google.common.collect.Maps;
/**
* @author Adrian Cole
*/
public class ParseSystemMetadataFromHeaders implements Function<HttpResponse, SystemMetadata> {
private final DateService dateService;
@Inject
public ParseSystemMetadataFromHeaders(DateService dateService) {
this.dateService = dateService;
}
public SystemMetadata apply(HttpResponse from) {
String meta = checkNotNull(from.getFirstHeaderOrNull(AtmosStorageHeaders.META),
AtmosStorageHeaders.META);
Map<String, String> metaMap = Maps.newHashMap();
String[] metas = meta.split(", ");
for (String entry : metas) {
String[] entrySplit = entry.split("=");
metaMap.put(entrySplit[0], entrySplit[1]);
}
assert metaMap.size() >= 12 : String.format("Should be 12 entries in %s", metaMap);
return new SystemMetadata(dateService.iso8601SecondsDateParse(checkNotNull(metaMap
.get("atime"), "atime")), dateService.iso8601SecondsDateParse(checkNotNull(metaMap
.get("ctime"), "ctime")), checkNotNull(metaMap.get("gid"), "gid"), dateService
.iso8601SecondsDateParse(checkNotNull(metaMap.get("itime"), "itime")), dateService
.iso8601SecondsDateParse(checkNotNull(metaMap.get("mtime"), "mtime")), Integer
.parseInt(checkNotNull(metaMap.get("nlink"), "nlink")), checkNotNull(metaMap
.get("objectid"), "objectid"), checkNotNull(metaMap.get("objname"), "objname"),
checkNotNull(metaMap.get("policyname"), "policyname"), Long.parseLong(checkNotNull(
metaMap.get("size"), "size")), FileType.fromValue(checkNotNull(metaMap
.get("type"), "type")), checkNotNull(metaMap.get("uid"), "uid"));
}
}

View File

@ -0,0 +1,85 @@
/**
*
* 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.functions;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Map;
import java.util.Set;
import org.jclouds.atmosonline.saas.domain.UserMetadata;
import org.jclouds.atmosonline.saas.reference.AtmosStorageHeaders;
import org.jclouds.http.HttpResponse;
import com.google.common.base.Function;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.inject.internal.ImmutableSet;
/**
* @author Adrian Cole
*/
public class ParseUserMetadataFromHeaders implements Function<HttpResponse, UserMetadata> {
private final Set<String> sysKeys = ImmutableSet.of("atime", "ctime", "gid", "itime", "mtime",
"nlink", "policyname", "size", "uid");
public UserMetadata apply(HttpResponse from) {
UserMetadata md = new UserMetadata();
Map<String, String> metaMap = getMetaMap(checkNotNull(from
.getFirstHeaderOrNull(AtmosStorageHeaders.META), AtmosStorageHeaders.META));
Set<String> keys = Sets.difference(metaMap.keySet(), sysKeys);
for (String key : keys) {
md.getMetadata().put(key, metaMap.get(key));
}
if (from.getFirstHeaderOrNull(AtmosStorageHeaders.LISTABLE_META) != null)
md.getListableMetadata().putAll(
getMetaMap(from.getFirstHeaderOrNull(AtmosStorageHeaders.LISTABLE_META)));
if (from.getFirstHeaderOrNull(AtmosStorageHeaders.TAGS) != null)
md.getTags().addAll(getTags(from.getFirstHeaderOrNull(AtmosStorageHeaders.TAGS)));
if (from.getFirstHeaderOrNull(AtmosStorageHeaders.LISTABLE_TAGS) != null)
md.getTags().addAll(getTags(from.getFirstHeaderOrNull(AtmosStorageHeaders.LISTABLE_TAGS)));
return md;
}
private Set<String> getTags(String meta) {
Set<String> tags = Sets.newTreeSet();
String[] metas = meta.split(", ");
for (String entry : metas) {
tags.add(entry);
}
return tags;
}
private Map<String, String> getMetaMap(String meta) {
Map<String, String> metaMap = Maps.newHashMap();
String[] metas = meta.split(", ");
for (String entry : metas) {
String[] entrySplit = entry.split("=");
metaMap.put(entrySplit[0], entrySplit[1]);
}
return metaMap;
}
}

View File

@ -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.handlers;
import javax.annotation.Resource;
import javax.inject.Inject;
import javax.inject.Named;
import org.jclouds.http.HttpCommand;
import org.jclouds.http.HttpConstants;
import org.jclouds.http.HttpResponse;
import org.jclouds.http.HttpRetryHandler;
import org.jclouds.logging.Logger;
import org.jclouds.util.Utils;
/**
* Handles Retryable responses with error codes in the 4xx range
*
* @author Adrian Cole
*/
public class AtmosStorageClientErrorRetryHandler implements HttpRetryHandler {
@Inject(optional = true)
@Named(HttpConstants.PROPERTY_HTTP_MAX_RETRIES)
private int retryCountLimit = 5;
@Resource
protected Logger logger = Logger.NULL;
public boolean shouldRetryRequest(HttpCommand command, HttpResponse response) {
Utils.closeClientButKeepContentStream(response);
command.incrementFailureCount();
if (!command.isReplayable()) {
logger.warn("Cannot retry after server error, command is not replayable: %1$s", command);
return false;
} else if (command.getFailureCount() > retryCountLimit) {
logger.warn(
"Cannot retry after server error, command has exceeded retry limit %1$d: %2$s",
retryCountLimit, command);
return false;
} else if (response.getStatusCode() == 409) {
return true;
}
return false;
}
}

View File

@ -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.handlers;
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.http.HttpCommand;
import org.jclouds.http.HttpErrorHandler;
import org.jclouds.http.HttpResponse;
import org.jclouds.http.HttpResponseException;
import org.jclouds.logging.Logger;
import org.jclouds.util.Utils;
/**
* This will parse and set an appropriate exception on the command object.
*
* @see AtmosStorageError
* @author Adrian Cole
*
*/
public class ParseAtmosStorageErrorFromXmlContent implements HttpErrorHandler {
@Resource
protected Logger logger = Logger.NULL;
private final AtmosStorageUtils utils;
@Inject
public ParseAtmosStorageErrorFromXmlContent(AtmosStorageUtils utils) {
this.utils = utils;
}
public void handleError(HttpCommand command, HttpResponse response) {
String content;
try {
content = response.getContent() != null ? Utils.toStringAndClose(response.getContent())
: null;
if (content != null) {
try {
if (content.indexOf('<') >= 0) {
AtmosStorageError error = utils.parseAtmosStorageErrorFromContent(command,
response, content);
command.setException(new AtmosStorageResponseException(command, response, error));
} else {
command.setException(new HttpResponseException(command, response, content));
}
} catch (Exception he) {
command.setException(new HttpResponseException(command, response, content));
Utils.rethrowIfRuntime(he);
}
} else {
command.setException(new HttpResponseException(command, response));
}
} catch (Exception e) {
command.setException(new HttpResponseException(command, response));
Utils.rethrowIfRuntime(e);
}
}
}

View File

@ -0,0 +1,39 @@
/**
*
* 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.reference;
/**
* Configuration properties and constants used in AtmosStorage connections.
*
* @author Adrian Cole
*/
public interface AtmosStorageConstants {
public static final String PROPERTY_EMCSAAS_ENDPOINT = "jclouds.emcsaas.endpoint";
public static final String PROPERTY_EMCSAAS_UID = "jclouds.emcsaas.uid";
public static final String PROPERTY_EMCSAAS_KEY = "jclouds.emcsaas.key";
/**
* 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";
}

View File

@ -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.reference;
/**
* Query parameters common to AtmosStorage apis.
*
* @see <a href="https://community.emc.com/community/labs/atmos_online" />
* @author Adrian Cole
*
*/
public interface AtmosStorageHeaders {
public static final String SIGNATURE = "x-emc-signature";
public static final String LISTABLE_META = "x-emc-listable-meta";
public static final String META = "x-emc-meta";
public static final String LISTABLE_TAGS = "x-emc-listable-tags";
public static final String TAGS = "x-emc-tags";
public static final String USER_ACL = "x-emc-useracl";
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";
}

View File

@ -0,0 +1,74 @@
/**
*
* 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.util;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import javax.inject.Inject;
import javax.inject.Provider;
import org.jclouds.atmosonline.saas.domain.AtmosStorageError;
import org.jclouds.atmosonline.saas.filters.SignRequest;
import org.jclouds.atmosonline.saas.xml.ErrorHandler;
import org.jclouds.http.HttpCommand;
import org.jclouds.http.HttpException;
import org.jclouds.http.HttpResponse;
import org.jclouds.http.functions.ParseSax;
/**
* Encryption, Hashing, and IO Utilities needed to sign and verify Atmos Storage requests and
* responses.
*
* @author Adrian Cole
*/
public class AtmosStorageUtils {
@Inject
SignRequest signer;
@Inject
ParseSax.Factory factory;
@Inject
Provider<ErrorHandler> errorHandlerProvider;
public AtmosStorageError parseAtmosStorageErrorFromContent(HttpCommand command,
HttpResponse response, InputStream content) throws HttpException {
AtmosStorageError error = (AtmosStorageError) factory.create(errorHandlerProvider.get())
.parse(content);
if (error.getCode() == 1032) {
error.setStringSigned(signer.createStringToSign(command.getRequest()));
}
return error;
}
public AtmosStorageError parseAtmosStorageErrorFromContent(HttpCommand command,
HttpResponse response, String content) throws HttpException {
return parseAtmosStorageErrorFromContent(command, response, new ByteArrayInputStream(content
.getBytes()));
}
}

View File

@ -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.xml;
import org.jclouds.atmosonline.saas.domain.AtmosStorageError;
import org.jclouds.http.functions.ParseSax;
/**
* Parses the error from the Atmos Online Storage REST API.
*
* @author Adrian Cole
*/
public class ErrorHandler extends ParseSax.HandlerWithResult<AtmosStorageError> {
private StringBuilder currentText = new StringBuilder();
private int code;
private String message;
public AtmosStorageError getResult() {
return new AtmosStorageError(code, message);
}
public void endElement(String uri, String name, String qName) {
if (qName.equals("Code")) {
this.code = Integer.parseInt(currentText.toString().trim());
} else if (qName.equals("Message")) {
this.message = currentText.toString().trim();
}
currentText = new StringBuilder();
}
public void characters(char ch[], int start, int length) {
currentText.append(ch, start, length);
}
}

View File

@ -0,0 +1,73 @@
/**
*
* 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.xml;
import java.util.SortedSet;
import org.jclouds.atmosonline.saas.domain.DirectoryEntry;
import org.jclouds.atmosonline.saas.domain.FileType;
import org.jclouds.http.functions.ParseSax;
import com.google.common.collect.Sets;
/**
* Parses an object list
* <p/>
*
* @see <a href="https://community.emc.com/community/labs/atmos_online" />
* @author Adrian Cole
*/
public class ListDirectoryResponseHandler extends
ParseSax.HandlerWithResult<SortedSet<DirectoryEntry>> {
private SortedSet<DirectoryEntry> entries = Sets.newTreeSet();
private String currentObjectId;
private FileType currentType;
private String currentName;
private StringBuilder currentText = new StringBuilder();
public SortedSet<DirectoryEntry> getResult() {
return entries;
}
public void endElement(String uri, String name, String qName) {
if (qName.equals("ObjectID")) {
currentObjectId = currentText.toString().trim();
} else if (qName.equals("FileType")) {
currentType = FileType.fromValue(currentText.toString().trim());
} else if (qName.equals("Filename")) {
currentName = currentText.toString().trim();
if (currentName.equals(""))
currentName = null;
} else if (qName.equals("DirectoryEntry")) {
entries.add(new DirectoryEntry(currentObjectId, currentType, currentName));
}
currentText = new StringBuilder();
}
public void characters(char ch[], int start, int length) {
currentText.append(ch, start, length);
}
}

View File

@ -0,0 +1,130 @@
/**
*
* 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;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.testng.Assert.assertEquals;
import java.io.IOException;
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.TimeUnit;
import org.apache.commons.io.IOUtils;
import org.jclouds.atmosonline.saas.domain.AtmosObject;
import org.jclouds.atmosonline.saas.domain.DirectoryEntry;
import org.jclouds.blobstore.integration.internal.BaseBlobStoreIntegrationTest;
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;
/**
* Tests behavior of {@code AtmosStorageClient}
*
* @author Adrian Cole
*/
@Test(groups = "live", sequential = true, testName = "emcsaas.AtmosStorageClientLiveTest")
public class AtmosStorageClientLiveTest {
protected AtmosStorageClient connection;
private String containerPrefix = BaseBlobStoreIntegrationTest.CONTAINER_PREFIX;
URI container1;
URI container2;
@BeforeGroups(groups = { "live" })
public void setupClient() {
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();
}
@Test
public void testListDirectorys() throws Exception {
SortedSet<DirectoryEntry> response = connection.listDirectories();
assert null != response;
}
String privateDirectory;
String publicDirectory;
String account;
@Test(timeOut = 5 * 60 * 1000)
public void testCreateDirectory() throws Exception {
boolean created = false;
while (!created) {
privateDirectory = containerPrefix + new SecureRandom().nextInt();
try {
created = connection.createDirectory(privateDirectory) != null;
} catch (UndeclaredThrowableException e) {
HttpResponseException htpe = (HttpResponseException) e.getCause().getCause();
if (htpe.getResponse().getStatusCode() == 409)
continue;
throw e;
}
}
SortedSet<DirectoryEntry> response = connection.listDirectories();
assert response.size() > 0;
for (DirectoryEntry id : response) {
SortedSet<DirectoryEntry> r2 = connection.listDirectory(id.getObjectName());
assert r2 != null;
}
}
@Test(timeOut = 5 * 60 * 1000, dependsOnMethods = { "testCreateDirectory" })
public void testFileOperations() throws Exception {
String data = "Here is my data";
// Test PUT with string data, ETag hash, and a piece of metadata
AtmosObject object = connection.newObject();
object.getContentMetadata().setName("object");
object.setData(data);
object.getContentMetadata().setContentLength(data.length());
object.generateMD5();
object.getContentMetadata().setContentType("text/plain");
object.getUserMetadata().getMetadata().put("Metadata", "metadata-value");
URI uri = connection.createFile(privateDirectory, object).get(30, TimeUnit.SECONDS);
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()));
assert getBlob.getContentMetadata().getContentType().startsWith("text/plain");
assertEquals(getBlob.getUserMetadata().getMetadata().get("Metadata"), "metadata-value");
try {
Utils.toStringAndClose(uri.toURL().openStream());
assert false : "shouldn't have worked, since it is private";
} catch (IOException e) {
}
}
}

View File

@ -0,0 +1,128 @@
/**
*
* 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;
import static org.testng.Assert.assertEquals;
import java.lang.reflect.Method;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import org.jclouds.atmosonline.saas.config.AtmosStorageRestClientModule;
import org.jclouds.atmosonline.saas.filters.SignRequest;
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.http.HttpUtils;
import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule;
import org.jclouds.http.functions.ParseSax;
import org.jclouds.http.functions.ParseURIFromListOrLocationHeaderIf20x;
import org.jclouds.logging.Logger;
import org.jclouds.logging.Logger.LoggerFactory;
import org.jclouds.rest.config.RestModule;
import org.jclouds.rest.internal.GeneratedHttpRequest;
import org.jclouds.rest.internal.RestAnnotationProcessor;
import org.jclouds.util.Jsr330;
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.TypeLiteral;
/**
* Tests behavior of {@code AtmosStorageClient}
*
* @author Adrian Cole
*/
@Test(groups = "unit", testName = "azureblob.AtmosStorageClientTest")
public class AtmosStorageClientTest {
public void testListDirectories() throws SecurityException, NoSuchMethodException {
Method method = AtmosStorageClient.class.getMethod("listDirectories");
GeneratedHttpRequest<AtmosStorageClient> httpMethod = processor.createRequest(method,
new Object[] {});
assertEquals(httpMethod.getRequestLine(),
"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);
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);
}
@BeforeClass
void setupFactory() {
Injector injector = Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
bindConstant().annotatedWith(
Jsr330.named(AtmosStorageConstants.PROPERTY_EMCSAAS_ENDPOINT)).to(
"http://accesspoint.emccis.com");
bindConstant().annotatedWith(Jsr330.named(AtmosStorageConstants.PROPERTY_EMCSAAS_UID))
.to("uid");
bindConstant().annotatedWith(Jsr330.named(AtmosStorageConstants.PROPERTY_EMCSAAS_KEY))
.to(HttpUtils.toBase64String("key".getBytes()));
bind(Logger.LoggerFactory.class).toInstance(new LoggerFactory() {
public Logger getLogger(String category) {
return Logger.NULL;
}
});
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;
}

View File

@ -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.binders;
import static org.testng.Assert.assertEquals;
import java.net.URI;
import org.jclouds.atmosonline.saas.domain.UserMetadata;
import org.jclouds.http.HttpRequest;
import org.testng.annotations.Test;
import com.google.inject.Guice;
import com.google.inject.Injector;
/**
* Tests behavior of {@code BindUserMetadataToHeaders}
*
* @author Adrian Cole
*/
@Test(groups = "unit", testName = "atmossaas.BindUserMetadataToHeadersTest")
public class BindUserMetadataToHeadersTest {
public void testMeta() {
Injector injector = Guice.createInjector();
BindUserMetadataToHeaders binder = injector
.getInstance(BindUserMetadataToHeaders.class);
UserMetadata metadata = new UserMetadata();
metadata.getMetadata().put("apple","bear");
metadata.getMetadata().put("sushi","king");
HttpRequest request = new HttpRequest("GET",URI.create("http://localhost"));
binder.bindToRequest(request, metadata);
assertEquals(request.getFirstHeaderOrNull("x-emc-meta"), "apple=bear,sushi=king");
}
public void testListableMeta() {
Injector injector = Guice.createInjector();
BindUserMetadataToHeaders binder = injector
.getInstance(BindUserMetadataToHeaders.class);
UserMetadata metadata = new UserMetadata();
metadata.getListableMetadata().put("apple","bear");
metadata.getListableMetadata().put("sushi","king");
HttpRequest request = new HttpRequest("GET",URI.create("http://localhost"));
binder.bindToRequest(request, metadata);
assertEquals(request.getFirstHeaderOrNull("x-emc-listable-meta"), "apple=bear,sushi=king");
}
public void testTags() {
Injector injector = Guice.createInjector();
BindUserMetadataToHeaders binder = injector
.getInstance(BindUserMetadataToHeaders.class);
UserMetadata tagsdata = new UserMetadata();
tagsdata.getTags().add("apple");
tagsdata.getTags().add("sushi");
HttpRequest request = new HttpRequest("GET",URI.create("http://localhost"));
binder.bindToRequest(request, tagsdata);
assertEquals(request.getFirstHeaderOrNull("x-emc-tags"), "apple,sushi");
}
public void testListableTags() {
Injector injector = Guice.createInjector();
BindUserMetadataToHeaders binder = injector
.getInstance(BindUserMetadataToHeaders.class);
UserMetadata tagsdata = new UserMetadata();
tagsdata.getListableTags().add("apple");
tagsdata.getListableTags().add("sushi");
HttpRequest request = new HttpRequest("GET",URI.create("http://localhost"));
binder.bindToRequest(request, tagsdata);
assertEquals(request.getFirstHeaderOrNull("x-emc-listable-tags"), "apple,sushi");
}
}

View File

@ -0,0 +1,113 @@
/**
*
* 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.config;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import java.util.concurrent.ConcurrentMap;
import org.bouncycastle.util.encoders.Base64;
import org.jclouds.atmosonline.saas.handlers.AtmosStorageClientErrorRetryHandler;
import org.jclouds.atmosonline.saas.handlers.ParseAtmosStorageErrorFromXmlContent;
import org.jclouds.atmosonline.saas.reference.AtmosStorageConstants;
import org.jclouds.http.functions.config.ParserModule;
import org.jclouds.http.handlers.DelegatingErrorHandler;
import org.jclouds.http.handlers.DelegatingRetryHandler;
import org.jclouds.http.handlers.RedirectionRetryHandler;
import org.jclouds.util.DateService;
import org.jclouds.util.Jsr330;
import org.testng.annotations.Test;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
/**
* @author Adrian Cole
*/
@Test(groups = "unit", testName = "emcsaas.AtmosStorageRestClientModuleTest")
public class AtmosStorageRestClientModuleTest {
Injector createInjector() {
return Guice.createInjector(new AtmosStorageRestClientModule(), new ParserModule(),
new AbstractModule() {
@Override
protected void configure() {
bindConstant().annotatedWith(
Jsr330.named(AtmosStorageConstants.PROPERTY_EMCSAAS_ENDPOINT)).to(
"http://localhost");
bindConstant().annotatedWith(
Jsr330.named(AtmosStorageConstants.PROPERTY_EMCSAAS_UID)).to("uid");
bindConstant().annotatedWith(
Jsr330.named(AtmosStorageConstants.PROPERTY_EMCSAAS_KEY)).to(
new String(Base64.encode("key".getBytes())));
bindConstant().annotatedWith(
Jsr330.named(AtmosStorageConstants.PROPERTY_EMCSAAS_SESSIONINTERVAL))
.to("2");
}
});
}
@Test
void testUpdatesOnlyOncePerSecond() throws NoSuchMethodException, InterruptedException {
AtmosStorageRestClientModule module = new AtmosStorageRestClientModule();
ConcurrentMap<String, String> map = module.provideTimeStampCache(1, new DateService());
String timeStamp = map.get("foo");
for (int i = 0; i < 10; i++)
map.get("foo");
assertEquals(timeStamp, map.get("foo"));
Thread.sleep(1001);
assertFalse(timeStamp.equals(map.get("foo")));
}
@Test
void testServerErrorHandler() {
DelegatingErrorHandler handler = createInjector().getInstance(DelegatingErrorHandler.class);
assertEquals(handler.getServerErrorHandler().getClass(),
ParseAtmosStorageErrorFromXmlContent.class);
}
@Test
void testClientErrorHandler() {
DelegatingErrorHandler handler = createInjector().getInstance(DelegatingErrorHandler.class);
assertEquals(handler.getClientErrorHandler().getClass(),
ParseAtmosStorageErrorFromXmlContent.class);
}
@Test
void testClientRetryHandler() {
DelegatingRetryHandler handler = createInjector().getInstance(DelegatingRetryHandler.class);
assertEquals(handler.getClientErrorRetryHandler().getClass(),
AtmosStorageClientErrorRetryHandler.class);
}
@Test
void testRedirectionRetryHandler() {
DelegatingRetryHandler handler = createInjector().getInstance(DelegatingRetryHandler.class);
assertEquals(handler.getRedirectionRetryHandler().getClass(), RedirectionRetryHandler.class);
}
}

View File

@ -0,0 +1,119 @@
/**
*
* 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.filters;
import static org.testng.Assert.assertEquals;
import java.io.IOException;
import java.net.URI;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import org.jclouds.atmosonline.saas.reference.AtmosStorageConstants;
import org.jclouds.atmosonline.saas.reference.AtmosStorageHeaders;
import org.jclouds.http.HttpRequest;
import org.jclouds.util.Jsr330;
import org.jclouds.util.TimeStamp;
import org.jclouds.util.Utils;
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.Provides;
@Test(groups = "unit", testName = "emcsaas.SignRequestTest")
public class SignRequestTest {
private static final String KEY = "LJLuryj6zs8ste6Y3jTGQp71xq0=";
private Injector injector;
private SignRequest filter;
@Test
void testCreateStringToSign() throws IOException {
String expects = Utils.toStringAndClose(getClass().getResourceAsStream("/hashstring.txt"));
HttpRequest request = newRequest();
String toSign = filter.replaceDateHeader(request).createStringToSign(request);
assertEquals(toSign, expects);
}
@Test
void testUid() throws IOException, NoSuchAlgorithmException, InvalidKeyException {
HttpRequest request = newRequest();
filter.replaceUIDHeader(request);
assertEquals(request.getFirstHeaderOrNull(AtmosStorageHeaders.UID), "user");
}
@Test
void testSignString() throws IOException, NoSuchAlgorithmException, InvalidKeyException {
String expects = "WHJo1MFevMnK4jCthJ974L3YHoo=";
HttpRequest request = newRequest();
String toSign = filter.replaceDateHeader(request).createStringToSign(request);
String signature = filter.signString(toSign);
assertEquals(signature, expects);
}
@BeforeClass
protected void createFilter() {
injector = Guice.createInjector(new AbstractModule() {
protected void configure() {
bindConstant().annotatedWith(Jsr330.named(AtmosStorageConstants.PROPERTY_EMCSAAS_UID))
.to("user");
bindConstant().annotatedWith(Jsr330.named(AtmosStorageConstants.PROPERTY_EMCSAAS_KEY))
.to(KEY);
}
@SuppressWarnings("unused")
@Provides
@TimeStamp
String getDate() {
return "Thu, 05 Jun 2008 16:38:19 GMT";
}
});
filter = injector.getInstance(SignRequest.class);
}
public HttpRequest newRequest() {
HttpRequest request = new HttpRequest("POST", URI.create("http://localhost/rest/objects"));
request.getHeaders().put(AtmosStorageHeaders.LISTABLE_META, "part4/part7/part8=quick");
request.getHeaders().put(AtmosStorageHeaders.META, "part1=buy");
request.getHeaders().put(HttpHeaders.ACCEPT, "*/*");
request.getHeaders().put(AtmosStorageHeaders.USER_ACL, "john=FULL_CONTROL,mary=WRITE");
request.getHeaders().put(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_OCTET_STREAM);
request.getHeaders().put(AtmosStorageHeaders.DATE, "Thu, 05 Jun 2008 16:38:19 GMT");
request.getHeaders().put(AtmosStorageHeaders.GROUP_ACL, "other=NONE");
request.getHeaders().put(HttpHeaders.HOST, "10.5.115.118");
request.getHeaders().put(HttpHeaders.CONTENT_LENGTH, "4286");
request.getHeaders().put(AtmosStorageHeaders.UID, "6039ac182f194e15b9261d73ce044939/user1");
return request;
}
}

View File

@ -0,0 +1,74 @@
/**
*
* 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.functions;
import static org.testng.Assert.assertEquals;
import org.jclouds.atmosonline.saas.domain.FileType;
import org.jclouds.atmosonline.saas.domain.SystemMetadata;
import org.jclouds.http.HttpResponse;
import org.jclouds.util.DateService;
import org.testng.annotations.Test;
import com.google.inject.Guice;
import com.google.inject.Injector;
/**
* Tests behavior of {@code ParseSystemMetadataFromHeaders}
*
* @author Adrian Cole
*/
@Test(groups = "unit", testName = "atmossaas.ParseSystemMetadataFromHeadersTest")
public class ParseSystemMetadataFromHeadersTest {
public void test() {
Injector injector = Guice.createInjector();
ParseSystemMetadataFromHeaders parser = injector
.getInstance(ParseSystemMetadataFromHeaders.class);
DateService dateService = injector.getInstance(DateService.class);
HttpResponse response = new HttpResponse();
response
.getHeaders()
.put(
"x-emc-meta",
"atime=2009-10-12T16:09:42Z, mtime=2009-10-19T04:37:00Z,"
+ " ctime=2009-10-19T04:37:00Z, itime=2009-10-12T16:09:42Z, type=directory, uid=root, "
+ "gid=rootr, objectid=4980cdb2b010109b04a44f7bb83f5f04ad354c638ae5, "
+ "objname=e913e09366364e9ba384b8fead643d43, size=4096, nlink=1, policyname=default");
SystemMetadata expected = new SystemMetadata(
dateService.iso8601SecondsDateParse("2009-10-12T16:09:42Z"), dateService
.iso8601SecondsDateParse("2009-10-19T04:37:00Z"), "rootr", dateService
.iso8601SecondsDateParse("2009-10-12T16:09:42Z"), dateService
.iso8601SecondsDateParse("2009-10-19T04:37:00Z"), 1,
"4980cdb2b010109b04a44f7bb83f5f04ad354c638ae5", "e913e09366364e9ba384b8fead643d43",
"default", 4096l, FileType.DIRECTORY, "root"
);
SystemMetadata data = parser.apply(response);
assertEquals(data, expected);
}
}

View File

@ -0,0 +1,55 @@
/**
*
* 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.xml;
import static org.testng.Assert.assertEquals;
import java.io.InputStream;
import org.jclouds.atmosonline.saas.domain.AtmosStorageError;
import org.jclouds.http.functions.BaseHandlerTest;
import org.jclouds.http.functions.ParseSax;
import org.testng.annotations.Test;
/**
* Tests behavior of {@code ErrorHandler}
*
* @author Adrian Cole
*/
@Test(groups = "unit", testName = "atmossaas.ErrorHandlerTest")
public class ErrorHandlerTest extends BaseHandlerTest {
ParseSax<AtmosStorageError> createParser() {
ParseSax<AtmosStorageError> parser = (ParseSax<AtmosStorageError>) factory.create(injector
.getInstance(ErrorHandler.class));
return parser;
}
public void testApplyInputStream() {
InputStream is = getClass().getResourceAsStream("/error.xml");
ParseSax<AtmosStorageError> parser = createParser();
AtmosStorageError result = parser.parse(is);
assertEquals(result.getCode(), 1003);
}
}

View File

@ -0,0 +1,64 @@
/**
*
* 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.xml;
import static org.testng.Assert.assertEquals;
import java.io.InputStream;
import java.util.SortedSet;
import org.jclouds.atmosonline.saas.domain.DirectoryEntry;
import org.jclouds.atmosonline.saas.domain.FileType;
import org.jclouds.http.functions.BaseHandlerTest;
import org.jclouds.http.functions.ParseSax;
import org.testng.annotations.Test;
import com.google.common.collect.Sets;
/**
* Tests behavior of {@code ListDirectoryResponseHandler}
*
* @author Adrian Cole
*/
@Test(groups = "unit", testName = "atmossaas.ListDirectoryResponseHandlerTest")
public class ListDirectoryResponseHandlerTest extends BaseHandlerTest {
ParseSax<SortedSet<DirectoryEntry>> createParser() {
ParseSax<SortedSet<DirectoryEntry>> parser = (ParseSax<SortedSet<DirectoryEntry>>) factory
.create(injector.getInstance(ListDirectoryResponseHandler.class));
return parser;
}
public void testApplyInputStreamBase() {
InputStream is = getClass().getResourceAsStream("/list_basic.xml");
ParseSax<SortedSet<DirectoryEntry>> parser = createParser();
SortedSet<DirectoryEntry> expected = Sets.newTreeSet();
expected.add(new DirectoryEntry("4980cdb2a411106a04a4538c92a1b204ad92077de6e3",
FileType.DIRECTORY, "adriancole-blobstore-2096685753"));
expected.add(new DirectoryEntry("4980cdb2a410105404980d99e53a0504ad93939e7dc3",
FileType.DIRECTORY, "adriancole-blobstore247496608"));
SortedSet<DirectoryEntry> result = parser.parse(is);
assertEquals(result, expected);
}
}

View File

@ -0,0 +1,5 @@
<?xml version='1.0' encoding='UTF-8'?>
<Error>
<Code>1003</Code>
<Message>The requested object was not found.</Message>
</Error>

View File

@ -0,0 +1,11 @@
POST
application/octet-stream
Thu, 05 Jun 2008 16:38:19 GMT
/rest/objects
x-emc-date:Thu, 05 Jun 2008 16:38:19 GMT
x-emc-groupacl:other=NONE
x-emc-listable-meta:part4/part7/part8=quick
x-emc-meta:part1=buy
x-emc-uid:6039ac182f194e15b9261d73ce044939/user1
x-emc-useracl:john=FULL_CONTROL,mary=WRITE

View File

@ -0,0 +1,40 @@
<?xml version='1.0' encoding='UTF-8'?>
<!--
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.
====================================================================
-->
<ListDirectoryResponse xmlns='http://www.emc.com/cos/'>
<DirectoryList>
<DirectoryEntry>
<ObjectID>4980cdb2a411106a04a4538c92a1b204ad92077de6e3</ObjectID>
<FileType>directory</FileType>
<Filename>adriancole-blobstore-2096685753</Filename>
</DirectoryEntry>
<DirectoryEntry>
<ObjectID>4980cdb2a410105404980d99e53a0504ad93939e7dc3</ObjectID>
<FileType>directory</FileType>
<Filename>adriancole-blobstore247496608</Filename>
</DirectoryEntry>
</DirectoryList>
</ListDirectoryResponse>

View File

@ -0,0 +1,95 @@
<?xml version='1.0' encoding='UTF-8'?>
<!--
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.
====================================================================
-->
<ListObjectsResponse xmlns='http://www.emc.com/cos/'>
<Object>
<ObjectID>499ad542a2a8bc200499ad5a7099940499b44f51e97d
</ObjectID>
<SystemMetadataList>
<Metadata>
<Name>atime</Name>
<Value>2009-02-17T23:15:01Z</Value>
</Metadata>
<Metadata>
<Name>mtime</Name>
<Value>2009-02-17T23:15:01Z</Value>
</Metadata>
<Metadata>
<Name>ctime</Name>
<Value>2009-02-17T23:15:01Z</Value>
</Metadata>
<Metadata>
<Name>itime</Name>
<Value>2009-02-17T23:15:01Z</Value>
</Metadata>
<Metadata>
<Name>type</Name>
<Value>regular</Value>
</Metadata>
<Metadata>
<Name>uid</Name>
<Value>user1</Value>
</Metadata>
<Metadata>
<Name>gid</Name>
<Value>apache</Value>
</Metadata>
<Metadata>
<Name>objectid</Name>
<Value>499ad542a2a8bc200499ad5a7099940499b44f51e97d
</Value>
</Metadata>
<Metadata>
<Name>objname</Name>
<Value></Value>
</Metadata>
<Metadata>
<Name>size</Name>
<Value>7589</Value>
</Metadata>
<Metadata>
<Name>nlink</Name>
<Value>0</Value>
</Metadata>
<Metadata>
<Name>policyname</Name>
<Value>default</Value>
</Metadata>
</SystemMetadataList>
<UserMetadataList>
<Metadata>
<Name>part1</Name>
<Value>order</Value>
<Listable>false</Listable>
</Metadata>
<Metadata>
<Name>part4/part7/part8</Name>
<Value>quick</Value>
<Listable>true</Listable>
</Metadata>
</UserMetadataList>
</Object>
</ListObjectsResponse>

View File

@ -0,0 +1,115 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
====================================================================
-->
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<!--
For more configuration infromation and examples see the Apache Log4j
website: http://logging.apache.org/log4j/
-->
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/"
debug="false">
<!-- A time/date based rolling appender -->
<appender name="WIREFILE" class="org.apache.log4j.DailyRollingFileAppender">
<param name="File" value="target/test-data/jclouds-wire.log" />
<param name="Append" value="true" />
<!-- Rollover at midnight each day -->
<param name="DatePattern" value="'.'yyyy-MM-dd" />
<param name="Threshold" value="TRACE" />
<layout class="org.apache.log4j.PatternLayout">
<!-- The default pattern: Date Priority [Category] Message\n -->
<param name="ConversionPattern" value="%d %-5p [%c] (%t) %m%n" />
<!--
The full pattern: Date MS Priority [Category] (Thread:NDC) Message\n
<param name="ConversionPattern" value="%d %-5r %-5p [%c] (%t:%x)
%m%n"/>
-->
</layout>
</appender>
<!-- A time/date based rolling appender -->
<appender name="FILE" class="org.apache.log4j.DailyRollingFileAppender">
<param name="File" value="target/test-data/jclouds.log" />
<param name="Append" value="true" />
<!-- Rollover at midnight each day -->
<param name="DatePattern" value="'.'yyyy-MM-dd" />
<param name="Threshold" value="TRACE" />
<layout class="org.apache.log4j.PatternLayout">
<!-- The default pattern: Date Priority [Category] Message\n -->
<param name="ConversionPattern" value="%d %-5p [%c] (%t) %m%n" />
<!--
The full pattern: Date MS Priority [Category] (Thread:NDC) Message\n
<param name="ConversionPattern" value="%d %-5r %-5p [%c] (%t:%x)
%m%n"/>
-->
</layout>
</appender>
<appender name="ASYNC" class="org.apache.log4j.AsyncAppender">
<appender-ref ref="FILE" />
</appender>
<appender name="ASYNCWIRE" class="org.apache.log4j.AsyncAppender">
<appender-ref ref="WIREFILE" />
</appender>
<!-- ================ -->
<!-- Limit categories -->
<!-- ================ -->
<category name="org.jclouds">
<priority value="DEBUG" />
<appender-ref ref="ASYNC" />
</category>
<category name="jclouds.http.headers">
<priority value="DEBUG" />
<appender-ref ref="ASYNCWIRE" />
</category>
<category name="jclouds.http.wire">
<priority value="DEBUG" />
<appender-ref ref="ASYNCWIRE" />
</category>
<!-- ======================= -->
<!-- Setup the Root category -->
<!-- ======================= -->
<root>
<priority value="WARN" />
</root>
</log4j:configuration>

60
atmosonline/saas/pom.xml Normal file
View File

@ -0,0 +1,60 @@
<!--
$HeadURL$
$Revision$
$Date$
Copyright (C) 2009 Adrian Cole <adrian@jclouds.org>
====================================================================
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.html
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.
====================================================================
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>jclouds-atmosonline-project</artifactId>
<groupId>org.jclouds</groupId>
<version>1.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jclouds-emcsaas-project</artifactId>
<packaging>pom</packaging>
<name>jclouds atmosonline storage service project</name>
<modules>
<module>core</module>
</modules>
<properties>
<jclouds.test.initializer>org.jclouds.atmosonline.saas.integration.AtmosStorageTestInitializer</jclouds.test.initializer>
<jclouds.test.user>${jclouds.emcsaas.uid}</jclouds.test.user>
<jclouds.test.key>${jclouds.emcsaas.key}</jclouds.test.key>
</properties>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>jclouds-blobstore-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>jclouds-blobstore-core</artifactId>
<version>${project.version}</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -48,6 +48,7 @@
<module>mezeo</module>
<module>nirvanix</module>
<module>vcloudx</module>
<module>atmosonline</module>
</modules>
<build>
<plugins>