JCLOUDS-457: Added Glacier vault operations

The Glacier client can create, delete, read, and list vaults.
This commit is contained in:
Roman C. Coedo 2014-05-13 12:08:49 +02:00 committed by Andrew Gaul
parent d4af465ca8
commit b892afc4d9
21 changed files with 1087 additions and 3 deletions

View File

@ -32,7 +32,7 @@
<packaging>bundle</packaging> <packaging>bundle</packaging>
<properties> <properties>
<test.glacier.endpoint>https://glacier.eu-west-1.amazonaws.com</test.glacier.endpoint> <test.glacier.endpoint>https://glacier.us-east-1.amazonaws.com</test.glacier.endpoint>
<test.glacier.api-version>2012-06-01</test.glacier.api-version> <test.glacier.api-version>2012-06-01</test.glacier.api-version>
<test.glacier.build-version /> <test.glacier.build-version />
<test.glacier.identity>${test.aws.identity}</test.glacier.identity> <test.glacier.identity>${test.aws.identity}</test.glacier.identity>

View File

@ -23,6 +23,7 @@ import java.net.URI;
import java.util.Properties; import java.util.Properties;
import org.jclouds.blobstore.BlobStoreContext; import org.jclouds.blobstore.BlobStoreContext;
import org.jclouds.glacier.config.GlacierParserModule;
import org.jclouds.glacier.config.GlacierRestClientModule; import org.jclouds.glacier.config.GlacierRestClientModule;
import org.jclouds.glacier.reference.GlacierHeaders; import org.jclouds.glacier.reference.GlacierHeaders;
import org.jclouds.rest.internal.BaseRestApiMetadata; import org.jclouds.rest.internal.BaseRestApiMetadata;
@ -82,7 +83,7 @@ public class GlacierApiMetadata extends BaseRestApiMetadata {
.defaultProperties(GlacierApiMetadata.defaultProperties()) .defaultProperties(GlacierApiMetadata.defaultProperties())
.context(CONTEXT_TOKEN) .context(CONTEXT_TOKEN)
.view(typeToken(BlobStoreContext.class)) .view(typeToken(BlobStoreContext.class))
.defaultModules(ImmutableSet.<Class<? extends Module>> of(GlacierRestClientModule.class)); .defaultModules(ImmutableSet.<Class<? extends Module>> of(GlacierRestClientModule.class, GlacierParserModule.class));
} }
@Override @Override

View File

@ -22,25 +22,87 @@ import java.io.Closeable;
import java.net.URI; import java.net.URI;
import javax.inject.Named; import javax.inject.Named;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.PUT; import javax.ws.rs.PUT;
import javax.ws.rs.Path; import javax.ws.rs.Path;
import javax.ws.rs.PathParam; import javax.ws.rs.PathParam;
import org.jclouds.Fallbacks.NullOnNotFoundOr404;
import org.jclouds.blobstore.attr.BlobScope; import org.jclouds.blobstore.attr.BlobScope;
import org.jclouds.glacier.domain.PaginatedVaultCollection;
import org.jclouds.glacier.domain.VaultMetadata;
import org.jclouds.glacier.fallbacks.FalseIfVaultNotEmpty;
import org.jclouds.glacier.filters.RequestAuthorizeSignature; import org.jclouds.glacier.filters.RequestAuthorizeSignature;
import org.jclouds.glacier.functions.ParseVaultMetadataFromHttpContent;
import org.jclouds.glacier.functions.ParseVaultMetadataListFromHttpContent;
import org.jclouds.glacier.options.PaginationOptions;
import org.jclouds.glacier.predicates.validators.VaultNameValidator;
import org.jclouds.glacier.reference.GlacierHeaders; import org.jclouds.glacier.reference.GlacierHeaders;
import org.jclouds.rest.annotations.Fallback;
import org.jclouds.rest.annotations.Headers; import org.jclouds.rest.annotations.Headers;
import org.jclouds.rest.annotations.ParamValidators;
import org.jclouds.rest.annotations.RequestFilters; import org.jclouds.rest.annotations.RequestFilters;
import org.jclouds.rest.annotations.ResponseParser;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
/**
* Provides asynchronous access to Amazon Glacier resources via their REST API.
* <p/>
*
* @see GlacierClient
* @see <a href="http://aws.amazon.com/documentation/glacier/" />
* @author Roman Coedo
*/
@Headers(keys = GlacierHeaders.VERSION, values = "2012-06-01") @Headers(keys = GlacierHeaders.VERSION, values = "2012-06-01")
@RequestFilters(RequestAuthorizeSignature.class) @RequestFilters(RequestAuthorizeSignature.class)
@BlobScope(CONTAINER) @BlobScope(CONTAINER)
public interface GlacierAsyncClient extends Closeable { public interface GlacierAsyncClient extends Closeable {
/**
* @see GlacierClient#createVault
*/
@Named("CreateVault") @Named("CreateVault")
@PUT @PUT
@Path("/-/vaults/{vault}") @Path("/-/vaults/{vault}")
ListenableFuture<URI> createVault(@PathParam("vault") String vaultName); ListenableFuture<URI> createVault(@PathParam("vault") @ParamValidators(VaultNameValidator.class) String vaultName);
/**
* @see GlacierClient#deleteVaultIfEmpty
*/
@Named("DeleteVault")
@DELETE
@Path("/-/vaults/{vault}")
@Fallback(FalseIfVaultNotEmpty.class)
ListenableFuture<Boolean> deleteVaultIfEmpty(@PathParam("vault") @ParamValidators(VaultNameValidator.class) String vaultName);
/**
* @see GlacierClient#describeVault
*/
@Named("DescribeVault")
@GET
@Path("/-/vaults/{vault}")
@ResponseParser(ParseVaultMetadataFromHttpContent.class)
@Fallback(NullOnNotFoundOr404.class)
ListenableFuture<VaultMetadata> describeVault(
@PathParam("vault") @ParamValidators(VaultNameValidator.class) String vaultName);
/**
* @see GlacierClient#listVaults(PaginationOptions)
*/
@Named("ListVaults")
@GET
@Path("/-/vaults")
@ResponseParser(ParseVaultMetadataListFromHttpContent.class)
ListenableFuture<PaginatedVaultCollection> listVaults(PaginationOptions options);
/**
* @see GlacierClient#listVaults
*/
@Named("ListVaults")
@GET
@Path("/-/vaults")
@ResponseParser(ParseVaultMetadataListFromHttpContent.class)
ListenableFuture<PaginatedVaultCollection> listVaults();
} }

