You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jclouds.apache.org by ga...@apache.org on 2014/05/15 11:34:09 UTC
git commit: JCLOUDS-457: Created the skeleton of the Glacier API.
Repository: jclouds-labs-aws
Updated Branches:
refs/heads/master ed6d635e9 -> ca269db8b
JCLOUDS-457: Created the skeleton of the Glacier API.
Project: http://git-wip-us.apache.org/repos/asf/jclouds-labs-aws/repo
Commit: http://git-wip-us.apache.org/repos/asf/jclouds-labs-aws/commit/ca269db8
Tree: http://git-wip-us.apache.org/repos/asf/jclouds-labs-aws/tree/ca269db8
Diff: http://git-wip-us.apache.org/repos/asf/jclouds-labs-aws/diff/ca269db8
Branch: refs/heads/master
Commit: ca269db8b3d6ff93e61ac9c7410c3c2d99406de2
Parents: ed6d635
Author: Roman C. Coedo <ro...@gmail.com>
Authored: Tue Apr 22 12:15:03 2014 +0200
Committer: Andrew Gaul <ga...@apache.org>
Committed: Thu May 15 02:32:27 2014 -0700
----------------------------------------------------------------------
glacier/pom.xml | 128 +++++++++++++
.../org/jclouds/glacier/GlacierApiMetadata.java | 98 ++++++++++
.../org/jclouds/glacier/GlacierAsyncClient.java | 46 +++++
.../java/org/jclouds/glacier/GlacierClient.java | 25 +++
.../glacier/config/GlacierRestClientModule.java | 64 +++++++
.../filters/RequestAuthorizeSignature.java | 73 +++++++
.../glacier/reference/GlacierHeaders.java | 28 +++
.../glacier/util/AWSRequestSignerV4.java | 189 +++++++++++++++++++
.../services/org.jclouds.apis.ApiMetadata | 1 +
.../jclouds/glacier/GlacierApiMetadataTest.java | 33 ++++
.../jclouds/glacier/GlacierClientMockTest.java | 77 ++++++++
.../glacier/util/AWSRequestSignerV4Test.java | 56 ++++++
.../services/org.jclouds.apis.ApiMetadata | 1 +
pom.xml | 1 +
14 files changed, 820 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/jclouds-labs-aws/blob/ca269db8/glacier/pom.xml
----------------------------------------------------------------------
diff --git a/glacier/pom.xml b/glacier/pom.xml
new file mode 100644
index 0000000..5ac9979
--- /dev/null
+++ b/glacier/pom.xml
@@ -0,0 +1,128 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ 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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.apache.jclouds.labs</groupId>
+ <artifactId>jclouds-labs-aws</artifactId>
+ <version>1.8.0-SNAPSHOT</version>
+ </parent>
+
+ <groupId>org.apache.jclouds.labs</groupId>
+ <artifactId>glacier</artifactId>
+ <name>jclouds glacier api</name>
+ <description>jclouds components to access an implementation of glacier</description>
+ <packaging>bundle</packaging>
+
+ <properties>
+ <test.glacier.endpoint>https://glacier.eu-west-1.amazonaws.com</test.glacier.endpoint>
+ <test.glacier.api-version>2012-06-01</test.glacier.api-version>
+ <test.glacier.build-version />
+ <test.glacier.identity>${test.aws.identity}</test.glacier.identity>
+ <test.glacier.credential>${test.aws.credential}</test.glacier.credential>
+
+ <jclouds.osgi.export>org.jclouds.glacier*;version="${project.version}"</jclouds.osgi.export>
+ <jclouds.osgi.import>
+ org.jclouds.labs*;version="${project.version}",
+ org.jclouds*;version="${project.version}",
+ *
+ </jclouds.osgi.import>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.jclouds.api</groupId>
+ <artifactId>sts</artifactId>
+ <version>${project.version}</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.jclouds</groupId>
+ <artifactId>jclouds-blobstore</artifactId>
+ <version>${project.version}</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.jclouds</groupId>
+ <artifactId>jclouds-core</artifactId>
+ <version>${project.version}</version>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.jclouds</groupId>
+ <artifactId>jclouds-blobstore</artifactId>
+ <version>${project.version}</version>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.jclouds.driver</groupId>
+ <artifactId>jclouds-log4j</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.google.mockwebserver</groupId>
+ <artifactId>mockwebserver</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <profiles>
+ <profile>
+ <id>live</id>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>integration</id>
+ <phase>integration-test</phase>
+ <goals>
+ <goal>test</goal>
+ </goals>
+ <configuration>
+ <systemPropertyVariables>
+ <jclouds.blobstore.httpstream.url>${jclouds.blobstore.httpstream.url}</jclouds.blobstore.httpstream.url>
+ <jclouds.blobstore.httpstream.md5>${jclouds.blobstore.httpstream.md5}</jclouds.blobstore.httpstream.md5>
+ <test.glacier.endpoint>${test.glacier.endpoint}</test.glacier.endpoint>
+ <test.glacier.api-version>${test.glacier.api-version}</test.glacier.api-version>
+ <test.glacier.build-version>${test.glacier.build-version}</test.glacier.build-version>
+ <test.glacier.identity>${test.glacier.identity}</test.glacier.identity>
+ <test.glacier.credential>${test.glacier.credential}</test.glacier.credential>
+ </systemPropertyVariables>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+
+</project>
http://git-wip-us.apache.org/repos/asf/jclouds-labs-aws/blob/ca269db8/glacier/src/main/java/org/jclouds/glacier/GlacierApiMetadata.java
----------------------------------------------------------------------
diff --git a/glacier/src/main/java/org/jclouds/glacier/GlacierApiMetadata.java b/glacier/src/main/java/org/jclouds/glacier/GlacierApiMetadata.java
new file mode 100644
index 0000000..c547e21
--- /dev/null
+++ b/glacier/src/main/java/org/jclouds/glacier/GlacierApiMetadata.java
@@ -0,0 +1,98 @@
+/*
+ * 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.glacier;
+
+import static org.jclouds.aws.reference.AWSConstants.PROPERTY_HEADER_TAG;
+import static org.jclouds.reflect.Reflection2.typeToken;
+
+import java.net.URI;
+import java.util.Properties;
+
+import org.jclouds.blobstore.BlobStoreContext;
+import org.jclouds.glacier.config.GlacierRestClientModule;
+import org.jclouds.glacier.reference.GlacierHeaders;
+import org.jclouds.rest.internal.BaseRestApiMetadata;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.reflect.TypeToken;
+import com.google.inject.Module;
+
+/**
+ * Implementation of ApiMetadata for Amazon Glacier API
+ *
+ * @author Roman Coedo
+ */
+public class GlacierApiMetadata extends BaseRestApiMetadata {
+
+ @Deprecated
+ public static final TypeToken<org.jclouds.rest.RestContext<GlacierClient, GlacierAsyncClient>> CONTEXT_TOKEN = new TypeToken<org.jclouds.rest.RestContext<GlacierClient, GlacierAsyncClient>>() {
+
+ private static final long serialVersionUID = 1L;
+ };
+
+ private static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ public Builder toBuilder() {
+ return builder().fromApiMetadata(this);
+ }
+
+ public GlacierApiMetadata() {
+ this(builder());
+ }
+
+ protected GlacierApiMetadata(Builder builder) {
+ super(new Builder());
+ }
+
+ public static Properties defaultProperties() {
+ Properties properties = BaseRestApiMetadata.defaultProperties();
+ properties.setProperty(PROPERTY_HEADER_TAG, GlacierHeaders.DEFAULT_AMAZON_HEADERTAG);
+ return properties;
+ }
+
+ public static class Builder extends BaseRestApiMetadata.Builder<Builder> {
+
+ @SuppressWarnings("deprecation")
+ protected Builder() {
+ super(GlacierClient.class, GlacierAsyncClient.class);
+ id("glacier")
+ .name("Amazon Glacier API")
+ .identityName("Access Key ID")
+ .credentialName("Secret Access Key")
+ .defaultEndpoint("https://glacier.us-east-1.amazonaws.com")
+ .documentation(URI.create("http://docs.aws.amazon.com/amazonglacier/latest/dev/amazon-glacier-api.html"))
+ .version("2012-06-01")
+ .defaultProperties(GlacierApiMetadata.defaultProperties())
+ .context(CONTEXT_TOKEN)
+ .view(typeToken(BlobStoreContext.class))
+ .defaultModules(ImmutableSet.<Class<? extends Module>> of(GlacierRestClientModule.class));
+ }
+
+ @Override
+ public GlacierApiMetadata build() {
+ return new GlacierApiMetadata(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/jclouds-labs-aws/blob/ca269db8/glacier/src/main/java/org/jclouds/glacier/GlacierAsyncClient.java
----------------------------------------------------------------------
diff --git a/glacier/src/main/java/org/jclouds/glacier/GlacierAsyncClient.java b/glacier/src/main/java/org/jclouds/glacier/GlacierAsyncClient.java
new file mode 100644
index 0000000..7f66811
--- /dev/null
+++ b/glacier/src/main/java/org/jclouds/glacier/GlacierAsyncClient.java
@@ -0,0 +1,46 @@
+/*
+ * 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.glacier;
+
+import static org.jclouds.blobstore.attr.BlobScopes.CONTAINER;
+
+import java.io.Closeable;
+import java.net.URI;
+
+import javax.inject.Named;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+
+import org.jclouds.blobstore.attr.BlobScope;
+import org.jclouds.glacier.filters.RequestAuthorizeSignature;
+import org.jclouds.glacier.reference.GlacierHeaders;
+import org.jclouds.rest.annotations.Headers;
+import org.jclouds.rest.annotations.RequestFilters;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+@Headers(keys = GlacierHeaders.VERSION, values = "2012-06-01")
+@RequestFilters(RequestAuthorizeSignature.class)
+@BlobScope(CONTAINER)
+public interface GlacierAsyncClient extends Closeable {
+
+ @Named("CreateVault")
+ @PUT
+ @Path("/-/vaults/{vault}")
+ ListenableFuture<URI> createVault(@PathParam("vault") String vaultName);
+}
http://git-wip-us.apache.org/repos/asf/jclouds-labs-aws/blob/ca269db8/glacier/src/main/java/org/jclouds/glacier/GlacierClient.java
----------------------------------------------------------------------
diff --git a/glacier/src/main/java/org/jclouds/glacier/GlacierClient.java b/glacier/src/main/java/org/jclouds/glacier/GlacierClient.java
new file mode 100644
index 0000000..3d392b9
--- /dev/null
+++ b/glacier/src/main/java/org/jclouds/glacier/GlacierClient.java
@@ -0,0 +1,25 @@
+/*
+ * 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.glacier;
+
+import java.io.Closeable;
+import java.net.URI;
+
+public interface GlacierClient extends Closeable {
+
+ URI createVault(String vaultName);
+}
http://git-wip-us.apache.org/repos/asf/jclouds-labs-aws/blob/ca269db8/glacier/src/main/java/org/jclouds/glacier/config/GlacierRestClientModule.java
----------------------------------------------------------------------
diff --git a/glacier/src/main/java/org/jclouds/glacier/config/GlacierRestClientModule.java b/glacier/src/main/java/org/jclouds/glacier/config/GlacierRestClientModule.java
new file mode 100644
index 0000000..7631ce9
--- /dev/null
+++ b/glacier/src/main/java/org/jclouds/glacier/config/GlacierRestClientModule.java
@@ -0,0 +1,64 @@
+/*
+ * 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.glacier.config;
+
+import java.util.concurrent.TimeUnit;
+
+import javax.inject.Named;
+
+import org.jclouds.Constants;
+import org.jclouds.date.DateService;
+import org.jclouds.date.TimeStamp;
+import org.jclouds.glacier.GlacierAsyncClient;
+import org.jclouds.glacier.GlacierClient;
+import org.jclouds.glacier.filters.RequestAuthorizeSignature;
+import org.jclouds.rest.ConfiguresRestClient;
+import org.jclouds.rest.config.RestClientModule;
+
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import com.google.inject.Provides;
+import com.google.inject.Scopes;
+
+@ConfiguresRestClient
+public class GlacierRestClientModule extends RestClientModule<GlacierClient, GlacierAsyncClient> {
+
+ @Override
+ protected void configure() {
+ super.configure();
+ bind(RequestAuthorizeSignature.class).in(Scopes.SINGLETON);
+ }
+
+ @Provides
+ @TimeStamp
+ protected String provideTimeStamp(@TimeStamp Supplier<String> cache) {
+ return cache.get();
+ }
+
+ @Provides
+ @TimeStamp
+ Supplier<String> provideTimeStampCache(@Named(Constants.PROPERTY_SESSION_INTERVAL) long seconds,
+ final DateService dateService) {
+ return Suppliers.memoizeWithExpiration(new Supplier<String>() {
+
+ @Override
+ public String get() {
+ return dateService.iso8601SecondsDateFormat().replaceAll("[-:]", "");
+ }
+ }, seconds, TimeUnit.SECONDS);
+ }
+}
http://git-wip-us.apache.org/repos/asf/jclouds-labs-aws/blob/ca269db8/glacier/src/main/java/org/jclouds/glacier/filters/RequestAuthorizeSignature.java
----------------------------------------------------------------------
diff --git a/glacier/src/main/java/org/jclouds/glacier/filters/RequestAuthorizeSignature.java b/glacier/src/main/java/org/jclouds/glacier/filters/RequestAuthorizeSignature.java
new file mode 100644
index 0000000..8f46a0f
--- /dev/null
+++ b/glacier/src/main/java/org/jclouds/glacier/filters/RequestAuthorizeSignature.java
@@ -0,0 +1,73 @@
+/*
+ * 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.glacier.filters;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import javax.annotation.Resource;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Provider;
+import javax.inject.Singleton;
+
+import org.jclouds.Constants;
+import org.jclouds.crypto.Crypto;
+import org.jclouds.date.TimeStamp;
+import org.jclouds.domain.Credentials;
+import org.jclouds.glacier.reference.GlacierHeaders;
+import org.jclouds.glacier.util.AWSRequestSignerV4;
+import org.jclouds.http.HttpException;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpRequestFilter;
+import org.jclouds.http.HttpUtils;
+import org.jclouds.logging.Logger;
+
+import com.google.common.base.Supplier;
+import com.google.common.net.HttpHeaders;
+
+@Singleton
+public class RequestAuthorizeSignature implements HttpRequestFilter {
+
+ private final AWSRequestSignerV4 signer;
+
+ @Resource
+ @Named(Constants.LOGGER_SIGNATURE)
+ Logger signatureLog = Logger.NULL;
+
+ private final Provider<String> timeStampProvider;
+ private final HttpUtils utils;
+
+ @Inject
+ public RequestAuthorizeSignature(@TimeStamp Provider<String> timeStampProvider,
+ @org.jclouds.location.Provider Supplier<Credentials> creds, Crypto crypto, HttpUtils utils) {
+ checkNotNull(creds);
+ this.signer = new AWSRequestSignerV4(creds.get().identity, creds.get().credential, checkNotNull(crypto));
+ this.timeStampProvider = checkNotNull(timeStampProvider);
+ this.utils = checkNotNull(utils);
+ }
+
+ @Override
+ public HttpRequest filter(HttpRequest request) throws HttpException {
+ request = request.toBuilder().removeHeader(HttpHeaders.DATE)
+ .replaceHeader(GlacierHeaders.ALTERNATE_DATE, timeStampProvider.get())
+ .replaceHeader(HttpHeaders.HOST, request.getEndpoint().getHost()).build();
+ utils.logRequest(signatureLog, request, ">>");
+ request = this.signer.sign(request);
+ utils.logRequest(signatureLog, request, "<<");
+ return request;
+ }
+}
http://git-wip-us.apache.org/repos/asf/jclouds-labs-aws/blob/ca269db8/glacier/src/main/java/org/jclouds/glacier/reference/GlacierHeaders.java
----------------------------------------------------------------------
diff --git a/glacier/src/main/java/org/jclouds/glacier/reference/GlacierHeaders.java b/glacier/src/main/java/org/jclouds/glacier/reference/GlacierHeaders.java
new file mode 100644
index 0000000..51a0afe
--- /dev/null
+++ b/glacier/src/main/java/org/jclouds/glacier/reference/GlacierHeaders.java
@@ -0,0 +1,28 @@
+/*
+ * 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.glacier.reference;
+
+public final class GlacierHeaders {
+
+ public static final String DEFAULT_AMAZON_HEADERTAG = "amz";
+ public static final String HEADER_PREFIX = "x-" + DEFAULT_AMAZON_HEADERTAG + "-";
+ public static final String VERSION = HEADER_PREFIX + "glacier-version";
+ public static final String ALTERNATE_DATE = HEADER_PREFIX + "date";
+
+ private GlacierHeaders() {
+ }
+}
http://git-wip-us.apache.org/repos/asf/jclouds-labs-aws/blob/ca269db8/glacier/src/main/java/org/jclouds/glacier/util/AWSRequestSignerV4.java
----------------------------------------------------------------------
diff --git a/glacier/src/main/java/org/jclouds/glacier/util/AWSRequestSignerV4.java b/glacier/src/main/java/org/jclouds/glacier/util/AWSRequestSignerV4.java
new file mode 100644
index 0000000..e893b03
--- /dev/null
+++ b/glacier/src/main/java/org/jclouds/glacier/util/AWSRequestSignerV4.java
@@ -0,0 +1,189 @@
+/*
+ * 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.glacier.util;
+
+import static com.google.common.base.Charsets.UTF_8;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.io.IOException;
+import java.util.Locale;
+import java.util.Map.Entry;
+
+import javax.crypto.Mac;
+
+import org.jclouds.crypto.Crypto;
+import org.jclouds.glacier.reference.GlacierHeaders;
+import org.jclouds.http.HttpException;
+import org.jclouds.http.HttpRequest;
+
+import com.google.common.base.Function;
+import com.google.common.base.Joiner;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.SortedSetMultimap;
+import com.google.common.collect.TreeMultimap;
+import com.google.common.hash.Hashing;
+import com.google.common.io.BaseEncoding;
+import com.google.common.io.ByteStreams;
+import com.google.common.net.HttpHeaders;
+
+// TODO: Query parameters, not necessary for Glacier
+// TODO: Endpoint on buildCredentialScope is being read from the static string. Uncool.
+/**
+ * Signs requests using the AWSv4 signing algorithm
+ *
+ * @see <a href="http://docs.aws.amazon.com/general/latest/gr/sigv4_signing.html" />
+ * @author Roman Coedo
+ */
+public class AWSRequestSignerV4 {
+
+ public static final String AUTH_TAG = "AWS4";
+ public static final String HEADER_TAG = "x-amz-";
+ public static final String ALGORITHM = AUTH_TAG + "-HMAC-SHA256";
+ public static final String TERMINATION_STRING = "aws4_request";
+ public static final String REGION = "us-east-1";
+ public static final String SERVICE = "glacier";
+
+ private final Crypto crypto;
+ private final String identity;
+ private final String credential;
+
+ public AWSRequestSignerV4(String identity, String credential, Crypto crypto) {
+ this.crypto = checkNotNull(crypto);
+ this.identity = checkNotNull(identity);
+ this.credential = checkNotNull(credential);
+ }
+
+ private static String buildHashedCanonicalRequest(String method, String endpoint, String hashedPayload,
+ String canonicalizedHeadersString, String signedHeaders) {
+ return sha256((method + "\n" + endpoint + "\n" + "" + "\n" + canonicalizedHeadersString + "\n" + signedHeaders
+ + "\n" + hashedPayload).getBytes());
+ }
+
+ private static String createStringToSign(String date, String credentialScope, String hashedCanonicalRequest) {
+ return ALGORITHM + "\n" + date + "\n" + credentialScope + "\n" + hashedCanonicalRequest;
+ }
+
+ private static String formatDateWithoutTimestamp(String date) {
+ return date.substring(0, 8);
+ }
+
+ private static String buildCredentialScope(String dateWithoutTimeStamp) {
+ return dateWithoutTimeStamp + "/" + REGION + "/" + SERVICE + "/" + TERMINATION_STRING;
+ }
+
+ private static Multimap<String, String> buildCanonicalizedHeadersMap(HttpRequest request) {
+ Multimap<String, String> headers = request.getHeaders();
+ SortedSetMultimap<String, String> canonicalizedHeaders = TreeMultimap.create();
+ for (Entry<String, String> header : headers.entries()) {
+ if (header.getKey() == null)
+ continue;
+ String key = header.getKey().toString().toLowerCase(Locale.getDefault());
+ // Ignore any headers that are not particularly interesting.
+ if (key.equalsIgnoreCase(HttpHeaders.CONTENT_TYPE) || key.equalsIgnoreCase(HttpHeaders.CONTENT_MD5)
+ || key.equalsIgnoreCase(HttpHeaders.HOST) || key.startsWith(HEADER_TAG)) {
+ canonicalizedHeaders.put(key, header.getValue());
+ }
+ }
+ return canonicalizedHeaders;
+ }
+
+ private static String buildCanonicalizedHeadersString(Multimap<String, String> canonicalizedHeadersMap) {
+ StringBuilder canonicalizedHeadersBuffer = new StringBuilder();
+ for (Entry<String, String> header : canonicalizedHeadersMap.entries()) {
+ String key = header.getKey();
+ canonicalizedHeadersBuffer.append(key.toLowerCase()).append(':').append(header.getValue()).append('\n');
+ }
+ return canonicalizedHeadersBuffer.toString();
+ }
+
+ private static String buildSignedHeaders(Multimap<String, String> canonicalizedHeadersMap) {
+ return Joiner.on(';').join(Iterables.transform(canonicalizedHeadersMap.keySet(), new Function<String, String>() {
+
+ @Override
+ public String apply(String input) {
+ return input.toLowerCase();
+ }
+ }));
+ }
+
+ private static String sha256(byte[] unhashedBytes) {
+ return Hashing.sha256().hashBytes(unhashedBytes).toString();
+ }
+
+ private static String buildHashedPayload(HttpRequest request) {
+ try {
+ byte[] unhashedBytes = request.getPayload() == null ? "".getBytes() : ByteStreams.toByteArray(request
+ .getPayload().getInput());
+ return sha256(unhashedBytes);
+ } catch (IOException e) {
+ throw new HttpException("Error signing request", e);
+ }
+ }
+
+ private static String buildAuthHeader(String accessKey, String credentialScope, String signedHeaders,
+ String signature) {
+ return ALGORITHM + " " + "Credential=" + accessKey + "/" + credentialScope + "," + "SignedHeaders="
+ + signedHeaders + "," + "Signature=" + signature;
+ }
+
+ private byte[] hmacSha256(byte[] key, String s) {
+ try {
+ Mac hmacSHA256 = crypto.hmacSHA256(key);
+ return hmacSHA256.doFinal(s.getBytes());
+ } catch (Exception e) {
+ throw new HttpException("Error signing request", e);
+ }
+ }
+
+ private String buildSignature(String dateWithoutTimestamp, String stringToSign) {
+ byte[] kSecret = (AUTH_TAG + credential).getBytes(UTF_8);
+ byte[] kDate = hmacSha256(kSecret, dateWithoutTimestamp);
+ byte[] kRegion = hmacSha256(kDate, REGION);
+ byte[] kService = hmacSha256(kRegion, SERVICE);
+ byte[] kSigning = hmacSha256(kService, TERMINATION_STRING);
+ return BaseEncoding.base16().encode(hmacSha256(kSigning, stringToSign)).toLowerCase();
+ }
+
+ public HttpRequest sign(HttpRequest request) {
+ // Grab the needed data to build the signature
+ Multimap<String, String> canonicalizedHeadersMap = buildCanonicalizedHeadersMap(request);
+ String canonicalizedHeadersString = buildCanonicalizedHeadersString(canonicalizedHeadersMap);
+ String signedHeaders = buildSignedHeaders(canonicalizedHeadersMap);
+ String date = request.getFirstHeaderOrNull(GlacierHeaders.ALTERNATE_DATE);
+ String dateWithoutTimestamp = formatDateWithoutTimestamp(date);
+ String method = request.getMethod();
+ String endpoint = request.getEndpoint().getRawPath();
+ String credentialScope = buildCredentialScope(dateWithoutTimestamp);
+ String hashedPayload = buildHashedPayload(request);
+
+ // Task 1: Create a Canonical Request For Signature Version 4.
+ String hashedCanonicalRequest = buildHashedCanonicalRequest(method, endpoint, hashedPayload,
+ canonicalizedHeadersString, signedHeaders);
+
+ // Task 2: Create a String to Sign for Signature Version 4.
+ String stringToSign = createStringToSign(date, credentialScope, hashedCanonicalRequest);
+
+ // Task 3: Calculate the AWS Signature Version 4.
+ String signature = buildSignature(dateWithoutTimestamp, stringToSign);
+
+ // Sign the request
+ String authHeader = buildAuthHeader(identity, credentialScope, signedHeaders, signature);
+ request = request.toBuilder().replaceHeader(HttpHeaders.AUTHORIZATION, authHeader).build();
+ return request;
+ }
+}
http://git-wip-us.apache.org/repos/asf/jclouds-labs-aws/blob/ca269db8/glacier/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
----------------------------------------------------------------------
diff --git a/glacier/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata b/glacier/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
new file mode 100644
index 0000000..ef20fba
--- /dev/null
+++ b/glacier/src/main/resources/META-INF/services/org.jclouds.apis.ApiMetadata
@@ -0,0 +1 @@
+org.jclouds.glacier.GlacierApiMetadata
http://git-wip-us.apache.org/repos/asf/jclouds-labs-aws/blob/ca269db8/glacier/src/test/java/org/jclouds/glacier/GlacierApiMetadataTest.java
----------------------------------------------------------------------
diff --git a/glacier/src/test/java/org/jclouds/glacier/GlacierApiMetadataTest.java b/glacier/src/test/java/org/jclouds/glacier/GlacierApiMetadataTest.java
new file mode 100644
index 0000000..0cb3272
--- /dev/null
+++ b/glacier/src/test/java/org/jclouds/glacier/GlacierApiMetadataTest.java
@@ -0,0 +1,33 @@
+/*
+ * 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.glacier;
+
+import org.jclouds.blobstore.internal.BaseBlobStoreApiMetadataTest;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author Roman Coedo
+ */
+@Test(groups = "unit", testName = "GlacierApiMetadataTest")
+public class GlacierApiMetadataTest extends BaseBlobStoreApiMetadataTest {
+
+ public GlacierApiMetadataTest() {
+ super(new GlacierApiMetadata());
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/jclouds-labs-aws/blob/ca269db8/glacier/src/test/java/org/jclouds/glacier/GlacierClientMockTest.java
----------------------------------------------------------------------
diff --git a/glacier/src/test/java/org/jclouds/glacier/GlacierClientMockTest.java b/glacier/src/test/java/org/jclouds/glacier/GlacierClientMockTest.java
new file mode 100644
index 0000000..b3ec6c2
--- /dev/null
+++ b/glacier/src/test/java/org/jclouds/glacier/GlacierClientMockTest.java
@@ -0,0 +1,77 @@
+/*
+ * 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.glacier;
+
+import static com.google.common.util.concurrent.MoreExecutors.sameThreadExecutor;
+import static org.jclouds.Constants.PROPERTY_MAX_RETRIES;
+import static org.jclouds.Constants.PROPERTY_SO_TIMEOUT;
+import static org.testng.Assert.assertEquals;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URL;
+import java.util.Properties;
+import java.util.Set;
+
+import org.jclouds.ContextBuilder;
+import org.jclouds.concurrent.config.ExecutorServiceModule;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.inject.Module;
+import com.google.mockwebserver.MockResponse;
+import com.google.mockwebserver.MockWebServer;
+import com.google.mockwebserver.RecordedRequest;
+
+public class GlacierClientMockTest {
+
+ private static final String VAULT_NAME = "ConcreteVaultName";
+
+ private static final Set<Module> modules = ImmutableSet.<Module> of(new ExecutorServiceModule(sameThreadExecutor(),
+ sameThreadExecutor()));
+
+ static GlacierClient getGlacierClient(URL server) {
+ Properties overrides = new Properties();
+ // prevent expect-100 bug http://code.google.com/p/mockwebserver/issues/detail?id=6
+ overrides.setProperty(PROPERTY_SO_TIMEOUT, "0");
+ overrides.setProperty(PROPERTY_MAX_RETRIES, "1");
+ return ContextBuilder.newBuilder("glacier").credentials("accessKey", "secretKey").endpoint(server.toString())
+ .modules(modules).overrides(overrides).buildApi(GlacierClient.class);
+ }
+
+ public void testCreateVault() throws IOException, InterruptedException {
+ // Prepare the response
+ MockResponse mr = new MockResponse();
+ mr.setResponseCode(201);
+ mr.addHeader("x-amzn-RequestId", "AAABZpJrTyioDC_HsOmHae8EZp_uBSJr6cnGOLKp_XJCl-Q");
+ mr.addHeader("Date", "Sun, 25 Mar 2012 12:02:00 GMT");
+ mr.addHeader("Location", "/111122223333/vaults/" + VAULT_NAME);
+ MockWebServer server = new MockWebServer();
+ server.enqueue(mr);
+ server.play();
+
+ // Send the request and check the response
+ try {
+ GlacierClient client = getGlacierClient(server.getUrl("/"));
+ URI responseUri = client.createVault(VAULT_NAME);
+ assertEquals(responseUri.toString(), server.getUrl("/") + "111122223333/vaults/" + VAULT_NAME);
+ RecordedRequest request = server.takeRequest();
+ assertEquals(request.getRequestLine(), "PUT /-/vaults/" + VAULT_NAME + " HTTP/1.1");
+ } finally {
+ server.shutdown();
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/jclouds-labs-aws/blob/ca269db8/glacier/src/test/java/org/jclouds/glacier/util/AWSRequestSignerV4Test.java
----------------------------------------------------------------------
diff --git a/glacier/src/test/java/org/jclouds/glacier/util/AWSRequestSignerV4Test.java b/glacier/src/test/java/org/jclouds/glacier/util/AWSRequestSignerV4Test.java
new file mode 100644
index 0000000..24d479a
--- /dev/null
+++ b/glacier/src/test/java/org/jclouds/glacier/util/AWSRequestSignerV4Test.java
@@ -0,0 +1,56 @@
+/*
+ * 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.glacier.util;
+
+import static org.testng.Assert.assertEquals;
+
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateException;
+
+import org.jclouds.encryption.internal.JCECrypto;
+import org.jclouds.http.HttpRequest;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.Multimap;
+import com.google.common.collect.TreeMultimap;
+
+@Test(groups = "unit", testName = "AWSRequestSignerV4Test")
+public class AWSRequestSignerV4Test {
+
+ @Test
+ public void testSignatureCalculation() throws NoSuchAlgorithmException, CertificateException {
+ String auth = "AWS4-HMAC-SHA256 " + "Credential=AKIAIOSFODNN7EXAMPLE/20120525/us-east-1/glacier/aws4_request,"
+ + "SignedHeaders=host;x-amz-date;x-amz-glacier-version,"
+ + "Signature=3ce5b2f2fffac9262b4da9256f8d086b4aaf42eba5f111c21681a65a127b7c2a";
+ String identity = "AKIAIOSFODNN7EXAMPLE";
+ String credential = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY";
+ AWSRequestSignerV4 signer = new AWSRequestSignerV4(identity, credential, new JCECrypto());
+ HttpRequest request = signer.sign(createRequest());
+ assertEquals(request.getFirstHeaderOrNull("Authorization"), auth);
+ }
+
+ private HttpRequest createRequest() {
+ Multimap<String, String> headers = TreeMultimap.create();
+ headers.put("Host", "glacier.us-east-1.amazonaws.com");
+ headers.put("x-amz-date", "20120525T002453Z");
+ headers.put("x-amz-glacier-version", "2012-06-01");
+ HttpRequest request = HttpRequest.builder().method("PUT")
+ .endpoint("https://glacier.us-east-1.amazonaws.com/-/vaults/examplevault").headers(headers).build();
+ return request;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/jclouds-labs-aws/blob/ca269db8/glacier/src/test/resources/META-INF/services/org.jclouds.apis.ApiMetadata
----------------------------------------------------------------------
diff --git a/glacier/src/test/resources/META-INF/services/org.jclouds.apis.ApiMetadata b/glacier/src/test/resources/META-INF/services/org.jclouds.apis.ApiMetadata
new file mode 100644
index 0000000..ef20fba
--- /dev/null
+++ b/glacier/src/test/resources/META-INF/services/org.jclouds.apis.ApiMetadata
@@ -0,0 +1 @@
+org.jclouds.glacier.GlacierApiMetadata
http://git-wip-us.apache.org/repos/asf/jclouds-labs-aws/blob/ca269db8/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 1c5c45a..722bdf7 100644
--- a/pom.xml
+++ b/pom.xml
@@ -68,6 +68,7 @@
<module>elb</module>
<module>iam</module>
<module>rds</module>
+ <module>glacier</module>
<module>aws-elb</module>
<module>aws-iam</module>
<module>aws-rds</module>