diff --git a/atmosonline/pom.xml b/atmosonline/pom.xml new file mode 100644 index 0000000000..a54e40f158 --- /dev/null +++ b/atmosonline/pom.xml @@ -0,0 +1,68 @@ + + + + + jclouds-project + org.jclouds + 1.0-SNAPSHOT + ../project/pom.xml + + 4.0.0 + jclouds-atmosonline-project + pom + jclouds atmosonline project + + saas + + + + ${project.groupId} + jclouds-core + ${project.version} + + + ${project.groupId} + jclouds-core + ${project.version} + test-jar + test + + + log4j + log4j + 1.2.14 + test + + + ${project.groupId} + jclouds-log4j + ${project.version} + test + + + diff --git a/atmosonline/saas/core/pom.xml b/atmosonline/saas/core/pom.xml new file mode 100644 index 0000000000..12709c381f --- /dev/null +++ b/atmosonline/saas/core/pom.xml @@ -0,0 +1,48 @@ + + + + + org.jclouds + jclouds-emcsaas-project + 1.0-SNAPSHOT + ../pom.xml + + 4.0.0 + org.jclouds + jclouds-emcsaas + jclouds atmosonline storage service core + jar + jclouds Core components to access atmosonline saas + + + scm:svn:http://jclouds.googlecode.com/svn/trunk/mezo/saas/core + scm:svn:https://jclouds.googlecode.com/svn/trunk/mezo/saas/core + http://jclouds.googlecode.com/svn/trunk/mezo/saas/core + + + diff --git a/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/AtmosStorage.java b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/AtmosStorage.java new file mode 100644 index 0000000000..20e93e3c76 --- /dev/null +++ b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/AtmosStorage.java @@ -0,0 +1,44 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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 { + +} diff --git a/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/AtmosStorageClient.java b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/AtmosStorageClient.java new file mode 100644 index 0000000000..256f47111d --- /dev/null +++ b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/AtmosStorageClient.java @@ -0,0 +1,98 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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. + *