View File

@ -19,7 +19,64 @@ package org.jclouds.glacier;
import java.io.Closeable; import java.io.Closeable;
import java.net.URI; import java.net.URI;
import org.jclouds.glacier.domain.PaginatedVaultCollection;
import org.jclouds.glacier.domain.VaultMetadata;
import org.jclouds.glacier.options.PaginationOptions;
/**
* Provides access to Amazon Glacier resources via their REST API.
* <p/>
*
* @see GlacierAsyncClient
* @see <a href="http://aws.amazon.com/documentation/glacier/" />
* @author Roman Coedo
*/
public interface GlacierClient extends Closeable { public interface GlacierClient extends Closeable {
/**
* A PUT request operation with a vault name to create a new vault to store archives.
*
* @param vaultName
* A name for the Vault being created.
* @return A reference to an URI pointing to the resource created.
* @see <a href="http://docs.aws.amazon.com/amazonglacier/latest/dev/api-vault-put.html" />
*/
URI createVault(String vaultName); URI createVault(String vaultName);
/**
* A DELETE request operation with a vault name to delete an existing vault. The vault must be empty.
*
* @param vaultName
* Name of the Vault being deleted.
* @return False if the vault was not empty and therefore not deleted, true otherwise.
* @see <a href="http://docs.aws.amazon.com/amazonglacier/latest/dev/api-vault-delete.html" />
*/
boolean deleteVaultIfEmpty(String vaultName);
/**
* A GET request operation with a vault name to fetch the vault metadata.
*
* @param vaultName
* Name of the Vault being described.
* @return A VaultMetadata object containing all the information relevant to the vault.
* @see <a href="http://docs.aws.amazon.com/amazonglacier/latest/dev/api-vault-get.html" />
*/
VaultMetadata describeVault(String vaultName);
/**
* A GET request operation to retrieve a vault listing.
*
* @param options
* Options used for pagination.
* @return A PaginatedVaultCollection object containing the list of vaults.
* @see <a href="http://docs.aws.amazon.com/amazonglacier/latest/dev/api-vaults-get.html" />
*/
PaginatedVaultCollection listVaults(PaginationOptions options);
/**
* A GET request operation to retrieve a vault listing.
*
* @see GlacierClient#listVaults(PaginationOptions)
*/
PaginatedVaultCollection listVaults();
} }

View File

@ -0,0 +1,60 @@
/*
* 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.base.Preconditions.checkNotNull;
import org.jclouds.glacier.domain.GlacierError;
import org.jclouds.http.HttpCommand;
import org.jclouds.http.HttpResponse;
import org.jclouds.http.HttpResponseException;
/**
* Encapsulates a GlacierError.
*
* @author Roman Coedo
*/
public class GlacierResponseException extends HttpResponseException {
private static final long serialVersionUID = 1L;
private final GlacierError error;
public GlacierResponseException(String message, HttpCommand command, HttpResponse response, GlacierError error,
Throwable cause) {
super(message, command, response, cause);
this.error = checkNotNull(error, "error");
}
public GlacierResponseException(HttpCommand command, HttpResponse response, GlacierError error, Throwable cause) {
super("request " + command.getCurrentRequest().getRequestLine() + " failed with code " + response.getStatusCode()
+ ", error: " + error.toString(), command, response, cause);
this.error = checkNotNull(error, "error");
}
public GlacierResponseException(HttpCommand command, HttpResponse response, GlacierError error) {
this(command, response, error, null);
}
public GlacierResponseException(String message, HttpCommand command, HttpResponse response, GlacierError error) {
this(message, command, response, error, null);
}
public GlacierError getError() {
return error;
}
}