+ * + * @see + * @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 listDirectories(); + + @GET + @Path("/rest/namespace/{directoryName}/") + @XMLResponseParser(ListDirectoryResponseHandler.class) + @Consumes(MediaType.TEXT_XML) + SortedSet 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 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 readFile(@PathParam("path") String path, GetOptions... options); +} diff --git a/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/AtmosStorageContextBuilder.java b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/AtmosStorageContextBuilder.java new file mode 100755 index 0000000000..8e81785d9c --- /dev/null +++ b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/AtmosStorageContextBuilder.java @@ -0,0 +1,68 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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 { + + public AtmosStorageContextBuilder(Properties props) { + super(new TypeLiteral() { + }, props); + } + + @Override + protected void addClientModule(List modules) { + modules.add(new AtmosStorageRestClientModule()); + } + + @Override + protected void addContextModule(List 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); + } + +} diff --git a/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/AtmosStoragePropertiesBuilder.java b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/AtmosStoragePropertiesBuilder.java new file mode 100644 index 0000000000..cc6fe96759 --- /dev/null +++ b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/AtmosStoragePropertiesBuilder.java @@ -0,0 +1,75 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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; + } + +} diff --git a/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/AtmosStorageResponseException.java b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/AtmosStorageResponseException.java new file mode 100644 index 0000000000..7b2146eb5e --- /dev/null +++ b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/AtmosStorageResponseException.java @@ -0,0 +1,84 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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; + } + +} diff --git a/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/binders/BindAtmosObjectToEntityAndMetadataToHeaders.java b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/binders/BindAtmosObjectToEntityAndMetadataToHeaders.java new file mode 100644 index 0000000000..3dcf87f2bb --- /dev/null +++ b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/binders/BindAtmosObjectToEntityAndMetadataToHeaders.java @@ -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()); + } +} diff --git a/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/binders/BindUserMetadataToHeaders.java b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/binders/BindUserMetadataToHeaders.java new file mode 100644 index 0000000000..a451beeb9d --- /dev/null +++ b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/binders/BindUserMetadataToHeaders.java @@ -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 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 map) { + StringBuffer header = new StringBuffer(); + for (Entry entry : map.entrySet()) { + header.append(entry.getKey()).append("=").append(entry.getValue()).append(","); + } + header.deleteCharAt(header.length() - 1); + return header.toString(); + } +} diff --git a/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/blobstore/functions/ObjectToBlob.java b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/blobstore/functions/ObjectToBlob.java new file mode 100644 index 0000000000..8bfa4ac308 --- /dev/null +++ b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/blobstore/functions/ObjectToBlob.java @@ -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 { + 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; + } +} diff --git a/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/blobstore/functions/ObjectToBlobMetadata.java b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/blobstore/functions/ObjectToBlobMetadata.java new file mode 100644 index 0000000000..a9b70996c5 --- /dev/null +++ b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/blobstore/functions/ObjectToBlobMetadata.java @@ -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 { + 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; + } +} \ No newline at end of file diff --git a/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/config/AtmosObjectModule.java b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/config/AtmosObjectModule.java new file mode 100644 index 0000000000..75b9f768c1 --- /dev/null +++ b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/config/AtmosObjectModule.java @@ -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 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); + } + +} \ No newline at end of file diff --git a/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/config/AtmosStorageContextModule.java b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/config/AtmosStorageContextModule.java new file mode 100644 index 0000000000..9693b4ca98 --- /dev/null +++ b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/config/AtmosStorageContextModule.java @@ -0,0 +1,61 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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 provideContext(Closer closer, AtmosStorageClient defaultApi, + @AtmosStorage URI endPoint, + @Named(AtmosStorageConstants.PROPERTY_EMCSAAS_UID) String account) { + return new RestContextImpl(closer, defaultApi, endPoint, account); + } + +} diff --git a/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/config/AtmosStorageRestClientModule.java b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/config/AtmosStorageRestClientModule.java new file mode 100644 index 0000000000..09511eec92 --- /dev/null +++ b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/config/AtmosStorageRestClientModule.java @@ -0,0 +1,118 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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 cache) { + return cache.get("doesn't matter"); + } + + /** + * borrowing concurrency code to ensure that caching takes place properly + */ + @Provides + @TimeStamp + ConcurrentMap provideTimeStampCache( + @Named(PROPERTY_EMCSAAS_SESSIONINTERVAL) long seconds, final DateService dateService) { + return new MapMaker().expiration(seconds, TimeUnit.SECONDS).makeComputingMap( + new Function() { + 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); + } + +} diff --git a/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/domain/AtmosObject.java b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/domain/AtmosObject.java new file mode 100644 index 0000000000..bb1c9cb509 --- /dev/null +++ b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/domain/AtmosObject.java @@ -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 + */ +public interface AtmosObject extends Comparable { + public interface Factory { + AtmosObject create(@Nullable MutableContentMetadata contentMetadata); + + AtmosObject create(SystemMetadata systemMetadata, UserMetadata userMetadata); + } + + /** + * generate an MD5 Hash for the current data. + *

+ *

Note

+ *

+ * 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 getAllHeaders(); + + void setAllHeaders(Multimap allHeaders); +} \ No newline at end of file diff --git a/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/domain/AtmosStorageError.java b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/domain/AtmosStorageError.java new file mode 100644 index 0000000000..4152122a6e --- /dev/null +++ b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/domain/AtmosStorageError.java @@ -0,0 +1,69 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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; + } + +} diff --git a/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/domain/DirectoryEntry.java b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/domain/DirectoryEntry.java new file mode 100644 index 0000000000..30a1d0a7db --- /dev/null +++ b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/domain/DirectoryEntry.java @@ -0,0 +1,96 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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 { + 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; + } +} \ No newline at end of file diff --git a/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/domain/FileType.java b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/domain/FileType.java new file mode 100644 index 0000000000..78b7df9493 --- /dev/null +++ b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/domain/FileType.java @@ -0,0 +1,38 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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()); + } + +} diff --git a/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/domain/MutableContentMetadata.java b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/domain/MutableContentMetadata.java new file mode 100644 index 0000000000..6579c6f0e3 --- /dev/null +++ b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/domain/MutableContentMetadata.java @@ -0,0 +1,105 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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. + *

+ * 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 + */ + 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; + } + +} \ No newline at end of file diff --git a/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/domain/SystemMetadata.java b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/domain/SystemMetadata.java new file mode 100644 index 0000000000..b3d5ca38d8 --- /dev/null +++ b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/domain/SystemMetadata.java @@ -0,0 +1,163 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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; + } + +} \ No newline at end of file diff --git a/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/domain/UploadInfo.java b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/domain/UploadInfo.java new file mode 100644 index 0000000000..afb826b336 --- /dev/null +++ b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/domain/UploadInfo.java @@ -0,0 +1,45 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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; + } + +} diff --git a/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/domain/UserMetadata.java b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/domain/UserMetadata.java new file mode 100644 index 0000000000..3a044a78da --- /dev/null +++ b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/domain/UserMetadata.java @@ -0,0 +1,61 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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 metadata = Maps.newTreeMap(); + private final SortedMap listableMetadata = Maps.newTreeMap(); + private final SortedSet tags = Sets.newTreeSet(); + private final SortedSet listableTags = Sets.newTreeSet(); + + public Map getMetadata() { + return metadata; + } + + public Map getListableMetadata() { + return listableMetadata; + } + + public Set getTags() { + return tags; + } + + public Set getListableTags() { + return listableTags; + } + +} \ No newline at end of file diff --git a/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/domain/internal/AtmosObjectImpl.java b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/domain/internal/AtmosObjectImpl.java new file mode 100644 index 0000000000..a0f6dc40e1 --- /dev/null +++ b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/domain/internal/AtmosObjectImpl.java @@ -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 { + 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 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 getAllHeaders() { + return allHeaders; + } + + /** + * {@inheritDoc} + */ + public void setAllHeaders(Multimap 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; + } + +} diff --git a/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/filters/SignRequest.java b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/filters/SignRequest.java new file mode 100644 index 0000000000..8bb6769639 --- /dev/null +++ b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/filters/SignRequest.java @@ -0,0 +1,158 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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 + * @author Adrian Cole + * + */ +@Singleton +public class SignRequest implements HttpRequestFilter { + + private final String uid; + private final byte[] key; + private final Provider timeStampProvider; + + @Inject + public SignRequest(@Named(AtmosStorageConstants.PROPERTY_EMCSAAS_UID) String uid, + @Named(AtmosStorageConstants.PROPERTY_EMCSAAS_KEY) String encodedKey, + @TimeStamp Provider 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 headers = new TreeSet(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 collection) { + return (collection != null && collection.size() >= 1) ? collection.iterator().next() : ""; + } +} \ No newline at end of file diff --git a/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/functions/AtmosObjectName.java b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/functions/AtmosObjectName.java new file mode 100644 index 0000000000..08f74acecd --- /dev/null +++ b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/functions/AtmosObjectName.java @@ -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 { + + public String apply(Object in) { + AtmosObject from = (AtmosObject) in; + return from.getContentMetadata().getName() != null ? from.getContentMetadata().getName() + : from.getSystemMetadata().getObjectName(); + } + +} \ No newline at end of file diff --git a/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/functions/ParseObjectFromHeadersAndHttpContent.java b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/functions/ParseObjectFromHeadersAndHttpContent.java new file mode 100644 index 0000000000..ca45b23a83 --- /dev/null +++ b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/functions/ParseObjectFromHeadersAndHttpContent.java @@ -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 { + + 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()); + } + +} \ No newline at end of file diff --git a/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/functions/ParseSystemMetadataFromHeaders.java b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/functions/ParseSystemMetadataFromHeaders.java new file mode 100644 index 0000000000..a93d13352c --- /dev/null +++ b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/functions/ParseSystemMetadataFromHeaders.java @@ -0,0 +1,74 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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 { + 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 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")); + } +} \ No newline at end of file diff --git a/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/functions/ParseUserMetadataFromHeaders.java b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/functions/ParseUserMetadataFromHeaders.java new file mode 100644 index 0000000000..069cbb5e06 --- /dev/null +++ b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/functions/ParseUserMetadataFromHeaders.java @@ -0,0 +1,85 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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 { + private final Set sysKeys = ImmutableSet.of("atime", "ctime", "gid", "itime", "mtime", + "nlink", "policyname", "size", "uid"); + + public UserMetadata apply(HttpResponse from) { + UserMetadata md = new UserMetadata(); + Map metaMap = getMetaMap(checkNotNull(from + .getFirstHeaderOrNull(AtmosStorageHeaders.META), AtmosStorageHeaders.META)); + + Set 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 getTags(String meta) { + Set tags = Sets.newTreeSet(); + String[] metas = meta.split(", "); + for (String entry : metas) { + tags.add(entry); + } + return tags; + } + + private Map getMetaMap(String meta) { + Map metaMap = Maps.newHashMap(); + String[] metas = meta.split(", "); + for (String entry : metas) { + String[] entrySplit = entry.split("="); + metaMap.put(entrySplit[0], entrySplit[1]); + } + return metaMap; + } +} \ No newline at end of file diff --git a/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/handlers/AtmosStorageClientErrorRetryHandler.java b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/handlers/AtmosStorageClientErrorRetryHandler.java new file mode 100644 index 0000000000..e99e3ec8bd --- /dev/null +++ b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/handlers/AtmosStorageClientErrorRetryHandler.java @@ -0,0 +1,67 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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; + } + +} diff --git a/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/handlers/ParseAtmosStorageErrorFromXmlContent.java b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/handlers/ParseAtmosStorageErrorFromXmlContent.java new file mode 100644 index 0000000000..1c6df8b099 --- /dev/null +++ b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/handlers/ParseAtmosStorageErrorFromXmlContent.java @@ -0,0 +1,84 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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); + } + } + +} \ No newline at end of file diff --git a/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/reference/AtmosStorageConstants.java b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/reference/AtmosStorageConstants.java new file mode 100644 index 0000000000..5c23c93e28 --- /dev/null +++ b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/reference/AtmosStorageConstants.java @@ -0,0 +1,39 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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"; +} diff --git a/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/reference/AtmosStorageHeaders.java b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/reference/AtmosStorageHeaders.java new file mode 100644 index 0000000000..294c05f832 --- /dev/null +++ b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/reference/AtmosStorageHeaders.java @@ -0,0 +1,44 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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 + * @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"; +} diff --git a/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/util/AtmosStorageUtils.java b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/util/AtmosStorageUtils.java new file mode 100644 index 0000000000..a7e3c6326a --- /dev/null +++ b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/util/AtmosStorageUtils.java @@ -0,0 +1,74 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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 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())); + } + +} \ No newline at end of file diff --git a/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/xml/ErrorHandler.java b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/xml/ErrorHandler.java new file mode 100644 index 0000000000..1c4ae1aaf5 --- /dev/null +++ b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/xml/ErrorHandler.java @@ -0,0 +1,57 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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 { + + 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); + } +} diff --git a/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/xml/ListDirectoryResponseHandler.java b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/xml/ListDirectoryResponseHandler.java new file mode 100644 index 0000000000..0f72ab4804 --- /dev/null +++ b/atmosonline/saas/core/src/main/java/org/jclouds/atmosonline/saas/xml/ListDirectoryResponseHandler.java @@ -0,0 +1,73 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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 + *