View File

@ -0,0 +1,35 @@
/*
* 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 org.jclouds.json.config.GsonModule.DateAdapter;
import org.jclouds.json.config.GsonModule.Iso8601DateAdapter;
import com.google.inject.AbstractModule;
/**
* Configures the parser mappings.
*
* @author Roman Coedo
*/
public class GlacierParserModule extends AbstractModule {
@Override
protected void configure() {
bind(DateAdapter.class).to(Iso8601DateAdapter.class);
}
}

View File

@ -26,6 +26,11 @@ import org.jclouds.date.TimeStamp;
import org.jclouds.glacier.GlacierAsyncClient; import org.jclouds.glacier.GlacierAsyncClient;
import org.jclouds.glacier.GlacierClient; import org.jclouds.glacier.GlacierClient;
import org.jclouds.glacier.filters.RequestAuthorizeSignature; import org.jclouds.glacier.filters.RequestAuthorizeSignature;
import org.jclouds.glacier.handlers.ParseGlacierErrorFromJsonContent;
import org.jclouds.http.HttpErrorHandler;
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.ConfiguresRestClient;
import org.jclouds.rest.config.RestClientModule; import org.jclouds.rest.config.RestClientModule;
@ -34,6 +39,11 @@ import com.google.common.base.Suppliers;
import com.google.inject.Provides; import com.google.inject.Provides;
import com.google.inject.Scopes; import com.google.inject.Scopes;
/**
* Configures the mappings. Installs the Object and Parser modules.
*
* @author Roman Coedo
*/
@ConfiguresRestClient @ConfiguresRestClient
public class GlacierRestClientModule extends RestClientModule<GlacierClient, GlacierAsyncClient> { public class GlacierRestClientModule extends RestClientModule<GlacierClient, GlacierAsyncClient> {
@ -61,4 +71,11 @@ public class GlacierRestClientModule extends RestClientModule<GlacierClient, Gla
} }
}, seconds, TimeUnit.SECONDS); }, seconds, TimeUnit.SECONDS);
} }
@Override
protected void bindErrorHandlers() {
bind(HttpErrorHandler.class).annotatedWith(Redirection.class).to(ParseGlacierErrorFromJsonContent.class);
bind(HttpErrorHandler.class).annotatedWith(ClientError.class).to(ParseGlacierErrorFromJsonContent.class);
bind(HttpErrorHandler.class).annotatedWith(ServerError.class).to(ParseGlacierErrorFromJsonContent.class);
}
} }

View File

@ -0,0 +1,81 @@
/*
* 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.domain;
import java.beans.ConstructorProperties;
import org.jclouds.javax.annotation.Nullable;
import com.google.common.base.Objects;
/**
* Defines attributes to describe a GlacierError
*
* @author Roman Coedo
*/
public class GlacierError {
private final String code;
private final String message;
private final String type;
@ConstructorProperties({ "code", "message", "type" })
public GlacierError(@Nullable String code, @Nullable String message, @Nullable String type) {
this.code = code;
this.message = message;
this.type = type;
}
public String getCode() {
return code;
}
public String getMessage() {
return message;
}
public String getType() {
return type;
}
public boolean isValid() {
return (this.code != null) && (this.message != null) && (this.type != null);
}
@Override
public int hashCode() {
return Objects.hashCode(this.code, this.message, this.type);
}
@Override
public boolean equals(Object obj) {
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
GlacierError other = (GlacierError) obj;
return Objects.equal(this.code, other.code)
&& Objects.equal(this.message, other.message)
&& Objects.equal(this.type, other.type);
}
@Override
public String toString() {
return "GlacierError [code=" + this.getCode() + ", message=" + this.getMessage() + "type=" + this.getType() + "]";
}
}

View File

@ -0,0 +1,59 @@
/*
* 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.domain;
import java.beans.ConstructorProperties;
import java.util.Iterator;
import org.jclouds.collect.IterableWithMarker;
import org.jclouds.glacier.options.PaginationOptions;
import com.google.common.base.Optional;
import com.google.gson.annotations.SerializedName;
/**
* Paginated collection used to store Vault listing information.
*
* @author Roman Coedo
*/
public class PaginatedVaultCollection extends IterableWithMarker<VaultMetadata> {
@SerializedName("VaultList")
private final Iterable<VaultMetadata> vaults;
@SerializedName("Marker")
private final String marker;
@ConstructorProperties({ "VaultList", "Marker" })
public PaginatedVaultCollection(Iterable<VaultMetadata> vaults, String marker) {
this.vaults = vaults;
this.marker = marker;
}
@Override
public Iterator<VaultMetadata> iterator() {
return vaults.iterator();
}
@Override
public Optional<Object> nextMarker() {
return Optional.<Object>fromNullable(marker);
}
public PaginationOptions nextPaginationOptions() {
return PaginationOptions.class.cast(nextMarker().get());
}
}