+ * + * @see + * @author Adrian Cole + */ +public class ListDirectoryResponseHandler extends + ParseSax.HandlerWithResult> { + + private SortedSet entries = Sets.newTreeSet(); + private String currentObjectId; + private FileType currentType; + private String currentName; + + private StringBuilder currentText = new StringBuilder(); + + public SortedSet 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); + } +} diff --git a/atmosonline/saas/core/src/test/java/org/jclouds/atmosonline/saas/AtmosStorageClientLiveTest.java b/atmosonline/saas/core/src/test/java/org/jclouds/atmosonline/saas/AtmosStorageClientLiveTest.java new file mode 100644 index 0000000000..ff6142c4b4 --- /dev/null +++ b/atmosonline/saas/core/src/test/java/org/jclouds/atmosonline/saas/AtmosStorageClientLiveTest.java @@ -0,0 +1,130 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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 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 response = connection.listDirectories(); + assert response.size() > 0; + for (DirectoryEntry id : response) { + SortedSet 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) { + + } + } +} diff --git a/atmosonline/saas/core/src/test/java/org/jclouds/atmosonline/saas/AtmosStorageClientTest.java b/atmosonline/saas/core/src/test/java/org/jclouds/atmosonline/saas/AtmosStorageClientTest.java new file mode 100644 index 0000000000..6d1a60d810 --- /dev/null +++ b/atmosonline/saas/core/src/test/java/org/jclouds/atmosonline/saas/AtmosStorageClientTest.java @@ -0,0 +1,128 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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 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 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 processor; +} diff --git a/atmosonline/saas/core/src/test/java/org/jclouds/atmosonline/saas/binders/BindUserMetadataToHeadersTest.java b/atmosonline/saas/core/src/test/java/org/jclouds/atmosonline/saas/binders/BindUserMetadataToHeadersTest.java new file mode 100644 index 0000000000..156be00090 --- /dev/null +++ b/atmosonline/saas/core/src/test/java/org/jclouds/atmosonline/saas/binders/BindUserMetadataToHeadersTest.java @@ -0,0 +1,97 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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"); + } +} diff --git a/atmosonline/saas/core/src/test/java/org/jclouds/atmosonline/saas/config/AtmosStorageRestClientModuleTest.java b/atmosonline/saas/core/src/test/java/org/jclouds/atmosonline/saas/config/AtmosStorageRestClientModuleTest.java new file mode 100644 index 0000000000..4eb2b48e2d --- /dev/null +++ b/atmosonline/saas/core/src/test/java/org/jclouds/atmosonline/saas/config/AtmosStorageRestClientModuleTest.java @@ -0,0 +1,113 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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 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); + } + +} \ No newline at end of file diff --git a/atmosonline/saas/core/src/test/java/org/jclouds/atmosonline/saas/filters/SignRequestTest.java b/atmosonline/saas/core/src/test/java/org/jclouds/atmosonline/saas/filters/SignRequestTest.java new file mode 100644 index 0000000000..75eea61470 --- /dev/null +++ b/atmosonline/saas/core/src/test/java/org/jclouds/atmosonline/saas/filters/SignRequestTest.java @@ -0,0 +1,119 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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; + } +} \ No newline at end of file diff --git a/atmosonline/saas/core/src/test/java/org/jclouds/atmosonline/saas/functions/ParseSystemMetadataFromHeadersTest.java b/atmosonline/saas/core/src/test/java/org/jclouds/atmosonline/saas/functions/ParseSystemMetadataFromHeadersTest.java new file mode 100644 index 0000000000..85681359aa --- /dev/null +++ b/atmosonline/saas/core/src/test/java/org/jclouds/atmosonline/saas/functions/ParseSystemMetadataFromHeadersTest.java @@ -0,0 +1,74 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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); + } +} diff --git a/atmosonline/saas/core/src/test/java/org/jclouds/atmosonline/saas/xml/ErrorHandlerTest.java b/atmosonline/saas/core/src/test/java/org/jclouds/atmosonline/saas/xml/ErrorHandlerTest.java new file mode 100644 index 0000000000..7110999732 --- /dev/null +++ b/atmosonline/saas/core/src/test/java/org/jclouds/atmosonline/saas/xml/ErrorHandlerTest.java @@ -0,0 +1,55 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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 createParser() { + ParseSax parser = (ParseSax) factory.create(injector + .getInstance(ErrorHandler.class)); + return parser; + } + + public void testApplyInputStream() { + InputStream is = getClass().getResourceAsStream("/error.xml"); + ParseSax parser = createParser(); + AtmosStorageError result = parser.parse(is); + assertEquals(result.getCode(), 1003); + } +} diff --git a/atmosonline/saas/core/src/test/java/org/jclouds/atmosonline/saas/xml/ListDirectoryResponseHandlerTest.java b/atmosonline/saas/core/src/test/java/org/jclouds/atmosonline/saas/xml/ListDirectoryResponseHandlerTest.java new file mode 100644 index 0000000000..62b0f317a0 --- /dev/null +++ b/atmosonline/saas/core/src/test/java/org/jclouds/atmosonline/saas/xml/ListDirectoryResponseHandlerTest.java @@ -0,0 +1,64 @@ +/** + * + * Copyright (C) 2009 Cloud Conscious, LLC. + * + * ==================================================================== + * 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> createParser() { + ParseSax> parser = (ParseSax>) factory + .create(injector.getInstance(ListDirectoryResponseHandler.class)); + return parser; + } + + public void testApplyInputStreamBase() { + InputStream is = getClass().getResourceAsStream("/list_basic.xml"); + ParseSax> parser = createParser(); + SortedSet expected = Sets.newTreeSet(); + expected.add(new DirectoryEntry("4980cdb2a411106a04a4538c92a1b204ad92077de6e3", + FileType.DIRECTORY, "adriancole-blobstore-2096685753")); + expected.add(new DirectoryEntry("4980cdb2a410105404980d99e53a0504ad93939e7dc3", + FileType.DIRECTORY, "adriancole-blobstore247496608")); + SortedSet result = parser.parse(is); + assertEquals(result, expected); + } +} diff --git a/atmosonline/saas/core/src/test/resources/error.xml b/atmosonline/saas/core/src/test/resources/error.xml new file mode 100644 index 0000000000..444a6a8eb5 --- /dev/null +++ b/atmosonline/saas/core/src/test/resources/error.xml @@ -0,0 +1,5 @@ + + +1003 +The requested object was not found. + \ No newline at end of file diff --git a/atmosonline/saas/core/src/test/resources/hashstring.txt b/atmosonline/saas/core/src/test/resources/hashstring.txt new file mode 100644 index 0000000000..ec250aa604 --- /dev/null +++ b/atmosonline/saas/core/src/test/resources/hashstring.txt @@ -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 \ No newline at end of file diff --git a/atmosonline/saas/core/src/test/resources/list_basic.xml b/atmosonline/saas/core/src/test/resources/list_basic.xml new file mode 100644 index 0000000000..928f1b7501 --- /dev/null +++ b/atmosonline/saas/core/src/test/resources/list_basic.xml @@ -0,0 +1,40 @@ + + + + + + 4980cdb2a411106a04a4538c92a1b204ad92077de6e3 + directory + adriancole-blobstore-2096685753 + + + 4980cdb2a410105404980d99e53a0504ad93939e7dc3 + directory + adriancole-blobstore247496608 + + + \ No newline at end of file diff --git a/atmosonline/saas/core/src/test/resources/list_meta.xml b/atmosonline/saas/core/src/test/resources/list_meta.xml new file mode 100644 index 0000000000..6c293c2aed --- /dev/null +++ b/atmosonline/saas/core/src/test/resources/list_meta.xml @@ -0,0 +1,95 @@ + + + + + 499ad542a2a8bc200499ad5a7099940499b44f51e97d + + + + atime + 2009-02-17T23:15:01Z + + + mtime + 2009-02-17T23:15:01Z + + + ctime + 2009-02-17T23:15:01Z + + + itime + 2009-02-17T23:15:01Z + + + type + regular + + + uid + user1 + + + gid + apache + + + objectid + 499ad542a2a8bc200499ad5a7099940499b44f51e97d + + + + objname + + + + size + 7589 + + + nlink + 0 + + + policyname + default + + + + + part1 + order + false + + + part4/part7/part8 + quick + true + + + + diff --git a/atmosonline/saas/core/src/test/resources/log4j.xml b/atmosonline/saas/core/src/test/resources/log4j.xml new file mode 100755 index 0000000000..204069364f --- /dev/null +++ b/atmosonline/saas/core/src/test/resources/log4j.xml @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/atmosonline/saas/pom.xml b/atmosonline/saas/pom.xml new file mode 100644 index 0000000000..7835c38595 --- /dev/null +++ b/atmosonline/saas/pom.xml @@ -0,0 +1,60 @@ + + + + jclouds-atmosonline-project + org.jclouds + 1.0-SNAPSHOT + ../pom.xml + + 4.0.0 + jclouds-emcsaas-project + pom + jclouds atmosonline storage service project + + core + + + org.jclouds.atmosonline.saas.integration.AtmosStorageTestInitializer + ${jclouds.emcsaas.uid} + ${jclouds.emcsaas.key} + + + + ${project.groupId} + jclouds-blobstore-core + ${project.version} + + + ${project.groupId} + jclouds-blobstore-core + ${project.version} + test-jar + test + + + diff --git a/pom.xml b/pom.xml index 501ca27fc0..23ae4e7055 100644 --- a/pom.xml +++ b/pom.xml @@ -48,6 +48,7 @@ mezeo nirvanix vcloudx + atmosonline