View File

@ -0,0 +1,118 @@
/*
* 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.domain;
import static com.google.common.base.Preconditions.checkNotNull;
import java.beans.ConstructorProperties;
import java.util.Date;
import org.jclouds.javax.annotation.Nullable;
import com.google.common.base.Objects;
import com.google.common.collect.ComparisonChain;
import com.google.gson.annotations.SerializedName;
/**
* Defines the attributes needed to describe a vault.
*
* @author Roman Coedo
*/
public class VaultMetadata implements Comparable<VaultMetadata> {
@SerializedName("VaultName")
private final String vaultName;
@SerializedName("VaultARN")
private final String vaultARN;
@SerializedName("CreationDate")
private final Date creationDate;
@SerializedName("LastInventoryDate")
private final Date lastInventoryDate;
@SerializedName("NumberOfArchives")
private final Long numberOfArchives;
@SerializedName("SizeInBytes")
private final Long sizeInBytes;
@ConstructorProperties({ "VaultName", "VaultARN", "CreationDate", "LastInventoryDate", "NumberOfArchives",
"SizeInBytes" })
public VaultMetadata(String vaultName, String vaultARN, Date creationDate, @Nullable Date lastInventoryDate,
long numberOfArchives, long sizeInBytes) {
this.vaultName = checkNotNull(vaultName);
this.vaultARN = checkNotNull(vaultARN);
this.creationDate = checkNotNull(creationDate);
this.lastInventoryDate = lastInventoryDate;
this.numberOfArchives = checkNotNull(numberOfArchives);
this.sizeInBytes = checkNotNull(sizeInBytes);
}
public String getVaultName() {
return vaultName;
}
public String getVaultARN() {
return vaultARN;
}
public Date getCreationDate() {
return creationDate;
}
public Date getLastInventoryDate() {
return lastInventoryDate;
}
public long getNumberOfArchives() {
return numberOfArchives;
}
public long getSizeInBytes() {
return sizeInBytes;
}
@Override
public int hashCode() {
return Objects.hashCode(this.vaultName, this.vaultARN, this.creationDate, this.lastInventoryDate,
this.numberOfArchives, this.sizeInBytes);
}
@Override
public boolean equals(Object obj) {
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
VaultMetadata other = (VaultMetadata) obj;
return Objects.equal(this.vaultName, other.vaultName) && Objects.equal(this.vaultARN, other.vaultARN)
&& Objects.equal(this.creationDate, other.creationDate)
&& Objects.equal(this.lastInventoryDate, other.lastInventoryDate)
&& Objects.equal(this.numberOfArchives, other.numberOfArchives)
&& Objects.equal(this.sizeInBytes, other.sizeInBytes);
}
@Override
public String toString() {
return "VaultMetadata [vaultName=" + vaultName + ", vaultARN=" + vaultARN + ", creationDate=" + creationDate
+ ", lastInventoryDate=" + lastInventoryDate + ", numberOfArchives=" + numberOfArchives + ", sizeInBytes="
+ sizeInBytes + "]";
}
@Override
public int compareTo(VaultMetadata o) {
return ComparisonChain.start().compare(this.vaultName, o.getVaultName()).result();
}
}

View File

@ -0,0 +1,47 @@
/*
* 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.fallbacks;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Throwables.propagate;
import static com.google.common.util.concurrent.Futures.immediateFuture;
import static org.jclouds.util.Throwables2.getFirstThrowableOfType;
import org.jclouds.Fallback;
import com.google.common.util.concurrent.ListenableFuture;
/**
* Fallback used when deleting a vault.
*
* @author Roman Coedo
*/
public class FalseIfVaultNotEmpty implements Fallback<Boolean> {
@Override
public ListenableFuture<Boolean> create(Throwable t) throws Exception {
return immediateFuture(createOrPropagate(t));
}
@Override
public Boolean createOrPropagate(Throwable t) throws Exception {
if (getFirstThrowableOfType(checkNotNull(t, "throwable"), IllegalArgumentException.class) != null)
return false;
throw propagate(t);
}
}

View File

@ -39,6 +39,11 @@ import org.jclouds.logging.Logger;
import com.google.common.base.Supplier; import com.google.common.base.Supplier;
import com.google.common.net.HttpHeaders; import com.google.common.net.HttpHeaders;
/**
* Signs the request using the AWSRequestSignerV4.
*
* @author Roman Coedo
*/
@Singleton @Singleton
public class RequestAuthorizeSignature implements HttpRequestFilter { public class RequestAuthorizeSignature implements HttpRequestFilter {

View File

@ -0,0 +1,37 @@
/*
* 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.functions;
import org.jclouds.glacier.domain.VaultMetadata;
import org.jclouds.http.functions.ParseJson;
import org.jclouds.json.Json;
import com.google.inject.Inject;
import com.google.inject.TypeLiteral;
/**
* Parses the JSON vault information from the HttpResponse.
*
* @author Roman Coedo
*/
public class ParseVaultMetadataFromHttpContent extends ParseJson<VaultMetadata> {
@Inject
public ParseVaultMetadataFromHttpContent(Json json) {
super(json, TypeLiteral.get(VaultMetadata.class));
}
}

View File

@ -0,0 +1,37 @@
/*
* 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.functions;
import org.jclouds.glacier.domain.PaginatedVaultCollection;
import org.jclouds.http.functions.ParseJson;
import org.jclouds.json.Json;
import com.google.inject.Inject;
import com.google.inject.TypeLiteral;
/**
* Parses the JSON vault list from the HttpResponse.
*
* @author Roman Coedo
*/
public class ParseVaultMetadataListFromHttpContent extends ParseJson<PaginatedVaultCollection> {
@Inject
public ParseVaultMetadataListFromHttpContent(Json json) {
super(json, TypeLiteral.get(PaginatedVaultCollection.class));
}
}

View File

@ -0,0 +1,67 @@
/*
* 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.handlers;
import org.jclouds.glacier.GlacierResponseException;
import org.jclouds.glacier.domain.GlacierError;
import org.jclouds.http.HttpCommand;
import org.jclouds.http.HttpErrorHandler;
import org.jclouds.http.HttpResponse;
import org.jclouds.http.HttpResponseException;
import org.jclouds.http.functions.ParseJson;
import org.jclouds.json.Json;
import org.jclouds.rest.AuthorizationException;
import org.jclouds.rest.InsufficientResourcesException;
import org.jclouds.rest.ResourceNotFoundException;
import com.google.inject.Inject;
import com.google.inject.TypeLiteral;
/**
* Parses a GlacierError from a Json response content.
*
* @author Roman Coedo
*/
public class ParseGlacierErrorFromJsonContent extends ParseJson<GlacierError> implements HttpErrorHandler {
@Inject
public ParseGlacierErrorFromJsonContent(Json json) {
super(json, TypeLiteral.get(GlacierError.class));
}
private static Exception refineException(GlacierError error, Exception exception) {
if ("AccessDeniedException".equals(error.getCode())) {
return new AuthorizationException(error.getMessage(), exception);
} else if ("InvalidParameterValueException".equals(error.getCode())) {
return new IllegalArgumentException(error.getMessage(), exception);
} else if ("LimitExceededException".equals(error.getCode())) {
return new InsufficientResourcesException(error.getMessage(), exception);
} else if ("ResourceNotFoundException".equals(error.getCode())) {
return new ResourceNotFoundException(error.getMessage(), exception);
}
return exception;
}
@Override
public void handleError(HttpCommand command, HttpResponse response) {
GlacierError error = this.apply(response);
Exception exception = error.isValid() ? refineException(error, new GlacierResponseException(command, response,
error)) : new HttpResponseException(command, response);
command.setException(exception);
}
}

View File

@ -0,0 +1,58 @@
/*
* 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.options;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import org.jclouds.http.options.BaseHttpRequestOptions;
/**
* Pagination options used to specify the collection responses.
*
* @author Roman Coedo
*/
public class PaginationOptions extends BaseHttpRequestOptions {
private static final int MIN_LIMIT = 1;
private static final int MAX_LIMIT = 1000;
public PaginationOptions marker(String marker) {
queryParameters.put("marker", checkNotNull(marker, "marker"));
return this;
}
public PaginationOptions limit(int limit) {
checkArgument(limit >= MIN_LIMIT, "limit must be >= " + MIN_LIMIT);
checkArgument(limit <= MAX_LIMIT, "limit must be <= " + MAX_LIMIT);
queryParameters.put("limit", Integer.toString(limit));
return this;
}
public static class Builder {
public static PaginationOptions marker(String marker) {
PaginationOptions options = new PaginationOptions();
return options.marker(marker);
}
public static PaginationOptions limit(int limit) {
PaginationOptions options = new PaginationOptions();
return options.limit(limit);
}
}
}

View File

@ -0,0 +1,57 @@
/*
* 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.predicates.validators;
import org.jclouds.predicates.Validator;
import com.google.common.base.CharMatcher;
import com.google.inject.Singleton;
/**
* Validates Vault names according to Amazon Vault conventions.
*
* @author Roman Coedo
*/
@Singleton
public class VaultNameValidator extends Validator<String> {
private static final int MIN_LENGTH = 1;
private static final int MAX_LENGTH = 255;
@Override
public void validate(String vaultName) {
if (vaultName == null || vaultName.length() < MIN_LENGTH || vaultName.length() > MAX_LENGTH)
throw exception(vaultName, "Can't be null or empty. Length must be " + MIN_LENGTH + " to " + MAX_LENGTH
+ " symbols.");
CharMatcher range = getAcceptableRange();
if (!range.matchesAllOf(vaultName))
throw exception(vaultName, "Should have ASCII letters and numbers, underscores, hyphens, or periods.");
}
private static CharMatcher getAcceptableRange() {
return CharMatcher.inRange('a', 'z').or(CharMatcher.inRange('A', 'Z')).or(CharMatcher.inRange('0', '9'))
.or(CharMatcher.anyOf("-_."));
}
protected IllegalArgumentException exception(String vaultName, String reason) {
return new IllegalArgumentException(
String.format(
"Object '%s' doesn't match AWS Vault naming convention. "
+ "Reason: %s. For more info, please refer to http://docs.aws.amazon.com/amazonglacier/latest/dev/api-vault-put.html.",
vaultName, reason));
}
}

View File

@ -16,6 +16,11 @@
*/ */
package org.jclouds.glacier.reference; package org.jclouds.glacier.reference;
/**
* Headers used by Amazon Glacier.
*
* @author Roman Coedo
*/
public final class GlacierHeaders { public final class GlacierHeaders {
public static final String DEFAULT_AMAZON_HEADERTAG = "amz"; public static final String DEFAULT_AMAZON_HEADERTAG = "amz";

View File

@ -0,0 +1,89 @@
/*
* 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.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
import java.util.Iterator;
import java.util.UUID;
import org.jclouds.apis.BaseApiLiveTest;
import org.jclouds.glacier.domain.PaginatedVaultCollection;
import org.jclouds.glacier.domain.VaultMetadata;
import org.testng.annotations.Test;
/**
* Live test for Glacier.
*
* @author Roman Coedo
*/
@Test(groups = { "integration", "live" })
public class GlacierClientLiveTest extends BaseApiLiveTest<GlacierClient>{
public GlacierClientLiveTest() {
this.provider = "glacier";
}
private static final String VAULT_NAME1 = "testV1";
private static final String VAULT_NAME2 = "testV2";
private static final String VAULT_NAME3 = "testV3";
@Test(groups = { "integration", "live" })
public void testDeleteVaultIfEmptyOrNotFound() throws Exception {
assertTrue(api.deleteVaultIfEmpty(UUID.randomUUID().toString()));
}
@Test(groups = { "integration", "live" })
public void testDescribeNonExistentVault() throws Exception {
VaultMetadata vault = api.describeVault(UUID.randomUUID().toString());
assertEquals(vault, null);
}
private void testDescribeVault() throws Exception {
VaultMetadata vault = api.describeVault(VAULT_NAME1);
assertEquals(vault.getVaultName(), VAULT_NAME1);
assertEquals(vault.getNumberOfArchives(), 0);
assertEquals(vault.getSizeInBytes(), 0);
assertEquals(vault.getLastInventoryDate(), null);
}
private void testListVaults() throws Exception {
PaginatedVaultCollection vaults = api.listVaults();
Iterator<VaultMetadata> i = vaults.iterator();
assertEquals(i.next().getVaultName(), VAULT_NAME1);
assertEquals(i.next().getVaultName(), VAULT_NAME2);
assertEquals(i.next().getVaultName(), VAULT_NAME3);
}
@Test(groups = { "integration", "live" })
public void testCreateDescribeAndListVault() throws Exception {
try {
String path = api.createVault(VAULT_NAME1).toString();
api.createVault(VAULT_NAME2);
api.createVault(VAULT_NAME3);
assertTrue(path.contains("https://glacier.us-east-1.amazonaws.com/"));
assertTrue(path.contains("/vaults/" + VAULT_NAME1));
this.testDescribeVault();
this.testListVaults();
} finally {
api.deleteVaultIfEmpty(VAULT_NAME1);
api.deleteVaultIfEmpty(VAULT_NAME2);
api.deleteVaultIfEmpty(VAULT_NAME3);
}
}
}

View File

@ -20,15 +20,21 @@ import static com.google.common.util.concurrent.MoreExecutors.sameThreadExecutor
import static org.jclouds.Constants.PROPERTY_MAX_RETRIES; import static org.jclouds.Constants.PROPERTY_MAX_RETRIES;
import static org.jclouds.Constants.PROPERTY_SO_TIMEOUT; import static org.jclouds.Constants.PROPERTY_SO_TIMEOUT;
import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
import java.io.IOException; import java.io.IOException;
import java.net.URI; import java.net.URI;
import java.net.URL; import java.net.URL;
import java.util.Iterator;
import java.util.Properties; import java.util.Properties;
import java.util.Set; import java.util.Set;
import org.jclouds.ContextBuilder; import org.jclouds.ContextBuilder;
import org.jclouds.concurrent.config.ExecutorServiceModule; import org.jclouds.concurrent.config.ExecutorServiceModule;
import org.jclouds.glacier.domain.PaginatedVaultCollection;
import org.jclouds.glacier.domain.VaultMetadata;
import org.jclouds.glacier.options.PaginationOptions;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.inject.Module; import com.google.inject.Module;
@ -36,6 +42,11 @@ import com.google.mockwebserver.MockResponse;
import com.google.mockwebserver.MockWebServer; import com.google.mockwebserver.MockWebServer;
import com.google.mockwebserver.RecordedRequest; import com.google.mockwebserver.RecordedRequest;
/**
* Mock test for Glacier.
*
* @author Roman Coedo
*/
public class GlacierClientMockTest { public class GlacierClientMockTest {
private static final String VAULT_NAME = "ConcreteVaultName"; private static final String VAULT_NAME = "ConcreteVaultName";
@ -74,4 +85,130 @@ public class GlacierClientMockTest {
server.shutdown(); server.shutdown();
} }
} }
public void testDeleteVault() throws IOException, InterruptedException {
// Prepare the response
MockResponse mr = new MockResponse();
mr.setResponseCode(204);
mr.addHeader("x-amzn-RequestId", "AAABZpJrTyioDC_HsOmHae8EZp_uBSJr6cnGOLKp_XJCl-Q");
mr.addHeader("Date", "Sun, 25 Mar 2012 12:02:00 GMT");
MockWebServer server = new MockWebServer();
server.enqueue(mr);
server.play();
// Send the request and check the response
try {
GlacierClient client = getGlacierClient(server.getUrl("/"));
assertTrue(client.deleteVaultIfEmpty(VAULT_NAME));
RecordedRequest request = server.takeRequest();
assertEquals(request.getRequestLine(), "DELETE /-/vaults/" + VAULT_NAME + " HTTP/1.1");
} finally {
server.shutdown();
}
}
public void testDescribeVault() throws IOException, InterruptedException {
// Prepare the response
MockResponse mr = new MockResponse();
mr.setResponseCode(200);
mr.addHeader("x-amzn-RequestId", "AAABZpJrTyioDC_HsOmHae8EZp_uBSJr6cnGOLKp_XJCl-Q");
mr.addHeader("Date", "Sun, 25 Mar 2012 12:02:00 GMT");
mr.addHeader("Content-Type", "application/json");
mr.addHeader("Content-Length", "260");
mr.setBody("{\"CreationDate\" : \"2012-02-20T17:01:45.198Z\",\"LastInventoryDate\" : "
+ "\"2012-03-20T17:03:43.221Z\",\"NumberOfArchives\" : 192,\"SizeInBytes\" : 78088912,\"VaultARN\" : "
+ "\"arn:aws:glacier:us-east-1:012345678901:vaults/examplevault\",\"VaultName\" : \"examplevault\"}");
MockWebServer server = new MockWebServer();
server.enqueue(mr);
server.play();
// Send the request and check the response
try {
GlacierClient client = getGlacierClient(server.getUrl("/"));
VaultMetadata vm = client.describeVault(VAULT_NAME);
RecordedRequest request = server.takeRequest();
assertEquals(request.getRequestLine(), "GET /-/vaults/" + VAULT_NAME + " HTTP/1.1");
assertEquals(vm.getVaultName(), "examplevault");
assertEquals(vm.getVaultARN(), "arn:aws:glacier:us-east-1:012345678901:vaults/examplevault");
assertEquals(vm.getSizeInBytes(), 78088912);
assertEquals(vm.getNumberOfArchives(), 192);
} finally {
server.shutdown();
}
}
public void testListVaults() throws IOException, InterruptedException {
// Prepare the response
MockResponse mr = new MockResponse();
mr.setResponseCode(200);
mr.addHeader("x-amzn-RequestId", "AAABZpJrTyioDC_HsOmHae8EZp_uBSJr6cnGOLKp_XJCl-Q");
mr.addHeader("Date", "Sun, 25 Mar 2012 12:02:00 GMT");
mr.addHeader("Content-Type", "application/json");
mr.addHeader("Content-Length", "497");
mr.setBody("{" + "\"Marker\": null,\"VaultList\": [ {\"CreationDate\": \"2012-03-16T22:22:47.214Z\","
+ "\"LastInventoryDate\": \"2012-03-21T22:06:51.218Z\",\"NumberOfArchives\": 2,"
+ "\"SizeInBytes\": 12334,\"VaultARN\": \"arn:aws:glacier:us-east-1:012345678901:vaults/examplevault1\","
+ "\"VaultName\": \"examplevault1\"}, {\"CreationDate\": \"2012-03-19T22:06:51.218Z\","
+ "\"LastInventoryDate\": \"2012-03-21T22:06:51.218Z\", \"NumberOfArchives\": 0,\"SizeInBytes\": 0,"
+ "\"VaultARN\": \"arn:aws:glacier:us-east-1:012345678901:vaults/examplevault2\","
+ "\"VaultName\": \"examplevault2\"},{\"CreationDate\": \"2012-03-19T22:06:51.218Z\","
+ "\"LastInventoryDate\": \"2012-03-25T12:14:31.121Z\",\"NumberOfArchives\": 0,\"SizeInBytes\": 0,"
+ "\"VaultARN\": \"arn:aws:glacier:us-east-1:012345678901:vaults/examplevault3\","
+ "\"VaultName\": \"examplevault3\"}]}");
MockWebServer server = new MockWebServer();
server.enqueue(mr);
server.play();
// Send the request and check the response
try {
GlacierClient client = getGlacierClient(server.getUrl("/"));
PaginatedVaultCollection vc = client.listVaults();
Iterator<VaultMetadata> i = vc.iterator();
assertEquals(i.next().getVaultName(), "examplevault1");
assertEquals(i.next().getVaultName(), "examplevault2");
assertEquals(i.next().getVaultName(), "examplevault3");
RecordedRequest request = server.takeRequest();
assertEquals(request.getRequestLine(), "GET /-/vaults HTTP/1.1");
} finally {
server.shutdown();
}
}
public void testListVaultsWithQueryParams() throws IOException, InterruptedException {
// Prepare the response
MockResponse mr = new MockResponse();
mr.setResponseCode(200);
mr.addHeader("x-amzn-RequestId", "AAABZpJrTyioDC_HsOmHae8EZp_uBSJr6cnGOLKp_XJCl-Q");
mr.addHeader("Date", "Sun, 25 Mar 2012 12:02:00 GMT");
mr.addHeader("Content-Type", "application/json");
mr.addHeader("Content-Length", "497");
mr.setBody("{\"Marker\": \"arn:aws:glacier:us-east-1:012345678901:vaults/examplevault3\","
+ "\"VaultList\": [{\"CreationDate\": \"2012-03-16T22:22:47.214Z\",\"LastInventoryDate\":"
+ "\"2012-03-21T22:06:51.218Z\",\"NumberOfArchives\": 2,\"SizeInBytes\": 12334,"
+ "\"VaultARN\": \"arn:aws:glacier:us-east-1:012345678901:vaults/examplevault1\","
+ "\"VaultName\": \"examplevault1\"},{\"CreationDate\": \"2012-03-19T22:06:51.218Z\","
+ "\"LastInventoryDate\": \"2012-03-21T22:06:51.218Z\",\"NumberOfArchives\": 0,"
+ "\"SizeInBytes\": 0,\"VaultARN\": \"arn:aws:glacier:us-east-1:012345678901:vaults/examplevault2\","
+ "\"VaultName\": \"examplevault2\"}]}");
MockWebServer server = new MockWebServer();
server.enqueue(mr);
server.play();
// Send the request and check the response
try {
GlacierClient client = getGlacierClient(server.getUrl("/"));
PaginatedVaultCollection vc = client.listVaults(PaginationOptions.Builder.limit(2).marker(
"arn:aws:glacier:us-east-1:012345678901:vaults/examplevault1"));
Iterator<VaultMetadata> i = vc.iterator();
assertEquals(i.next().getVaultName(), "examplevault1");
assertEquals(i.next().getVaultName(), "examplevault2");
assertFalse(i.hasNext());
assertEquals(vc.nextMarker().get(), "arn:aws:glacier:us-east-1:012345678901:vaults/examplevault3");
RecordedRequest request = server.takeRequest();
assertEquals(request.getRequestLine(), "GET /-/vaults?limit=2&"
+ "marker=arn%3Aaws%3Aglacier%3Aus-east-1%3A012345678901%3Avaults/examplevault1 HTTP/1.1");
} finally {
server.shutdown();
}
}
} }

View File

@ -0,0 +1,55 @@
/*
* 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.predicates.validators;
import org.testng.annotations.Test;
@Test(groups = "unit", testName = "VaultNameValidatorTest")
public class VaultNameValidatorTest {
private static final VaultNameValidator VALIDATOR = new VaultNameValidator();
public void testValidate() {
VALIDATOR.validate("VALID_NAME");
VALIDATOR.validate("VALID-NAME");
VALIDATOR.validate("VALID255NAME");
VALIDATOR.validate("255VALID-NAME");
VALIDATOR.validate("VALID.NAME");
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testValidateInvalidName() {
VALIDATOR.validate("");
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testValidateInvalidName2() {
VALIDATOR.validate("<InvalidName>");
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testValidateInvalidName3() {
VALIDATOR.validate("INVALID,NAME");
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testValidateInvalidName4() {
VALIDATOR.validate("INVALIDNAMEINVALIDNAMEINVALIDNAMEINVALIDNAMEINVALIDNAMEINVALIDNAMEINVALIDNAMEINVALIDNAMEINVAL" +
"IDNAMEINVALIDNAMEINVALIDNAMEINVALIDNAMEINVALIDNAMEINVALIDNAMEINVALIDNAMEINVALIDNAMEINVALIDNAMEINVALIDNAMEI" +
"NVALIDNAMEINVALIDNAMEINVALIDNAMEINVALIDNAMEINVALIDNAMEINVALIDNAMEINVALIDNAMEINVALIDNAME");
}
}