mirror of https://github.com/apache/jclouds.git
Issu 301: refactored utilities that depend on guava. introduced Crypto, CryptoStreams, and more Payloads to help deal with encrypted payloads and headers
This commit is contained in:
parent
a9a0c53fb2
commit
6f180ddb4e
|
@ -56,7 +56,7 @@ import org.jclouds.http.annotation.ServerError;
|
||||||
import org.jclouds.http.filters.BasicAuthentication;
|
import org.jclouds.http.filters.BasicAuthentication;
|
||||||
import org.jclouds.rest.ConfiguresRestClient;
|
import org.jclouds.rest.ConfiguresRestClient;
|
||||||
import org.jclouds.rest.config.RestClientModule;
|
import org.jclouds.rest.config.RestClientModule;
|
||||||
import org.jclouds.encryption.EncryptionService;
|
import org.jclouds.crypto.Crypto;
|
||||||
|
|
||||||
import ${package}.${providerName};
|
import ${package}.${providerName};
|
||||||
import ${package}.${providerName}Client;
|
import ${package}.${providerName}Client;
|
||||||
|
@ -85,9 +85,9 @@ public class ${providerName}RestClientModule extends
|
||||||
public BasicAuthentication provideBasicAuthentication(
|
public BasicAuthentication provideBasicAuthentication(
|
||||||
@Named(${providerName}Constants.PROPERTY_${ucaseProviderName}_USER) String user,
|
@Named(${providerName}Constants.PROPERTY_${ucaseProviderName}_USER) String user,
|
||||||
@Named(${providerName}Constants.PROPERTY_${ucaseProviderName}_PASSWORD) String password,
|
@Named(${providerName}Constants.PROPERTY_${ucaseProviderName}_PASSWORD) String password,
|
||||||
EncryptionService encryptionService)
|
Crypto crypto)
|
||||||
throws UnsupportedEncodingException {
|
throws UnsupportedEncodingException {
|
||||||
return new BasicAuthentication(user, password, encryptionService);
|
return new BasicAuthentication(user, password, crypto);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
|
|
|
@ -22,7 +22,7 @@ import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
import org.jclouds.atmosonline.saas.domain.AtmosObject;
|
import org.jclouds.atmosonline.saas.domain.AtmosObject;
|
||||||
import org.jclouds.encryption.EncryptionService;
|
import org.jclouds.crypto.Crypto;
|
||||||
import org.jclouds.http.HttpRequest;
|
import org.jclouds.http.HttpRequest;
|
||||||
import org.jclouds.rest.Binder;
|
import org.jclouds.rest.Binder;
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ public class BindMetadataToHeaders implements Binder {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
protected BindMetadataToHeaders(BindUserMetadataToHeaders metaBinder,
|
protected BindMetadataToHeaders(BindUserMetadataToHeaders metaBinder,
|
||||||
EncryptionService encryptionService) {
|
Crypto crypto) {
|
||||||
this.metaBinder = metaBinder;
|
this.metaBinder = metaBinder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,8 +19,6 @@
|
||||||
package org.jclouds.atmosonline.saas.blobstore;
|
package org.jclouds.atmosonline.saas.blobstore;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
import static org.jclouds.concurrent.ConcurrentUtils.compose;
|
|
||||||
import static org.jclouds.concurrent.ConcurrentUtils.makeListenable;
|
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -54,8 +52,9 @@ import org.jclouds.blobstore.functions.BlobToHttpGetOptions;
|
||||||
import org.jclouds.blobstore.internal.BaseAsyncBlobStore;
|
import org.jclouds.blobstore.internal.BaseAsyncBlobStore;
|
||||||
import org.jclouds.blobstore.strategy.internal.FetchBlobMetadata;
|
import org.jclouds.blobstore.strategy.internal.FetchBlobMetadata;
|
||||||
import org.jclouds.blobstore.util.BlobUtils;
|
import org.jclouds.blobstore.util.BlobUtils;
|
||||||
|
import org.jclouds.concurrent.Futures;
|
||||||
|
import org.jclouds.crypto.Crypto;
|
||||||
import org.jclouds.domain.Location;
|
import org.jclouds.domain.Location;
|
||||||
import org.jclouds.encryption.EncryptionService;
|
|
||||||
import org.jclouds.http.options.GetOptions;
|
import org.jclouds.http.options.GetOptions;
|
||||||
|
|
||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
|
@ -73,7 +72,7 @@ public class AtmosAsyncBlobStore extends BaseAsyncBlobStore {
|
||||||
private final BlobToObject blob2Object;
|
private final BlobToObject blob2Object;
|
||||||
private final BlobStoreListOptionsToListOptions container2ContainerListOptions;
|
private final BlobStoreListOptionsToListOptions container2ContainerListOptions;
|
||||||
private final DirectoryEntryListToResourceMetadataList container2ResourceList;
|
private final DirectoryEntryListToResourceMetadataList container2ResourceList;
|
||||||
private final EncryptionService encryptionService;
|
private final Crypto crypto;
|
||||||
private final BlobToHttpGetOptions blob2ObjectGetOptions;
|
private final BlobToHttpGetOptions blob2ObjectGetOptions;
|
||||||
private final Provider<FetchBlobMetadata> fetchBlobMetadataProvider;
|
private final Provider<FetchBlobMetadata> fetchBlobMetadataProvider;
|
||||||
|
|
||||||
|
@ -83,7 +82,7 @@ public class AtmosAsyncBlobStore extends BaseAsyncBlobStore {
|
||||||
Set<? extends Location> locations, AtmosStorageAsyncClient async, AtmosStorageClient sync,
|
Set<? extends Location> locations, AtmosStorageAsyncClient async, AtmosStorageClient sync,
|
||||||
ObjectToBlob object2Blob, ObjectToBlobMetadata object2BlobMd, BlobToObject blob2Object,
|
ObjectToBlob object2Blob, ObjectToBlobMetadata object2BlobMd, BlobToObject blob2Object,
|
||||||
BlobStoreListOptionsToListOptions container2ContainerListOptions,
|
BlobStoreListOptionsToListOptions container2ContainerListOptions,
|
||||||
DirectoryEntryListToResourceMetadataList container2ResourceList, EncryptionService encryptionService,
|
DirectoryEntryListToResourceMetadataList container2ResourceList, Crypto crypto,
|
||||||
BlobToHttpGetOptions blob2ObjectGetOptions, Provider<FetchBlobMetadata> fetchBlobMetadataProvider) {
|
BlobToHttpGetOptions blob2ObjectGetOptions, Provider<FetchBlobMetadata> fetchBlobMetadataProvider) {
|
||||||
super(context, blobUtils, service, defaultLocation, locations);
|
super(context, blobUtils, service, defaultLocation, locations);
|
||||||
this.blob2ObjectGetOptions = checkNotNull(blob2ObjectGetOptions, "blob2ObjectGetOptions");
|
this.blob2ObjectGetOptions = checkNotNull(blob2ObjectGetOptions, "blob2ObjectGetOptions");
|
||||||
|
@ -95,7 +94,7 @@ public class AtmosAsyncBlobStore extends BaseAsyncBlobStore {
|
||||||
this.object2Blob = checkNotNull(object2Blob, "object2Blob");
|
this.object2Blob = checkNotNull(object2Blob, "object2Blob");
|
||||||
this.blob2Object = checkNotNull(blob2Object, "blob2Object");
|
this.blob2Object = checkNotNull(blob2Object, "blob2Object");
|
||||||
this.object2BlobMd = checkNotNull(object2BlobMd, "object2BlobMd");
|
this.object2BlobMd = checkNotNull(object2BlobMd, "object2BlobMd");
|
||||||
this.encryptionService = checkNotNull(encryptionService, "encryptionService");
|
this.crypto = checkNotNull(crypto, "crypto");
|
||||||
this.fetchBlobMetadataProvider = checkNotNull(fetchBlobMetadataProvider, "fetchBlobMetadataProvider");
|
this.fetchBlobMetadataProvider = checkNotNull(fetchBlobMetadataProvider, "fetchBlobMetadataProvider");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,7 +103,7 @@ public class AtmosAsyncBlobStore extends BaseAsyncBlobStore {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public ListenableFuture<BlobMetadata> blobMetadata(String container, String key) {
|
public ListenableFuture<BlobMetadata> blobMetadata(String container, String key) {
|
||||||
return compose(async.headFile(container + "/" + key), new Function<AtmosObject, BlobMetadata>() {
|
return Futures.compose(async.headFile(container + "/" + key), new Function<AtmosObject, BlobMetadata>() {
|
||||||
@Override
|
@Override
|
||||||
public BlobMetadata apply(AtmosObject from) {
|
public BlobMetadata apply(AtmosObject from) {
|
||||||
return object2BlobMd.apply(from);
|
return object2BlobMd.apply(from);
|
||||||
|
@ -119,7 +118,7 @@ public class AtmosAsyncBlobStore extends BaseAsyncBlobStore {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public ListenableFuture<Boolean> createContainerInLocation(Location location, String container) {
|
public ListenableFuture<Boolean> createContainerInLocation(Location location, String container) {
|
||||||
return compose(async.createDirectory(container), new Function<URI, Boolean>() {
|
return Futures.compose(async.createDirectory(container), new Function<URI, Boolean>() {
|
||||||
|
|
||||||
public Boolean apply(URI from) {
|
public Boolean apply(URI from) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -133,7 +132,7 @@ public class AtmosAsyncBlobStore extends BaseAsyncBlobStore {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public ListenableFuture<Void> createDirectory(String container, String directory) {
|
public ListenableFuture<Void> createDirectory(String container, String directory) {
|
||||||
return compose(async.createDirectory(container + "/" + directory), new Function<URI, Void>() {
|
return Futures.compose(async.createDirectory(container + "/" + directory), new Function<URI, Void>() {
|
||||||
|
|
||||||
public Void apply(URI from) {
|
public Void apply(URI from) {
|
||||||
return null;// no etag
|
return null;// no etag
|
||||||
|
@ -195,7 +194,7 @@ public class AtmosAsyncBlobStore extends BaseAsyncBlobStore {
|
||||||
public ListenableFuture<Blob> getBlob(String container, String key, org.jclouds.blobstore.options.GetOptions options) {
|
public ListenableFuture<Blob> getBlob(String container, String key, org.jclouds.blobstore.options.GetOptions options) {
|
||||||
GetOptions httpOptions = blob2ObjectGetOptions.apply(options);
|
GetOptions httpOptions = blob2ObjectGetOptions.apply(options);
|
||||||
ListenableFuture<AtmosObject> returnVal = async.readFile(container + "/" + key, httpOptions);
|
ListenableFuture<AtmosObject> returnVal = async.readFile(container + "/" + key, httpOptions);
|
||||||
return compose(returnVal, object2Blob, service);
|
return Futures.compose(returnVal, object2Blob, service);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -203,7 +202,7 @@ public class AtmosAsyncBlobStore extends BaseAsyncBlobStore {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public ListenableFuture<PageSet<? extends StorageMetadata>> list() {
|
public ListenableFuture<PageSet<? extends StorageMetadata>> list() {
|
||||||
return compose(async.listDirectories(), container2ResourceList, service);
|
return Futures.compose(async.listDirectories(), container2ResourceList, service);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -215,8 +214,9 @@ public class AtmosAsyncBlobStore extends BaseAsyncBlobStore {
|
||||||
container = AtmosStorageUtils.adjustContainerIfDirOptionPresent(container, options);
|
container = AtmosStorageUtils.adjustContainerIfDirOptionPresent(container, options);
|
||||||
ListOptions nativeOptions = container2ContainerListOptions.apply(options);
|
ListOptions nativeOptions = container2ContainerListOptions.apply(options);
|
||||||
ListenableFuture<BoundedSet<? extends DirectoryEntry>> returnVal = async.listDirectory(container, nativeOptions);
|
ListenableFuture<BoundedSet<? extends DirectoryEntry>> returnVal = async.listDirectory(container, nativeOptions);
|
||||||
ListenableFuture<PageSet<? extends StorageMetadata>> list = compose(returnVal, container2ResourceList, service);
|
ListenableFuture<PageSet<? extends StorageMetadata>> list = Futures.compose(returnVal, container2ResourceList,
|
||||||
return (ListenableFuture<PageSet<? extends StorageMetadata>>) (options.isDetailed() ? compose(list,
|
service);
|
||||||
|
return (ListenableFuture<PageSet<? extends StorageMetadata>>) (options.isDetailed() ? Futures.compose(list,
|
||||||
fetchBlobMetadataProvider.get().setContainerName(container), service) : list);
|
fetchBlobMetadataProvider.get().setContainerName(container), service) : list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,11 +227,11 @@ public class AtmosAsyncBlobStore extends BaseAsyncBlobStore {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public ListenableFuture<String> putBlob(final String container, final Blob blob) {
|
public ListenableFuture<String> putBlob(final String container, final Blob blob) {
|
||||||
return makeListenable(service.submit(new Callable<String>() {
|
return Futures.makeListenable(service.submit(new Callable<String>() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String call() throws Exception {
|
public String call() throws Exception {
|
||||||
return AtmosStorageUtils.putBlob(sync, encryptionService, blob2Object, container, blob);
|
return AtmosStorageUtils.putBlob(sync, crypto, blob2Object, container, blob);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,8 +44,8 @@ import org.jclouds.blobstore.functions.BlobToHttpGetOptions;
|
||||||
import org.jclouds.blobstore.internal.BaseBlobStore;
|
import org.jclouds.blobstore.internal.BaseBlobStore;
|
||||||
import org.jclouds.blobstore.strategy.internal.FetchBlobMetadata;
|
import org.jclouds.blobstore.strategy.internal.FetchBlobMetadata;
|
||||||
import org.jclouds.blobstore.util.BlobUtils;
|
import org.jclouds.blobstore.util.BlobUtils;
|
||||||
|
import org.jclouds.crypto.Crypto;
|
||||||
import org.jclouds.domain.Location;
|
import org.jclouds.domain.Location;
|
||||||
import org.jclouds.encryption.EncryptionService;
|
|
||||||
import org.jclouds.http.options.GetOptions;
|
import org.jclouds.http.options.GetOptions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -59,7 +59,7 @@ public class AtmosBlobStore extends BaseBlobStore {
|
||||||
private final BlobToObject blob2Object;
|
private final BlobToObject blob2Object;
|
||||||
private final BlobStoreListOptionsToListOptions container2ContainerListOptions;
|
private final BlobStoreListOptionsToListOptions container2ContainerListOptions;
|
||||||
private final DirectoryEntryListToResourceMetadataList container2ResourceList;
|
private final DirectoryEntryListToResourceMetadataList container2ResourceList;
|
||||||
private final EncryptionService encryptionService;
|
private final Crypto crypto;
|
||||||
private final BlobToHttpGetOptions blob2ObjectGetOptions;
|
private final BlobToHttpGetOptions blob2ObjectGetOptions;
|
||||||
private final Provider<FetchBlobMetadata> fetchBlobMetadataProvider;
|
private final Provider<FetchBlobMetadata> fetchBlobMetadataProvider;
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ public class AtmosBlobStore extends BaseBlobStore {
|
||||||
ObjectToBlobMetadata object2BlobMd, BlobToObject blob2Object,
|
ObjectToBlobMetadata object2BlobMd, BlobToObject blob2Object,
|
||||||
BlobStoreListOptionsToListOptions container2ContainerListOptions,
|
BlobStoreListOptionsToListOptions container2ContainerListOptions,
|
||||||
DirectoryEntryListToResourceMetadataList container2ResourceList,
|
DirectoryEntryListToResourceMetadataList container2ResourceList,
|
||||||
EncryptionService encryptionService, BlobToHttpGetOptions blob2ObjectGetOptions,
|
Crypto crypto, BlobToHttpGetOptions blob2ObjectGetOptions,
|
||||||
Provider<FetchBlobMetadata> fetchBlobMetadataProvider) {
|
Provider<FetchBlobMetadata> fetchBlobMetadataProvider) {
|
||||||
super(context, blobUtils, defaultLocation, locations);
|
super(context, blobUtils, defaultLocation, locations);
|
||||||
this.blob2ObjectGetOptions = checkNotNull(blob2ObjectGetOptions, "blob2ObjectGetOptions");
|
this.blob2ObjectGetOptions = checkNotNull(blob2ObjectGetOptions, "blob2ObjectGetOptions");
|
||||||
|
@ -80,7 +80,7 @@ public class AtmosBlobStore extends BaseBlobStore {
|
||||||
this.object2Blob = checkNotNull(object2Blob, "object2Blob");
|
this.object2Blob = checkNotNull(object2Blob, "object2Blob");
|
||||||
this.blob2Object = checkNotNull(blob2Object, "blob2Object");
|
this.blob2Object = checkNotNull(blob2Object, "blob2Object");
|
||||||
this.object2BlobMd = checkNotNull(object2BlobMd, "object2BlobMd");
|
this.object2BlobMd = checkNotNull(object2BlobMd, "object2BlobMd");
|
||||||
this.encryptionService = checkNotNull(encryptionService, "encryptionService");
|
this.crypto = checkNotNull(crypto, "crypto");
|
||||||
this.fetchBlobMetadataProvider = checkNotNull(fetchBlobMetadataProvider,
|
this.fetchBlobMetadataProvider = checkNotNull(fetchBlobMetadataProvider,
|
||||||
"fetchBlobMetadataProvider");
|
"fetchBlobMetadataProvider");
|
||||||
}
|
}
|
||||||
|
@ -204,7 +204,7 @@ public class AtmosBlobStore extends BaseBlobStore {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String putBlob(final String container, final Blob blob) {
|
public String putBlob(final String container, final Blob blob) {
|
||||||
return AtmosStorageUtils.putBlob(sync, encryptionService, blob2Object, container, blob);
|
return AtmosStorageUtils.putBlob(sync, crypto, blob2Object, container, blob);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
package org.jclouds.atmosonline.saas.blobstore.strategy;
|
package org.jclouds.atmosonline.saas.blobstore.strategy;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
import static org.jclouds.concurrent.ConcurrentUtils.awaitCompletion;
|
import static org.jclouds.concurrent.FutureIterables.awaitCompletion;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -43,7 +43,7 @@ import org.jclouds.blobstore.internal.BlobRuntimeException;
|
||||||
import org.jclouds.blobstore.options.ListContainerOptions;
|
import org.jclouds.blobstore.options.ListContainerOptions;
|
||||||
import org.jclouds.blobstore.strategy.ContainsValueInListStrategy;
|
import org.jclouds.blobstore.strategy.ContainsValueInListStrategy;
|
||||||
import org.jclouds.blobstore.strategy.ListBlobsInContainer;
|
import org.jclouds.blobstore.strategy.ListBlobsInContainer;
|
||||||
import static org.jclouds.concurrent.ConcurrentUtils.*;
|
import org.jclouds.concurrent.Futures;
|
||||||
import org.jclouds.logging.Logger;
|
import org.jclouds.logging.Logger;
|
||||||
|
|
||||||
import com.google.common.base.Throwables;
|
import com.google.common.base.Throwables;
|
||||||
|
@ -85,7 +85,7 @@ public class FindMD5InUserMetadata implements ContainsValueInListStrategy {
|
||||||
final BlockingQueue<Boolean> queue = new SynchronousQueue<Boolean>();
|
final BlockingQueue<Boolean> queue = new SynchronousQueue<Boolean>();
|
||||||
Map<String, Future<?>> responses = Maps.newHashMap();
|
Map<String, Future<?>> responses = Maps.newHashMap();
|
||||||
for (BlobMetadata md : getAllBlobMetadata.execute(containerName, options)) {
|
for (BlobMetadata md : getAllBlobMetadata.execute(containerName, options)) {
|
||||||
final ListenableFuture<AtmosObject> future = makeListenable(client.headFile(containerName
|
final ListenableFuture<AtmosObject> future = Futures.makeListenable(client.headFile(containerName
|
||||||
+ "/" + md.getName()), userExecutor);
|
+ "/" + md.getName()), userExecutor);
|
||||||
future.addListener(new Runnable() {
|
future.addListener(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
*/
|
*/
|
||||||
package org.jclouds.atmosonline.saas.domain;
|
package org.jclouds.atmosonline.saas.domain;
|
||||||
|
|
||||||
import org.jclouds.http.PayloadEnclosing;
|
import org.jclouds.io.PayloadEnclosing;
|
||||||
|
|
||||||
import com.google.common.collect.Multimap;
|
import com.google.common.collect.Multimap;
|
||||||
import com.google.inject.internal.Nullable;
|
import com.google.inject.internal.Nullable;
|
||||||
|
|
|
@ -26,9 +26,9 @@ import org.jclouds.atmosonline.saas.domain.AtmosObject;
|
||||||
import org.jclouds.atmosonline.saas.domain.MutableContentMetadata;
|
import org.jclouds.atmosonline.saas.domain.MutableContentMetadata;
|
||||||
import org.jclouds.atmosonline.saas.domain.SystemMetadata;
|
import org.jclouds.atmosonline.saas.domain.SystemMetadata;
|
||||||
import org.jclouds.atmosonline.saas.domain.UserMetadata;
|
import org.jclouds.atmosonline.saas.domain.UserMetadata;
|
||||||
import org.jclouds.http.PayloadEnclosing;
|
|
||||||
import org.jclouds.http.internal.PayloadEnclosingImpl;
|
import org.jclouds.http.internal.PayloadEnclosingImpl;
|
||||||
import org.jclouds.io.Payload;
|
import org.jclouds.io.Payload;
|
||||||
|
import org.jclouds.io.PayloadEnclosing;
|
||||||
import org.jclouds.io.payloads.DelegatingPayload;
|
import org.jclouds.io.payloads.DelegatingPayload;
|
||||||
|
|
||||||
import com.google.common.collect.LinkedHashMultimap;
|
import com.google.common.collect.LinkedHashMultimap;
|
||||||
|
|
|
@ -36,13 +36,15 @@ import javax.inject.Singleton;
|
||||||
import javax.ws.rs.core.HttpHeaders;
|
import javax.ws.rs.core.HttpHeaders;
|
||||||
|
|
||||||
import org.jclouds.atmosonline.saas.reference.AtmosStorageHeaders;
|
import org.jclouds.atmosonline.saas.reference.AtmosStorageHeaders;
|
||||||
|
import org.jclouds.crypto.Crypto;
|
||||||
|
import org.jclouds.crypto.CryptoStreams;
|
||||||
import org.jclouds.date.TimeStamp;
|
import org.jclouds.date.TimeStamp;
|
||||||
import org.jclouds.encryption.EncryptionService;
|
|
||||||
import org.jclouds.http.HttpException;
|
import org.jclouds.http.HttpException;
|
||||||
import org.jclouds.http.HttpRequest;
|
import org.jclouds.http.HttpRequest;
|
||||||
import org.jclouds.http.HttpRequestFilter;
|
import org.jclouds.http.HttpRequestFilter;
|
||||||
import org.jclouds.http.HttpUtils;
|
import org.jclouds.http.HttpUtils;
|
||||||
import org.jclouds.http.internal.SignatureWire;
|
import org.jclouds.http.internal.SignatureWire;
|
||||||
|
import org.jclouds.io.InputSuppliers;
|
||||||
import org.jclouds.logging.Logger;
|
import org.jclouds.logging.Logger;
|
||||||
import org.jclouds.util.Utils;
|
import org.jclouds.util.Utils;
|
||||||
|
|
||||||
|
@ -62,7 +64,7 @@ public class SignRequest implements HttpRequestFilter {
|
||||||
private final String uid;
|
private final String uid;
|
||||||
private final byte[] key;
|
private final byte[] key;
|
||||||
private final Provider<String> timeStampProvider;
|
private final Provider<String> timeStampProvider;
|
||||||
private final EncryptionService encryptionService;
|
private final Crypto crypto;
|
||||||
private final HttpUtils utils;
|
private final HttpUtils utils;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
|
@ -74,20 +76,19 @@ public class SignRequest implements HttpRequestFilter {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public SignRequest(SignatureWire signatureWire, @Named(PROPERTY_IDENTITY) String uid,
|
public SignRequest(SignatureWire signatureWire, @Named(PROPERTY_IDENTITY) String uid,
|
||||||
@Named(PROPERTY_CREDENTIAL) String encodedKey,
|
@Named(PROPERTY_CREDENTIAL) String encodedKey, @TimeStamp Provider<String> timeStampProvider,
|
||||||
@TimeStamp Provider<String> timeStampProvider, EncryptionService encryptionService,
|
Crypto crypto, HttpUtils utils) {
|
||||||
HttpUtils utils) {
|
|
||||||
this.signatureWire = signatureWire;
|
this.signatureWire = signatureWire;
|
||||||
this.uid = uid;
|
this.uid = uid;
|
||||||
this.key = encryptionService.fromBase64(encodedKey);
|
this.key = CryptoStreams.base64(encodedKey);
|
||||||
this.timeStampProvider = timeStampProvider;
|
this.timeStampProvider = timeStampProvider;
|
||||||
this.encryptionService = encryptionService;
|
this.crypto = crypto;
|
||||||
this.utils = utils;
|
this.utils = utils;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void filter(HttpRequest request) throws HttpException {
|
public void filter(HttpRequest request) throws HttpException {
|
||||||
String toSign = replaceUIDHeader(request).removeOldSignature(request).replaceDateHeader(
|
String toSign = replaceUIDHeader(request).removeOldSignature(request).replaceDateHeader(request)
|
||||||
request).createStringToSign(request);
|
.createStringToSign(request);
|
||||||
calculateAndReplaceAuthHeader(request, toSign);
|
calculateAndReplaceAuthHeader(request, toSign);
|
||||||
utils.logRequest(signatureLog, request, "<<");
|
utils.logRequest(signatureLog, request, "<<");
|
||||||
}
|
}
|
||||||
|
@ -111,19 +112,17 @@ public class SignRequest implements HttpRequestFilter {
|
||||||
return buffer.toString();
|
return buffer.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void calculateAndReplaceAuthHeader(HttpRequest request, String toSign)
|
private void calculateAndReplaceAuthHeader(HttpRequest request, String toSign) throws HttpException {
|
||||||
throws HttpException {
|
|
||||||
String signature = signString(toSign);
|
String signature = signString(toSign);
|
||||||
if (signatureWire.enabled())
|
if (signatureWire.enabled())
|
||||||
signatureWire.input(Utils.toInputStream(signature));
|
signatureWire.input(Utils.toInputStream(signature));
|
||||||
request.getHeaders().replaceValues(AtmosStorageHeaders.SIGNATURE,
|
request.getHeaders().replaceValues(AtmosStorageHeaders.SIGNATURE, Collections.singletonList(signature));
|
||||||
Collections.singletonList(signature));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String signString(String toSign) {
|
public String signString(String toSign) {
|
||||||
String signature;
|
String signature;
|
||||||
try {
|
try {
|
||||||
signature = encryptionService.base64(encryptionService.hmacSha1(toSign, key));
|
signature = CryptoStreams.base64(CryptoStreams.mac(InputSuppliers.of(toSign), crypto.hmacSHA1(key)));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new HttpException("error signing request", e);
|
throw new HttpException("error signing request", e);
|
||||||
}
|
}
|
||||||
|
@ -140,8 +139,7 @@ public class SignRequest implements HttpRequestFilter {
|
||||||
}
|
}
|
||||||
|
|
||||||
SignRequest replaceDateHeader(HttpRequest request) {
|
SignRequest replaceDateHeader(HttpRequest request) {
|
||||||
request.getHeaders().replaceValues(HttpHeaders.DATE,
|
request.getHeaders().replaceValues(HttpHeaders.DATE, Collections.singletonList(timeStampProvider.get()));
|
||||||
Collections.singletonList(timeStampProvider.get()));
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,9 +170,8 @@ public class SignRequest implements HttpRequestFilter {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void appendPayloadMetadata(HttpRequest request, StringBuilder buffer) {
|
private void appendPayloadMetadata(HttpRequest request, StringBuilder buffer) {
|
||||||
buffer.append(
|
buffer.append(utils.valueOrEmpty(request.getPayload() == null ? null : request.getPayload().getContentType()))
|
||||||
utils.valueOrEmpty(request.getPayload() == null ? null : request.getPayload()
|
.append("\n");
|
||||||
.getContentType())).append("\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
|
@ -182,8 +179,7 @@ public class SignRequest implements HttpRequestFilter {
|
||||||
// Only the value is used, not the header
|
// Only the value is used, not the header
|
||||||
// name. If a request does not include the header, this is an empty string.
|
// name. If a request does not include the header, this is an empty string.
|
||||||
for (String header : new String[] { "Range" })
|
for (String header : new String[] { "Range" })
|
||||||
toSign.append(utils.valueOrEmpty(request.getHeaders().get(header)).toLowerCase()).append(
|
toSign.append(utils.valueOrEmpty(request.getHeaders().get(header)).toLowerCase()).append("\n");
|
||||||
"\n");
|
|
||||||
// Standard HTTP header, in UTC format. Only the date value is used, not the header name.
|
// 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");
|
toSign.append(request.getHeaders().get(HttpHeaders.DATE).iterator().next()).append("\n");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
|
f * Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||||
*
|
*
|
||||||
* ====================================================================
|
* ====================================================================
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@ -28,8 +28,8 @@ import javax.inject.Singleton;
|
||||||
import org.jclouds.atmosonline.saas.domain.FileType;
|
import org.jclouds.atmosonline.saas.domain.FileType;
|
||||||
import org.jclouds.atmosonline.saas.domain.SystemMetadata;
|
import org.jclouds.atmosonline.saas.domain.SystemMetadata;
|
||||||
import org.jclouds.atmosonline.saas.reference.AtmosStorageHeaders;
|
import org.jclouds.atmosonline.saas.reference.AtmosStorageHeaders;
|
||||||
|
import org.jclouds.crypto.CryptoStreams;
|
||||||
import org.jclouds.date.DateService;
|
import org.jclouds.date.DateService;
|
||||||
import org.jclouds.encryption.EncryptionService;
|
|
||||||
import org.jclouds.http.HttpResponse;
|
import org.jclouds.http.HttpResponse;
|
||||||
|
|
||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
|
@ -41,18 +41,14 @@ import com.google.common.collect.Maps;
|
||||||
@Singleton
|
@Singleton
|
||||||
public class ParseSystemMetadataFromHeaders implements Function<HttpResponse, SystemMetadata> {
|
public class ParseSystemMetadataFromHeaders implements Function<HttpResponse, SystemMetadata> {
|
||||||
private final DateService dateService;
|
private final DateService dateService;
|
||||||
private final EncryptionService encryptionService;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public ParseSystemMetadataFromHeaders(DateService dateService,
|
public ParseSystemMetadataFromHeaders(DateService dateService) {
|
||||||
EncryptionService encryptionService) {
|
|
||||||
this.dateService = dateService;
|
this.dateService = dateService;
|
||||||
this.encryptionService = encryptionService;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public SystemMetadata apply(HttpResponse from) {
|
public SystemMetadata apply(HttpResponse from) {
|
||||||
String meta = checkNotNull(from.getFirstHeaderOrNull(AtmosStorageHeaders.META),
|
String meta = checkNotNull(from.getFirstHeaderOrNull(AtmosStorageHeaders.META), AtmosStorageHeaders.META);
|
||||||
AtmosStorageHeaders.META);
|
|
||||||
Map<String, String> metaMap = Maps.newHashMap();
|
Map<String, String> metaMap = Maps.newHashMap();
|
||||||
String[] metas = meta.split(", ");
|
String[] metas = meta.split(", ");
|
||||||
for (String entry : metas) {
|
for (String entry : metas) {
|
||||||
|
@ -60,17 +56,14 @@ public class ParseSystemMetadataFromHeaders implements Function<HttpResponse, Sy
|
||||||
metaMap.put(entrySplit[0], entrySplit[1]);
|
metaMap.put(entrySplit[0], entrySplit[1]);
|
||||||
}
|
}
|
||||||
assert metaMap.size() >= 12 : String.format("Should be 12 entries in %s", metaMap);
|
assert metaMap.size() >= 12 : String.format("Should be 12 entries in %s", metaMap);
|
||||||
byte[] md5 = metaMap.containsKey("content-md5") ? encryptionService.fromHex(metaMap
|
byte[] md5 = metaMap.containsKey("content-md5") ? CryptoStreams.hex(metaMap.get("content-md5")) : null;
|
||||||
.get("content-md5")) : null;
|
return new SystemMetadata(md5, dateService.iso8601SecondsDateParse(checkNotNull(metaMap.get("atime"), "atime")),
|
||||||
return new SystemMetadata(md5, dateService.iso8601SecondsDateParse(checkNotNull(metaMap
|
dateService.iso8601SecondsDateParse(checkNotNull(metaMap.get("ctime"), "ctime")), checkNotNull(metaMap
|
||||||
.get("atime"), "atime")), dateService.iso8601SecondsDateParse(checkNotNull(metaMap
|
.get("gid"), "gid"), dateService.iso8601SecondsDateParse(checkNotNull(metaMap.get("itime"),
|
||||||
.get("ctime"), "ctime")), checkNotNull(metaMap.get("gid"), "gid"), dateService
|
"itime")), dateService.iso8601SecondsDateParse(checkNotNull(metaMap.get("mtime"), "mtime")),
|
||||||
.iso8601SecondsDateParse(checkNotNull(metaMap.get("itime"), "itime")), dateService
|
Integer.parseInt(checkNotNull(metaMap.get("nlink"), "nlink")), checkNotNull(metaMap.get("objectid"),
|
||||||
.iso8601SecondsDateParse(checkNotNull(metaMap.get("mtime"), "mtime")), Integer
|
"objectid"), checkNotNull(metaMap.get("objname"), "objname"), checkNotNull(metaMap
|
||||||
.parseInt(checkNotNull(metaMap.get("nlink"), "nlink")), checkNotNull(metaMap
|
.get("policyname"), "policyname"), Long.parseLong(checkNotNull(metaMap.get("size"), "size")),
|
||||||
.get("objectid"), "objectid"), checkNotNull(metaMap.get("objname"), "objname"),
|
FileType.fromValue(checkNotNull(metaMap.get("type"), "type")), checkNotNull(metaMap.get("uid"), "uid"));
|
||||||
checkNotNull(metaMap.get("policyname"), "policyname"), Long.parseLong(checkNotNull(
|
|
||||||
metaMap.get("size"), "size")), FileType.fromValue(checkNotNull(metaMap
|
|
||||||
.get("type"), "type")), checkNotNull(metaMap.get("uid"), "uid"));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -30,7 +30,8 @@ import org.jclouds.atmosonline.saas.domain.AtmosStorageError;
|
||||||
import org.jclouds.atmosonline.saas.filters.SignRequest;
|
import org.jclouds.atmosonline.saas.filters.SignRequest;
|
||||||
import org.jclouds.atmosonline.saas.xml.ErrorHandler;
|
import org.jclouds.atmosonline.saas.xml.ErrorHandler;
|
||||||
import org.jclouds.blobstore.domain.Blob;
|
import org.jclouds.blobstore.domain.Blob;
|
||||||
import org.jclouds.encryption.EncryptionService;
|
import org.jclouds.crypto.Crypto;
|
||||||
|
import org.jclouds.crypto.CryptoStreams;
|
||||||
import org.jclouds.http.HttpCommand;
|
import org.jclouds.http.HttpCommand;
|
||||||
import org.jclouds.http.HttpException;
|
import org.jclouds.http.HttpException;
|
||||||
import org.jclouds.http.HttpResponse;
|
import org.jclouds.http.HttpResponse;
|
||||||
|
@ -56,10 +57,9 @@ public class AtmosStorageUtils {
|
||||||
@Inject
|
@Inject
|
||||||
Provider<ErrorHandler> errorHandlerProvider;
|
Provider<ErrorHandler> errorHandlerProvider;
|
||||||
|
|
||||||
public AtmosStorageError parseAtmosStorageErrorFromContent(HttpCommand command,
|
public AtmosStorageError parseAtmosStorageErrorFromContent(HttpCommand command, HttpResponse response,
|
||||||
HttpResponse response, InputStream content) throws HttpException {
|
InputStream content) throws HttpException {
|
||||||
AtmosStorageError error = (AtmosStorageError) factory.create(errorHandlerProvider.get())
|
AtmosStorageError error = (AtmosStorageError) factory.create(errorHandlerProvider.get()).parse(content);
|
||||||
.parse(content);
|
|
||||||
if (error.getCode() == 1032) {
|
if (error.getCode() == 1032) {
|
||||||
error.setStringSigned(signer.createStringToSign(command.getRequest()));
|
error.setStringSigned(signer.createStringToSign(command.getRequest()));
|
||||||
}
|
}
|
||||||
|
@ -67,13 +67,12 @@ public class AtmosStorageUtils {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String putBlob(final AtmosStorageClient sync, EncryptionService encryptionService,
|
public static String putBlob(final AtmosStorageClient sync, Crypto crypto, BlobToObject blob2Object,
|
||||||
BlobToObject blob2Object, String container, Blob blob) {
|
String container, Blob blob) {
|
||||||
final String path = container + "/" + blob.getMetadata().getName();
|
final String path = container + "/" + blob.getMetadata().getName();
|
||||||
deleteAndEnsureGone(sync, path);
|
deleteAndEnsureGone(sync, path);
|
||||||
if (blob.getMetadata().getContentMD5() != null)
|
if (blob.getMetadata().getContentMD5() != null)
|
||||||
blob.getMetadata().getUserMetadata().put("content-md5",
|
blob.getMetadata().getUserMetadata().put("content-md5", CryptoStreams.hex(blob.getMetadata().getContentMD5()));
|
||||||
encryptionService.hex(blob.getMetadata().getContentMD5()));
|
|
||||||
sync.createFile(container, blob2Object.apply(blob));
|
sync.createFile(container, blob2Object.apply(blob));
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
@ -93,10 +92,9 @@ public class AtmosStorageUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public AtmosStorageError parseAtmosStorageErrorFromContent(HttpCommand command,
|
public AtmosStorageError parseAtmosStorageErrorFromContent(HttpCommand command, HttpResponse response, String content)
|
||||||
HttpResponse response, String content) throws HttpException {
|
throws HttpException {
|
||||||
return parseAtmosStorageErrorFromContent(command, response, new ByteArrayInputStream(content
|
return parseAtmosStorageErrorFromContent(command, response, new ByteArrayInputStream(content.getBytes()));
|
||||||
.getBytes()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String adjustContainerIfDirOptionPresent(String container,
|
public static String adjustContainerIfDirOptionPresent(String container,
|
||||||
|
|
|
@ -89,8 +89,7 @@ public class AtmosStorageClientLiveTest {
|
||||||
private final String metadataValue;
|
private final String metadataValue;
|
||||||
private final String compare;
|
private final String compare;
|
||||||
|
|
||||||
private ObjectMatches(AtmosStorageClient connection, String name, String metadataValue,
|
private ObjectMatches(AtmosStorageClient connection, String name, String metadataValue, String compare) {
|
||||||
String compare) {
|
|
||||||
this.connection = connection;
|
this.connection = connection;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.metadataValue = metadataValue;
|
this.metadataValue = metadataValue;
|
||||||
|
@ -115,16 +114,12 @@ public class AtmosStorageClientLiveTest {
|
||||||
private BlobStoreContext context;
|
private BlobStoreContext context;
|
||||||
|
|
||||||
@BeforeGroups(groups = { "live" })
|
@BeforeGroups(groups = { "live" })
|
||||||
public void setupClient() throws InterruptedException, ExecutionException, TimeoutException,
|
public void setupClient() throws InterruptedException, ExecutionException, TimeoutException, IOException {
|
||||||
IOException {
|
String identity = checkNotNull(System.getProperty("jclouds.test.identity"), "jclouds.test.identity");
|
||||||
String identity = checkNotNull(System.getProperty("jclouds.test.identity"),
|
String credential = checkNotNull(System.getProperty("jclouds.test.credential"), "jclouds.test.credential");
|
||||||
"jclouds.test.identity");
|
context = new BlobStoreContextFactory().createContext("atmosonline", identity, credential, ImmutableSet
|
||||||
String credential = checkNotNull(System.getProperty("jclouds.test.credential"),
|
.<Module> of(new Log4JLoggingModule()));
|
||||||
"jclouds.test.credential");
|
RestContext<AtmosStorageClient, AtmosStorageAsyncClient> restContext = context.getProviderSpecificContext();
|
||||||
context = new BlobStoreContextFactory().createContext("atmosonline", identity, credential,
|
|
||||||
ImmutableSet.<Module> of(new Log4JLoggingModule()));
|
|
||||||
RestContext<AtmosStorageClient, AtmosStorageAsyncClient> restContext = context
|
|
||||||
.getProviderSpecificContext();
|
|
||||||
connection = restContext.getApi();
|
connection = restContext.getApi();
|
||||||
for (DirectoryEntry entry : connection.listDirectories()) {
|
for (DirectoryEntry entry : connection.listDirectories()) {
|
||||||
if (entry.getObjectName().startsWith(containerPrefix)) {
|
if (entry.getObjectName().startsWith(containerPrefix)) {
|
||||||
|
@ -169,8 +164,8 @@ public class AtmosStorageClientLiveTest {
|
||||||
createOrReplaceObject("object2", "here is my data!", "meta-value1");
|
createOrReplaceObject("object2", "here is my data!", "meta-value1");
|
||||||
createOrReplaceObject("object3", "here is my data!", "meta-value1");
|
createOrReplaceObject("object3", "here is my data!", "meta-value1");
|
||||||
createOrReplaceObject("object4", "here is my data!", "meta-value1");
|
createOrReplaceObject("object4", "here is my data!", "meta-value1");
|
||||||
BoundedSet<? extends DirectoryEntry> r2 = connection.listDirectory(privateDirectory,
|
BoundedSet<? extends DirectoryEntry> r2 = connection
|
||||||
ListOptions.Builder.limit(1));
|
.listDirectory(privateDirectory, ListOptions.Builder.limit(1));
|
||||||
assertEquals(r2.size(), 1);
|
assertEquals(r2.size(), 1);
|
||||||
assert r2.getToken() != null;
|
assert r2.getToken() != null;
|
||||||
assertEquals(Iterables.getLast(Sets.newTreeSet(r2)).getObjectName(), "object2");
|
assertEquals(Iterables.getLast(Sets.newTreeSet(r2)).getObjectName(), "object2");
|
||||||
|
@ -196,8 +191,7 @@ public class AtmosStorageClientLiveTest {
|
||||||
// loop to gather metrics
|
// loop to gather metrics
|
||||||
for (boolean stream : new Boolean[] { true, false }) {
|
for (boolean stream : new Boolean[] { true, false }) {
|
||||||
for (int i = 0; i < 10; i++) {
|
for (int i = 0; i < 10; i++) {
|
||||||
System.err.printf("upload/delete/create attempt %d type %s%n", i + 1, stream ? "stream"
|
System.err.printf("upload/delete/create attempt %d type %s%n", i + 1, stream ? "stream" : "string");
|
||||||
: "string");
|
|
||||||
// try updating
|
// try updating
|
||||||
createOrUpdateWithErrorLoop(stream, "there is my data", "2");
|
createOrUpdateWithErrorLoop(stream, "there is my data", "2");
|
||||||
|
|
||||||
|
@ -209,8 +203,7 @@ public class AtmosStorageClientLiveTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createOrUpdateWithErrorLoop(boolean stream, String data, String metadataValue)
|
private void createOrUpdateWithErrorLoop(boolean stream, String data, String metadataValue) throws Exception {
|
||||||
throws Exception {
|
|
||||||
createOrReplaceObject("object", makeData(data, stream), metadataValue);
|
createOrReplaceObject("object", makeData(data, stream), metadataValue);
|
||||||
assertEventuallyObjectMatches("object", data, metadataValue);
|
assertEventuallyObjectMatches("object", data, metadataValue);
|
||||||
}
|
}
|
||||||
|
@ -219,14 +212,13 @@ public class AtmosStorageClientLiveTest {
|
||||||
return stream ? Utils.toInputStream(in) : in;
|
return stream ? Utils.toInputStream(in) : in;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createOrReplaceObject(String name, Object data, String metadataValue)
|
private void createOrReplaceObject(String name, Object data, String metadataValue) throws Exception {
|
||||||
throws Exception {
|
|
||||||
// Test PUT with string data, ETag hash, and a piece of metadata
|
// Test PUT with string data, ETag hash, and a piece of metadata
|
||||||
AtmosObject object = connection.newObject();
|
AtmosObject object = connection.newObject();
|
||||||
object.getContentMetadata().setName(name);
|
object.getContentMetadata().setName(name);
|
||||||
object.setPayload(Payloads.newPayload(data));
|
object.setPayload(Payloads.newPayload(data));
|
||||||
object.getContentMetadata().setContentLength(16l);
|
object.getContentMetadata().setContentLength(16l);
|
||||||
context.utils().encryption().generateMD5BufferingIfNotRepeatable(object);
|
Payloads.calculateMD5(object);
|
||||||
object.getContentMetadata().setContentType("text/plain");
|
object.getContentMetadata().setContentType("text/plain");
|
||||||
object.getUserMetadata().getMetadata().put("Metadata", metadataValue);
|
object.getUserMetadata().getMetadata().put("Metadata", metadataValue);
|
||||||
replaceObject(object);
|
replaceObject(object);
|
||||||
|
@ -243,9 +235,8 @@ public class AtmosStorageClientLiveTest {
|
||||||
try {
|
try {
|
||||||
assertion.run();
|
assertion.run();
|
||||||
if (i > 0)
|
if (i > 0)
|
||||||
System.err.printf("%d attempts and %dms asserting %s%n", i + 1, System
|
System.err.printf("%d attempts and %dms asserting %s%n", i + 1, System.currentTimeMillis() - start,
|
||||||
.currentTimeMillis()
|
assertion.getClass().getSimpleName());
|
||||||
- start, assertion.getClass().getSimpleName());
|
|
||||||
return;
|
return;
|
||||||
} catch (AssertionError e) {
|
} catch (AssertionError e) {
|
||||||
error = e;
|
error = e;
|
||||||
|
@ -257,10 +248,9 @@ public class AtmosStorageClientLiveTest {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void assertEventuallyObjectMatches(final String name, final String compare,
|
protected void assertEventuallyObjectMatches(final String name, final String compare, final String metadataValue)
|
||||||
final String metadataValue) throws InterruptedException {
|
throws InterruptedException {
|
||||||
assertEventually(new ObjectMatches(connection, privateDirectory + "/" + name, metadataValue,
|
assertEventually(new ObjectMatches(connection, privateDirectory + "/" + name, metadataValue, compare));
|
||||||
compare));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void assertEventuallyHeadMatches(final String name, final String metadataValue)
|
protected void assertEventuallyHeadMatches(final String name, final String metadataValue)
|
||||||
|
@ -268,17 +258,15 @@ public class AtmosStorageClientLiveTest {
|
||||||
assertEventually(new HeadMatches(connection, privateDirectory + "/" + name, metadataValue));
|
assertEventually(new HeadMatches(connection, privateDirectory + "/" + name, metadataValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void verifyHeadObject(AtmosStorageClient connection, String path,
|
private static void verifyHeadObject(AtmosStorageClient connection, String path, String metadataValue)
|
||||||
String metadataValue) throws InterruptedException, ExecutionException,
|
throws InterruptedException, ExecutionException, TimeoutException, IOException {
|
||||||
TimeoutException, IOException {
|
|
||||||
AtmosObject getBlob = connection.headFile(path);
|
AtmosObject getBlob = connection.headFile(path);
|
||||||
assertEquals(Utils.toStringAndClose(getBlob.getPayload().getInput()), "");
|
assertEquals(Utils.toStringAndClose(getBlob.getPayload().getInput()), "");
|
||||||
verifyMetadata(metadataValue, getBlob);
|
verifyMetadata(metadataValue, getBlob);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void verifyObject(AtmosStorageClient connection, String path, String compare,
|
private static void verifyObject(AtmosStorageClient connection, String path, String compare, String metadataValue)
|
||||||
String metadataValue) throws InterruptedException, ExecutionException,
|
throws InterruptedException, ExecutionException, TimeoutException, IOException {
|
||||||
TimeoutException, IOException {
|
|
||||||
AtmosObject getBlob = connection.readFile(path);
|
AtmosObject getBlob = connection.readFile(path);
|
||||||
assertEquals(Utils.toStringAndClose(getBlob.getPayload().getInput()), compare);
|
assertEquals(Utils.toStringAndClose(getBlob.getPayload().getInput()), compare);
|
||||||
verifyMetadata(metadataValue, getBlob);
|
verifyMetadata(metadataValue, getBlob);
|
||||||
|
@ -304,8 +292,8 @@ public class AtmosStorageClientLiveTest {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Utils.toStringAndClose(URI.create(
|
Utils.toStringAndClose(URI.create(
|
||||||
"http://accesspoint.emccis.com/rest/objects/"
|
"http://accesspoint.emccis.com/rest/objects/" + getBlob.getSystemMetadata().getObjectID()).toURL()
|
||||||
+ getBlob.getSystemMetadata().getObjectID()).toURL().openStream());
|
.openStream());
|
||||||
assert false : "shouldn't have worked, since it is private";
|
assert false : "shouldn't have worked, since it is private";
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
|
||||||
|
@ -323,29 +311,25 @@ public class AtmosStorageClientLiveTest {
|
||||||
long time = System.currentTimeMillis();
|
long time = System.currentTimeMillis();
|
||||||
try {
|
try {
|
||||||
connection.createFile(privateDirectory, object);
|
connection.createFile(privateDirectory, object);
|
||||||
System.err.printf("%s %s; %dms%n", "created",
|
System.err.printf("%s %s; %dms%n", "created", object.getPayload() instanceof InputStreamPayload ? "stream"
|
||||||
object.getPayload() instanceof InputStreamPayload ? "stream" : "string", System
|
: "string", System.currentTimeMillis() - time);
|
||||||
.currentTimeMillis()
|
|
||||||
- time);
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
String message = Throwables.getRootCause(e).getMessage();
|
String message = Throwables.getRootCause(e).getMessage();
|
||||||
System.err.printf("failure %s %s; %dms: [%s]%n", "creating",
|
System.err.printf("failure %s %s; %dms: [%s]%n", "creating",
|
||||||
object.getPayload() instanceof InputStreamPayload ? "stream" : "string", System
|
object.getPayload() instanceof InputStreamPayload ? "stream" : "string", System.currentTimeMillis()
|
||||||
.currentTimeMillis()
|
|
||||||
- time, message);
|
- time, message);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void deleteConfirmed(final String path) throws InterruptedException, ExecutionException,
|
private void deleteConfirmed(final String path) throws InterruptedException, ExecutionException, TimeoutException {
|
||||||
TimeoutException {
|
|
||||||
long time = System.currentTimeMillis();
|
long time = System.currentTimeMillis();
|
||||||
deleteConsistencyAware(path);
|
deleteConsistencyAware(path);
|
||||||
System.err.printf("confirmed deletion after %dms%n", System.currentTimeMillis() - time);
|
System.err.printf("confirmed deletion after %dms%n", System.currentTimeMillis() - time);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void deleteImmediateAndVerifyWithHead(final String path) throws InterruptedException,
|
protected void deleteImmediateAndVerifyWithHead(final String path) throws InterruptedException, ExecutionException,
|
||||||
ExecutionException, TimeoutException {
|
TimeoutException {
|
||||||
try {
|
try {
|
||||||
connection.deletePath(path);
|
connection.deletePath(path);
|
||||||
} catch (KeyNotFoundException ex) {
|
} catch (KeyNotFoundException ex) {
|
||||||
|
@ -357,8 +341,8 @@ public class AtmosStorageClientLiveTest {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void deleteConsistencyAware(final String path) throws InterruptedException,
|
protected void deleteConsistencyAware(final String path) throws InterruptedException, ExecutionException,
|
||||||
ExecutionException, TimeoutException {
|
TimeoutException {
|
||||||
try {
|
try {
|
||||||
connection.deletePath(path);
|
connection.deletePath(path);
|
||||||
} catch (KeyNotFoundException ex) {
|
} catch (KeyNotFoundException ex) {
|
||||||
|
@ -370,8 +354,7 @@ public class AtmosStorageClientLiveTest {
|
||||||
}, INCONSISTENCY_WINDOW);
|
}, INCONSISTENCY_WINDOW);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void retryAndCheckSystemMetadataAndPutIfPresentReplaceStrategy(AtmosObject object)
|
protected void retryAndCheckSystemMetadataAndPutIfPresentReplaceStrategy(AtmosObject object) throws Exception {
|
||||||
throws Exception {
|
|
||||||
|
|
||||||
int failures = 0;
|
int failures = 0;
|
||||||
while (true) {
|
while (true) {
|
||||||
|
@ -390,8 +373,7 @@ public class AtmosStorageClientLiveTest {
|
||||||
object.getPayload() instanceof InputStreamPayload ? "stream" : "string");
|
object.getPayload() instanceof InputStreamPayload ? "stream" : "string");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkSystemMetadataAndPutIfPresentReplaceStrategy(AtmosObject object)
|
private void checkSystemMetadataAndPutIfPresentReplaceStrategy(AtmosObject object) throws Exception {
|
||||||
throws Exception {
|
|
||||||
long time = System.currentTimeMillis();
|
long time = System.currentTimeMillis();
|
||||||
boolean update = true;
|
boolean update = true;
|
||||||
try {
|
try {
|
||||||
|
@ -405,15 +387,13 @@ public class AtmosStorageClientLiveTest {
|
||||||
else
|
else
|
||||||
connection.createFile(privateDirectory, object);
|
connection.createFile(privateDirectory, object);
|
||||||
System.err.printf("%s %s; %dms%n", update ? "updated" : "created",
|
System.err.printf("%s %s; %dms%n", update ? "updated" : "created",
|
||||||
object.getPayload() instanceof InputStreamPayload ? "stream" : "string", System
|
object.getPayload() instanceof InputStreamPayload ? "stream" : "string", System.currentTimeMillis()
|
||||||
.currentTimeMillis()
|
|
||||||
- time);
|
- time);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
String message = Throwables.getRootCause(e).getMessage();
|
String message = Throwables.getRootCause(e).getMessage();
|
||||||
System.err.printf("failure %s %s; %dms: [%s]%n", update ? "updating" : "creating", object
|
System.err.printf("failure %s %s; %dms: [%s]%n", update ? "updating" : "creating",
|
||||||
.getPayload() instanceof InputStreamPayload ? "stream" : "string", System
|
object.getPayload() instanceof InputStreamPayload ? "stream" : "string", System.currentTimeMillis()
|
||||||
.currentTimeMillis()
|
- time, message);
|
||||||
- time, message);
|
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,8 +22,8 @@ import static org.testng.Assert.assertEquals;
|
||||||
|
|
||||||
import org.jclouds.atmosonline.saas.domain.FileType;
|
import org.jclouds.atmosonline.saas.domain.FileType;
|
||||||
import org.jclouds.atmosonline.saas.domain.SystemMetadata;
|
import org.jclouds.atmosonline.saas.domain.SystemMetadata;
|
||||||
|
import org.jclouds.crypto.CryptoStreams;
|
||||||
import org.jclouds.date.DateService;
|
import org.jclouds.date.DateService;
|
||||||
import org.jclouds.encryption.EncryptionService;
|
|
||||||
import org.jclouds.http.HttpResponse;
|
import org.jclouds.http.HttpResponse;
|
||||||
import org.jclouds.io.Payloads;
|
import org.jclouds.io.Payloads;
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
|
@ -41,29 +41,23 @@ public class ParseSystemMetadataFromHeadersTest {
|
||||||
|
|
||||||
public void test() {
|
public void test() {
|
||||||
Injector injector = Guice.createInjector();
|
Injector injector = Guice.createInjector();
|
||||||
ParseSystemMetadataFromHeaders parser = injector
|
ParseSystemMetadataFromHeaders parser = injector.getInstance(ParseSystemMetadataFromHeaders.class);
|
||||||
.getInstance(ParseSystemMetadataFromHeaders.class);
|
|
||||||
DateService dateService = injector.getInstance(DateService.class);
|
DateService dateService = injector.getInstance(DateService.class);
|
||||||
EncryptionService encryptionService = injector.getInstance(EncryptionService.class);
|
|
||||||
|
|
||||||
HttpResponse response = new HttpResponse(200, "ok", Payloads.newStringPayload(""));
|
HttpResponse response = new HttpResponse(200, "ok", Payloads.newStringPayload(""));
|
||||||
response
|
response.getHeaders().put(
|
||||||
.getHeaders()
|
"x-emc-meta",
|
||||||
.put(
|
"content-md5=1f3870be274f6c49b3e31a0c6728957f, atime=2009-10-12T16:09:42Z, mtime=2009-10-19T04:37:00Z,"
|
||||||
"x-emc-meta",
|
+ " ctime=2009-10-19T04:37:00Z, itime=2009-10-12T16:09:42Z, type=directory, uid=root, "
|
||||||
"content-md5=1f3870be274f6c49b3e31a0c6728957f, atime=2009-10-12T16:09:42Z, mtime=2009-10-19T04:37:00Z,"
|
+ "gid=rootr, objectid=4980cdb2b010109b04a44f7bb83f5f04ad354c638ae5, "
|
||||||
+ " ctime=2009-10-19T04:37:00Z, itime=2009-10-12T16:09:42Z, type=directory, uid=root, "
|
+ "objname=e913e09366364e9ba384b8fead643d43, size=4096, nlink=1, policyname=default");
|
||||||
+ "gid=rootr, objectid=4980cdb2b010109b04a44f7bb83f5f04ad354c638ae5, "
|
SystemMetadata expected = new SystemMetadata(CryptoStreams.hex("1f3870be274f6c49b3e31a0c6728957f"),
|
||||||
+ "objname=e913e09366364e9ba384b8fead643d43, size=4096, nlink=1, policyname=default");
|
|
||||||
SystemMetadata expected = new SystemMetadata(encryptionService
|
|
||||||
.fromHex("1f3870be274f6c49b3e31a0c6728957f"),
|
|
||||||
|
|
||||||
dateService.iso8601SecondsDateParse("2009-10-12T16:09:42Z"), dateService
|
dateService.iso8601SecondsDateParse("2009-10-12T16:09:42Z"), dateService
|
||||||
.iso8601SecondsDateParse("2009-10-19T04:37:00Z"), "rootr", dateService
|
.iso8601SecondsDateParse("2009-10-19T04:37:00Z"), "rootr", dateService
|
||||||
.iso8601SecondsDateParse("2009-10-12T16:09:42Z"), dateService
|
.iso8601SecondsDateParse("2009-10-12T16:09:42Z"), dateService
|
||||||
.iso8601SecondsDateParse("2009-10-19T04:37:00Z"), 1,
|
.iso8601SecondsDateParse("2009-10-19T04:37:00Z"), 1, "4980cdb2b010109b04a44f7bb83f5f04ad354c638ae5",
|
||||||
"4980cdb2b010109b04a44f7bb83f5f04ad354c638ae5", "e913e09366364e9ba384b8fead643d43",
|
"e913e09366364e9ba384b8fead643d43", "default", 4096l, FileType.DIRECTORY, "root"
|
||||||
"default", 4096l, FileType.DIRECTORY, "root"
|
|
||||||
|
|
||||||
);
|
);
|
||||||
SystemMetadata data = parser.apply(response);
|
SystemMetadata data = parser.apply(response);
|
||||||
|
|
|
@ -21,7 +21,6 @@ package org.jclouds.atmosonline.saas.internal;
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
import static com.google.common.util.concurrent.Futures.immediateFailedFuture;
|
import static com.google.common.util.concurrent.Futures.immediateFailedFuture;
|
||||||
import static com.google.common.util.concurrent.Futures.immediateFuture;
|
import static com.google.common.util.concurrent.Futures.immediateFuture;
|
||||||
import static org.jclouds.concurrent.ConcurrentUtils.compose;
|
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
|
@ -47,6 +46,7 @@ import org.jclouds.blobstore.TransientAsyncBlobStore;
|
||||||
import org.jclouds.blobstore.domain.Blob;
|
import org.jclouds.blobstore.domain.Blob;
|
||||||
import org.jclouds.blobstore.domain.BlobMetadata;
|
import org.jclouds.blobstore.domain.BlobMetadata;
|
||||||
import org.jclouds.blobstore.functions.HttpGetOptionsListToGetOptions;
|
import org.jclouds.blobstore.functions.HttpGetOptionsListToGetOptions;
|
||||||
|
import org.jclouds.concurrent.Futures;
|
||||||
import org.jclouds.http.options.GetOptions;
|
import org.jclouds.http.options.GetOptions;
|
||||||
|
|
||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
|
@ -97,7 +97,7 @@ public class StubAtmosStorageAsyncClient implements AtmosStorageAsyncClient {
|
||||||
container = directoryName;
|
container = directoryName;
|
||||||
path = null;
|
path = null;
|
||||||
}
|
}
|
||||||
return compose(blobStore.createContainerInLocation(null, container), new Function<Boolean, URI>() {
|
return Futures.compose(blobStore.createContainerInLocation(null, container), new Function<Boolean, URI>() {
|
||||||
|
|
||||||
public URI apply(Boolean from) {
|
public URI apply(Boolean from) {
|
||||||
if (path != null) {
|
if (path != null) {
|
||||||
|
@ -123,7 +123,7 @@ public class StubAtmosStorageAsyncClient implements AtmosStorageAsyncClient {
|
||||||
object.getContentMetadata().setName(path + "/" + file);
|
object.getContentMetadata().setName(path + "/" + file);
|
||||||
}
|
}
|
||||||
Blob blob = object2Blob.apply(object);
|
Blob blob = object2Blob.apply(object);
|
||||||
return compose(blobStore.putBlob(container, blob), new Function<String, URI>() {
|
return Futures.compose(blobStore.putBlob(container, blob), new Function<String, URI>() {
|
||||||
|
|
||||||
public URI apply(String from) {
|
public URI apply(String from) {
|
||||||
return URI.create(uri);
|
return URI.create(uri);
|
||||||
|
@ -135,7 +135,7 @@ public class StubAtmosStorageAsyncClient implements AtmosStorageAsyncClient {
|
||||||
public ListenableFuture<Void> deletePath(String path) {
|
public ListenableFuture<Void> deletePath(String path) {
|
||||||
if (path.indexOf('/') == path.length() - 1) {
|
if (path.indexOf('/') == path.length() - 1) {
|
||||||
// chop off the trailing slash
|
// chop off the trailing slash
|
||||||
return compose(blobStore.deleteContainerImpl(path.substring(0, path.length() - 1)),
|
return Futures.compose(blobStore.deleteContainerImpl(path.substring(0, path.length() - 1)),
|
||||||
new Function<Boolean, Void>() {
|
new Function<Boolean, Void>() {
|
||||||
|
|
||||||
public Void apply(Boolean from) {
|
public Void apply(Boolean from) {
|
||||||
|
@ -160,7 +160,7 @@ public class StubAtmosStorageAsyncClient implements AtmosStorageAsyncClient {
|
||||||
else {
|
else {
|
||||||
String container = path.substring(0, path.indexOf('/'));
|
String container = path.substring(0, path.indexOf('/'));
|
||||||
path = path.substring(path.indexOf('/') + 1);
|
path = path.substring(path.indexOf('/') + 1);
|
||||||
return compose(blobStore.blobMetadata(container, path), new Function<BlobMetadata, UserMetadata>() {
|
return Futures.compose(blobStore.blobMetadata(container, path), new Function<BlobMetadata, UserMetadata>() {
|
||||||
public UserMetadata apply(BlobMetadata from) {
|
public UserMetadata apply(BlobMetadata from) {
|
||||||
return blob2ObjectInfo.apply(from).getUserMetadata();
|
return blob2ObjectInfo.apply(from).getUserMetadata();
|
||||||
}
|
}
|
||||||
|
@ -172,7 +172,7 @@ public class StubAtmosStorageAsyncClient implements AtmosStorageAsyncClient {
|
||||||
String container = path.substring(0, path.indexOf('/'));
|
String container = path.substring(0, path.indexOf('/'));
|
||||||
path = path.substring(path.indexOf('/') + 1);
|
path = path.substring(path.indexOf('/') + 1);
|
||||||
try {
|
try {
|
||||||
return compose(blobStore.getBlob(container, path), blob2Object, service);
|
return Futures.compose(blobStore.getBlob(container, path), blob2Object, service);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return immediateFailedFuture(Throwables.getRootCause(e));
|
return immediateFailedFuture(Throwables.getRootCause(e));
|
||||||
}
|
}
|
||||||
|
@ -181,7 +181,7 @@ public class StubAtmosStorageAsyncClient implements AtmosStorageAsyncClient {
|
||||||
public ListenableFuture<BoundedSet<? extends DirectoryEntry>> listDirectories(ListOptions... optionsList) {
|
public ListenableFuture<BoundedSet<? extends DirectoryEntry>> listDirectories(ListOptions... optionsList) {
|
||||||
// org.jclouds.blobstore.options.ListOptions options = container2ContainerListOptions
|
// org.jclouds.blobstore.options.ListOptions options = container2ContainerListOptions
|
||||||
// .apply(optionsList);
|
// .apply(optionsList);
|
||||||
return compose(blobStore.list(), resource2ObjectList, service);
|
return Futures.compose(blobStore.list(), resource2ObjectList, service);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ListenableFuture<BoundedSet<? extends DirectoryEntry>> listDirectory(String directoryName,
|
public ListenableFuture<BoundedSet<? extends DirectoryEntry>> listDirectory(String directoryName,
|
||||||
|
@ -194,7 +194,7 @@ public class StubAtmosStorageAsyncClient implements AtmosStorageAsyncClient {
|
||||||
if (!path.equals(""))
|
if (!path.equals(""))
|
||||||
options.inDirectory(path);
|
options.inDirectory(path);
|
||||||
}
|
}
|
||||||
return compose(blobStore.list(container, options), resource2ObjectList, service);
|
return Futures.compose(blobStore.list(container, options), resource2ObjectList, service);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AtmosObject newObject() {
|
public AtmosObject newObject() {
|
||||||
|
@ -222,7 +222,7 @@ public class StubAtmosStorageAsyncClient implements AtmosStorageAsyncClient {
|
||||||
String container = path.substring(0, path.indexOf('/'));
|
String container = path.substring(0, path.indexOf('/'));
|
||||||
String blobName = path.substring(path.indexOf('/') + 1);
|
String blobName = path.substring(path.indexOf('/') + 1);
|
||||||
org.jclouds.blobstore.options.GetOptions getOptions = httpGetOptionsConverter.apply(options);
|
org.jclouds.blobstore.options.GetOptions getOptions = httpGetOptionsConverter.apply(options);
|
||||||
return compose(blobStore.getBlob(container, blobName, getOptions), blob2Object, service);
|
return Futures.compose(blobStore.getBlob(container, blobName, getOptions), blob2Object, service);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ListenableFuture<Void> updateFile(String parent, AtmosObject object) {
|
public ListenableFuture<Void> updateFile(String parent, AtmosObject object) {
|
||||||
|
|
|
@ -25,10 +25,13 @@ import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
import org.jclouds.aws.filters.FormSigner;
|
import org.jclouds.aws.filters.FormSigner;
|
||||||
import org.jclouds.encryption.EncryptionService;
|
import org.jclouds.crypto.Crypto;
|
||||||
|
import org.jclouds.crypto.CryptoStreams;
|
||||||
import org.jclouds.http.HttpRequest;
|
import org.jclouds.http.HttpRequest;
|
||||||
import org.jclouds.rest.Binder;
|
import org.jclouds.rest.Binder;
|
||||||
|
|
||||||
|
import com.google.common.base.Charsets;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
|
@ -36,16 +39,14 @@ import org.jclouds.rest.Binder;
|
||||||
@Singleton
|
@Singleton
|
||||||
public class BindS3UploadPolicyAndSignature implements Binder {
|
public class BindS3UploadPolicyAndSignature implements Binder {
|
||||||
private final FormSigner signer;
|
private final FormSigner signer;
|
||||||
private final EncryptionService encryptionService;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
BindS3UploadPolicyAndSignature(FormSigner signer, EncryptionService encryptionService) {
|
BindS3UploadPolicyAndSignature(FormSigner signer, Crypto crypto) {
|
||||||
this.signer = signer;
|
this.signer = signer;
|
||||||
this.encryptionService = encryptionService;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void bindToRequest(HttpRequest request, Object input) {
|
public void bindToRequest(HttpRequest request, Object input) {
|
||||||
String encodedJson = encryptionService.base64(checkNotNull(input, "json").toString().getBytes());
|
String encodedJson = CryptoStreams.base64(checkNotNull(input, "json").toString().getBytes(Charsets.UTF_8));
|
||||||
addFormParamTo(request, "Storage.S3.UploadPolicy", encodedJson);
|
addFormParamTo(request, "Storage.S3.UploadPolicy", encodedJson);
|
||||||
String signature = signer.sign(encodedJson);
|
String signature = signer.sign(encodedJson);
|
||||||
addFormParamTo(request, "Storage.S3.UploadPolicySignature", signature);
|
addFormParamTo(request, "Storage.S3.UploadPolicySignature", signature);
|
||||||
|
|
|
@ -36,7 +36,7 @@ import static org.jclouds.aws.ec2.util.EC2Utils.getAllRunningInstancesInRegion;
|
||||||
import static org.jclouds.aws.ec2.util.EC2Utils.parseHandle;
|
import static org.jclouds.aws.ec2.util.EC2Utils.parseHandle;
|
||||||
import static org.jclouds.compute.domain.OsFamily.CENTOS;
|
import static org.jclouds.compute.domain.OsFamily.CENTOS;
|
||||||
import static org.jclouds.compute.domain.OsFamily.UBUNTU;
|
import static org.jclouds.compute.domain.OsFamily.UBUNTU;
|
||||||
import static org.jclouds.concurrent.ConcurrentUtils.transformParallel;
|
import static org.jclouds.concurrent.FutureIterables.transformParallel;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
|
@ -124,7 +124,6 @@ import com.google.common.base.Splitter;
|
||||||
import com.google.common.base.Supplier;
|
import com.google.common.base.Supplier;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.common.collect.Iterables;
|
|
||||||
import com.google.common.collect.MapMaker;
|
import com.google.common.collect.MapMaker;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
import com.google.inject.AbstractModule;
|
import com.google.inject.AbstractModule;
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
package org.jclouds.aws.ec2.compute.strategy;
|
package org.jclouds.aws.ec2.compute.strategy;
|
||||||
|
|
||||||
import static com.google.common.collect.Iterables.concat;
|
import static com.google.common.collect.Iterables.concat;
|
||||||
import static org.jclouds.concurrent.ConcurrentUtils.transformParallel;
|
import static org.jclouds.concurrent.FutureIterables.transformParallel;
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
|
|
@ -21,10 +21,9 @@ package org.jclouds.aws.ec2.functions;
|
||||||
import static com.google.common.base.Preconditions.checkArgument;
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
import org.jclouds.encryption.EncryptionService;
|
import org.jclouds.crypto.CryptoStreams;
|
||||||
|
|
||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
|
|
||||||
|
@ -36,17 +35,13 @@ import com.google.common.base.Function;
|
||||||
@Singleton
|
@Singleton
|
||||||
public class ConvertUnencodedBytesToBase64EncodedString implements Function<Object, String> {
|
public class ConvertUnencodedBytesToBase64EncodedString implements Function<Object, String> {
|
||||||
|
|
||||||
@Inject
|
|
||||||
EncryptionService encryptionService;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String apply(Object from) {
|
public String apply(Object from) {
|
||||||
checkArgument(checkNotNull(from, "input") instanceof byte[],
|
checkArgument(checkNotNull(from, "input") instanceof byte[], "this binder is only valid for byte []!");
|
||||||
"this binder is only valid for byte []!");
|
|
||||||
|
|
||||||
byte[] unencodedData = (byte[]) from;
|
byte[] unencodedData = (byte[]) from;
|
||||||
checkArgument(checkNotNull(unencodedData, "unencodedData").length <= 16 * 1024,
|
checkArgument(checkNotNull(unencodedData, "unencodedData").length <= 16 * 1024,
|
||||||
"userData cannot be larger than 16kb");
|
"userData cannot be larger than 16kb");
|
||||||
return encryptionService.base64(unencodedData);
|
return CryptoStreams.base64(unencodedData);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -18,9 +18,7 @@
|
||||||
*/
|
*/
|
||||||
package org.jclouds.aws.ec2.xml;
|
package org.jclouds.aws.ec2.xml;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import org.jclouds.crypto.CryptoStreams;
|
||||||
|
|
||||||
import org.jclouds.encryption.EncryptionService;
|
|
||||||
|
|
||||||
import com.google.common.base.Charsets;
|
import com.google.common.base.Charsets;
|
||||||
|
|
||||||
|
@ -32,12 +30,8 @@ import com.google.common.base.Charsets;
|
||||||
*/
|
*/
|
||||||
public class UnencodeStringValueHandler extends StringValueHandler {
|
public class UnencodeStringValueHandler extends StringValueHandler {
|
||||||
|
|
||||||
@Inject
|
|
||||||
private EncryptionService encryptionService;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getResult() {
|
public String getResult() {
|
||||||
return super.getResult() == null ? null : new String(encryptionService.fromBase64(super
|
return super.getResult() == null ? null : new String(CryptoStreams.base64(super.getResult()), Charsets.UTF_8);
|
||||||
.getResult()), Charsets.UTF_8);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,13 +43,15 @@ import javax.inject.Singleton;
|
||||||
import javax.ws.rs.core.HttpHeaders;
|
import javax.ws.rs.core.HttpHeaders;
|
||||||
|
|
||||||
import org.jclouds.Constants;
|
import org.jclouds.Constants;
|
||||||
|
import org.jclouds.crypto.Crypto;
|
||||||
|
import org.jclouds.crypto.CryptoStreams;
|
||||||
import org.jclouds.date.TimeStamp;
|
import org.jclouds.date.TimeStamp;
|
||||||
import org.jclouds.encryption.EncryptionService;
|
|
||||||
import org.jclouds.http.HttpException;
|
import org.jclouds.http.HttpException;
|
||||||
import org.jclouds.http.HttpRequest;
|
import org.jclouds.http.HttpRequest;
|
||||||
import org.jclouds.http.HttpRequestFilter;
|
import org.jclouds.http.HttpRequestFilter;
|
||||||
import org.jclouds.http.HttpUtils;
|
import org.jclouds.http.HttpUtils;
|
||||||
import org.jclouds.http.internal.SignatureWire;
|
import org.jclouds.http.internal.SignatureWire;
|
||||||
|
import org.jclouds.io.InputSuppliers;
|
||||||
import org.jclouds.logging.Logger;
|
import org.jclouds.logging.Logger;
|
||||||
import org.jclouds.rest.RequestSigner;
|
import org.jclouds.rest.RequestSigner;
|
||||||
import org.jclouds.util.Utils;
|
import org.jclouds.util.Utils;
|
||||||
|
@ -69,13 +71,13 @@ import com.google.common.collect.Multimap;
|
||||||
@Singleton
|
@Singleton
|
||||||
public class FormSigner implements HttpRequestFilter, RequestSigner {
|
public class FormSigner implements HttpRequestFilter, RequestSigner {
|
||||||
|
|
||||||
public static String[] mandatoryParametersForSignature = new String[] { ACTION,
|
public static String[] mandatoryParametersForSignature = new String[] { ACTION, SIGNATURE_METHOD, SIGNATURE_VERSION,
|
||||||
SIGNATURE_METHOD, SIGNATURE_VERSION, VERSION };
|
VERSION };
|
||||||
private final SignatureWire signatureWire;
|
private final SignatureWire signatureWire;
|
||||||
private final String accessKey;
|
private final String accessKey;
|
||||||
private final String secretKey;
|
private final String secretKey;
|
||||||
private final Provider<String> dateService;
|
private final Provider<String> dateService;
|
||||||
private final EncryptionService encryptionService;
|
private final Crypto crypto;
|
||||||
private final HttpUtils utils;
|
private final HttpUtils utils;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
|
@ -83,24 +85,20 @@ public class FormSigner implements HttpRequestFilter, RequestSigner {
|
||||||
private Logger signatureLog = Logger.NULL;
|
private Logger signatureLog = Logger.NULL;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public FormSigner(SignatureWire signatureWire,
|
public FormSigner(SignatureWire signatureWire, @Named(Constants.PROPERTY_IDENTITY) String accessKey,
|
||||||
@Named(Constants.PROPERTY_IDENTITY) String accessKey,
|
@Named(Constants.PROPERTY_CREDENTIAL) String secretKey, @TimeStamp Provider<String> dateService,
|
||||||
@Named(Constants.PROPERTY_CREDENTIAL) String secretKey,
|
Crypto crypto, HttpUtils utils) {
|
||||||
@TimeStamp Provider<String> dateService, EncryptionService encryptionService,
|
|
||||||
HttpUtils utils) {
|
|
||||||
this.signatureWire = signatureWire;
|
this.signatureWire = signatureWire;
|
||||||
this.accessKey = accessKey;
|
this.accessKey = accessKey;
|
||||||
this.secretKey = secretKey;
|
this.secretKey = secretKey;
|
||||||
this.dateService = dateService;
|
this.dateService = dateService;
|
||||||
this.encryptionService = encryptionService;
|
this.crypto = crypto;
|
||||||
this.utils = utils;
|
this.utils = utils;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void filter(HttpRequest request) throws HttpException {
|
public void filter(HttpRequest request) throws HttpException {
|
||||||
checkNotNull(request.getFirstHeaderOrNull(HttpHeaders.HOST),
|
checkNotNull(request.getFirstHeaderOrNull(HttpHeaders.HOST), "request is not ready to sign; host not present");
|
||||||
"request is not ready to sign; host not present");
|
Multimap<String, String> decodedParams = parseQueryToMap(request.getPayload().getRawContent().toString());
|
||||||
Multimap<String, String> decodedParams = parseQueryToMap(request.getPayload().getRawContent()
|
|
||||||
.toString());
|
|
||||||
addSigningParams(decodedParams);
|
addSigningParams(decodedParams);
|
||||||
validateParams(decodedParams);
|
validateParams(decodedParams);
|
||||||
String stringToSign = createStringToSign(request, decodedParams);
|
String stringToSign = createStringToSign(request, decodedParams);
|
||||||
|
@ -141,8 +139,7 @@ public class FormSigner implements HttpRequestFilter, RequestSigner {
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
void validateParams(Multimap<String, String> params) {
|
void validateParams(Multimap<String, String> params) {
|
||||||
for (String parameter : mandatoryParametersForSignature) {
|
for (String parameter : mandatoryParametersForSignature) {
|
||||||
checkState(params.containsKey(parameter), "parameter " + parameter
|
checkState(params.containsKey(parameter), "parameter " + parameter + " is required for signature");
|
||||||
+ " is required for signature");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,8 +152,8 @@ public class FormSigner implements HttpRequestFilter, RequestSigner {
|
||||||
public String sign(String stringToSign) {
|
public String sign(String stringToSign) {
|
||||||
String signature;
|
String signature;
|
||||||
try {
|
try {
|
||||||
signature = encryptionService.base64(encryptionService.hmacSha256(stringToSign, secretKey
|
signature = CryptoStreams.base64(CryptoStreams.mac(InputSuppliers.of(stringToSign), crypto
|
||||||
.getBytes()));
|
.hmacSHA256(secretKey.getBytes())));
|
||||||
if (signatureWire.enabled())
|
if (signatureWire.enabled())
|
||||||
signatureWire.input(Utils.toInputStream(signature));
|
signatureWire.input(Utils.toInputStream(signature));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
@ -172,8 +169,7 @@ public class FormSigner implements HttpRequestFilter, RequestSigner {
|
||||||
// StringToSign = HTTPVerb + "\n" +
|
// StringToSign = HTTPVerb + "\n" +
|
||||||
stringToSign.append(request.getMethod()).append("\n");
|
stringToSign.append(request.getMethod()).append("\n");
|
||||||
// ValueOfHostHeaderInLowercase + "\n" +
|
// ValueOfHostHeaderInLowercase + "\n" +
|
||||||
stringToSign.append(request.getFirstHeaderOrNull(HttpHeaders.HOST).toLowerCase())
|
stringToSign.append(request.getFirstHeaderOrNull(HttpHeaders.HOST).toLowerCase()).append("\n");
|
||||||
.append("\n");
|
|
||||||
// HTTPRequestURI + "\n" +
|
// HTTPRequestURI + "\n" +
|
||||||
stringToSign.append(request.getEndpoint().getPath()).append("\n");
|
stringToSign.append(request.getEndpoint().getPath()).append("\n");
|
||||||
// CanonicalizedFormString <from the preceding step>
|
// CanonicalizedFormString <from the preceding step>
|
||||||
|
@ -204,8 +200,7 @@ public class FormSigner implements HttpRequestFilter, RequestSigner {
|
||||||
}
|
}
|
||||||
|
|
||||||
public String createStringToSign(HttpRequest input) {
|
public String createStringToSign(HttpRequest input) {
|
||||||
return createStringToSign(input, parseQueryToMap(input.getPayload().getRawContent()
|
return createStringToSign(input, parseQueryToMap(input.getPayload().getRawContent().toString()));
|
||||||
.toString()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,11 +19,9 @@
|
||||||
package org.jclouds.aws.s3.blobstore;
|
package org.jclouds.aws.s3.blobstore;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
import static org.jclouds.concurrent.ConcurrentUtils.compose;
|
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
|
@ -56,11 +54,13 @@ import org.jclouds.blobstore.internal.BaseAsyncBlobStore;
|
||||||
import org.jclouds.blobstore.options.ListContainerOptions;
|
import org.jclouds.blobstore.options.ListContainerOptions;
|
||||||
import org.jclouds.blobstore.strategy.internal.FetchBlobMetadata;
|
import org.jclouds.blobstore.strategy.internal.FetchBlobMetadata;
|
||||||
import org.jclouds.blobstore.util.BlobUtils;
|
import org.jclouds.blobstore.util.BlobUtils;
|
||||||
|
import org.jclouds.concurrent.Futures;
|
||||||
import org.jclouds.domain.Location;
|
import org.jclouds.domain.Location;
|
||||||
import org.jclouds.http.options.GetOptions;
|
import org.jclouds.http.options.GetOptions;
|
||||||
|
|
||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
@ -106,7 +106,7 @@ public class S3AsyncBlobStore extends BaseAsyncBlobStore {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public ListenableFuture<PageSet<? extends StorageMetadata>> list() {
|
public ListenableFuture<PageSet<? extends StorageMetadata>> list() {
|
||||||
return compose(async.listOwnedBuckets(),
|
return Futures.compose(async.listOwnedBuckets(),
|
||||||
new Function<Set<BucketMetadata>, org.jclouds.blobstore.domain.PageSet<? extends StorageMetadata>>() {
|
new Function<Set<BucketMetadata>, org.jclouds.blobstore.domain.PageSet<? extends StorageMetadata>>() {
|
||||||
public org.jclouds.blobstore.domain.PageSet<? extends StorageMetadata> apply(Set<BucketMetadata> from) {
|
public org.jclouds.blobstore.domain.PageSet<? extends StorageMetadata> apply(Set<BucketMetadata> from) {
|
||||||
return new PageSetImpl<StorageMetadata>(Iterables.transform(from, bucket2ResourceMd), null);
|
return new PageSetImpl<StorageMetadata>(Iterables.transform(from, bucket2ResourceMd), null);
|
||||||
|
@ -150,9 +150,10 @@ public class S3AsyncBlobStore extends BaseAsyncBlobStore {
|
||||||
public ListenableFuture<PageSet<? extends StorageMetadata>> list(String container, ListContainerOptions options) {
|
public ListenableFuture<PageSet<? extends StorageMetadata>> list(String container, ListContainerOptions options) {
|
||||||
ListBucketOptions httpOptions = container2BucketListOptions.apply(options);
|
ListBucketOptions httpOptions = container2BucketListOptions.apply(options);
|
||||||
ListenableFuture<ListBucketResponse> returnVal = async.listBucket(container, httpOptions);
|
ListenableFuture<ListBucketResponse> returnVal = async.listBucket(container, httpOptions);
|
||||||
ListenableFuture<PageSet<? extends StorageMetadata>> list = compose(returnVal, bucket2ResourceList, service);
|
ListenableFuture<PageSet<? extends StorageMetadata>> list = Futures.compose(returnVal, bucket2ResourceList,
|
||||||
return (options.isDetailed()) ? compose(list, fetchBlobMetadataProvider.get().setContainerName(container),
|
service);
|
||||||
service) : list;
|
return (options.isDetailed()) ? Futures.compose(list,
|
||||||
|
fetchBlobMetadataProvider.get().setContainerName(container), service) : list;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -185,7 +186,7 @@ public class S3AsyncBlobStore extends BaseAsyncBlobStore {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public ListenableFuture<BlobMetadata> blobMetadata(String container, String key) {
|
public ListenableFuture<BlobMetadata> blobMetadata(String container, String key) {
|
||||||
return compose(async.headObject(container, key), new Function<ObjectMetadata, BlobMetadata>() {
|
return Futures.compose(async.headObject(container, key), new Function<ObjectMetadata, BlobMetadata>() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BlobMetadata apply(ObjectMetadata from) {
|
public BlobMetadata apply(ObjectMetadata from) {
|
||||||
|
@ -206,7 +207,7 @@ public class S3AsyncBlobStore extends BaseAsyncBlobStore {
|
||||||
@Override
|
@Override
|
||||||
public ListenableFuture<Blob> getBlob(String container, String key, org.jclouds.blobstore.options.GetOptions options) {
|
public ListenableFuture<Blob> getBlob(String container, String key, org.jclouds.blobstore.options.GetOptions options) {
|
||||||
GetOptions httpOptions = blob2ObjectGetOptions.apply(options);
|
GetOptions httpOptions = blob2ObjectGetOptions.apply(options);
|
||||||
return compose(async.getObject(container, key, httpOptions), object2Blob, service);
|
return Futures.compose(async.getObject(container, key, httpOptions), object2Blob, service);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
*/
|
*/
|
||||||
package org.jclouds.aws.s3.domain;
|
package org.jclouds.aws.s3.domain;
|
||||||
|
|
||||||
import org.jclouds.http.PayloadEnclosing;
|
import org.jclouds.io.PayloadEnclosing;
|
||||||
|
|
||||||
import com.google.common.collect.Multimap;
|
import com.google.common.collect.Multimap;
|
||||||
import com.google.inject.internal.Nullable;
|
import com.google.inject.internal.Nullable;
|
||||||
|
|
|
@ -25,9 +25,9 @@ import javax.inject.Inject;
|
||||||
import org.jclouds.aws.s3.domain.AccessControlList;
|
import org.jclouds.aws.s3.domain.AccessControlList;
|
||||||
import org.jclouds.aws.s3.domain.MutableObjectMetadata;
|
import org.jclouds.aws.s3.domain.MutableObjectMetadata;
|
||||||
import org.jclouds.aws.s3.domain.S3Object;
|
import org.jclouds.aws.s3.domain.S3Object;
|
||||||
import org.jclouds.http.PayloadEnclosing;
|
|
||||||
import org.jclouds.http.internal.PayloadEnclosingImpl;
|
import org.jclouds.http.internal.PayloadEnclosingImpl;
|
||||||
import org.jclouds.io.Payload;
|
import org.jclouds.io.Payload;
|
||||||
|
import org.jclouds.io.PayloadEnclosing;
|
||||||
import org.jclouds.io.payloads.DelegatingPayload;
|
import org.jclouds.io.payloads.DelegatingPayload;
|
||||||
|
|
||||||
import com.google.common.collect.LinkedHashMultimap;
|
import com.google.common.collect.LinkedHashMultimap;
|
||||||
|
|
|
@ -43,13 +43,15 @@ import javax.ws.rs.core.HttpHeaders;
|
||||||
|
|
||||||
import org.jclouds.Constants;
|
import org.jclouds.Constants;
|
||||||
import org.jclouds.aws.s3.Bucket;
|
import org.jclouds.aws.s3.Bucket;
|
||||||
|
import org.jclouds.crypto.Crypto;
|
||||||
|
import org.jclouds.crypto.CryptoStreams;
|
||||||
import org.jclouds.date.TimeStamp;
|
import org.jclouds.date.TimeStamp;
|
||||||
import org.jclouds.encryption.EncryptionService;
|
|
||||||
import org.jclouds.http.HttpException;
|
import org.jclouds.http.HttpException;
|
||||||
import org.jclouds.http.HttpRequest;
|
import org.jclouds.http.HttpRequest;
|
||||||
import org.jclouds.http.HttpRequestFilter;
|
import org.jclouds.http.HttpRequestFilter;
|
||||||
import org.jclouds.http.HttpUtils;
|
import org.jclouds.http.HttpUtils;
|
||||||
import org.jclouds.http.internal.SignatureWire;
|
import org.jclouds.http.internal.SignatureWire;
|
||||||
|
import org.jclouds.io.InputSuppliers;
|
||||||
import org.jclouds.logging.Logger;
|
import org.jclouds.logging.Logger;
|
||||||
import org.jclouds.rest.RequestSigner;
|
import org.jclouds.rest.RequestSigner;
|
||||||
import org.jclouds.rest.internal.GeneratedHttpRequest;
|
import org.jclouds.rest.internal.GeneratedHttpRequest;
|
||||||
|
@ -71,13 +73,13 @@ import com.google.common.collect.Iterables;
|
||||||
public class RequestAuthorizeSignature implements HttpRequestFilter, RequestSigner {
|
public class RequestAuthorizeSignature implements HttpRequestFilter, RequestSigner {
|
||||||
private final String[] firstHeadersToSign = new String[] { HttpHeaders.DATE };
|
private final String[] firstHeadersToSign = new String[] { HttpHeaders.DATE };
|
||||||
|
|
||||||
public static Set<String> SPECIAL_QUERIES = ImmutableSet.of("acl", "torrent", "logging",
|
public static Set<String> SPECIAL_QUERIES = ImmutableSet.of("acl", "torrent", "logging", "location",
|
||||||
"location", "requestPayment");
|
"requestPayment");
|
||||||
private final SignatureWire signatureWire;
|
private final SignatureWire signatureWire;
|
||||||
private final String accessKey;
|
private final String accessKey;
|
||||||
private final String secretKey;
|
private final String secretKey;
|
||||||
private final Provider<String> timeStampProvider;
|
private final Provider<String> timeStampProvider;
|
||||||
private final EncryptionService encryptionService;
|
private final Crypto crypto;
|
||||||
private final HttpUtils utils;
|
private final HttpUtils utils;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
|
@ -90,15 +92,11 @@ public class RequestAuthorizeSignature implements HttpRequestFilter, RequestSign
|
||||||
private final boolean isVhostStyle;
|
private final boolean isVhostStyle;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public RequestAuthorizeSignature(SignatureWire signatureWire,
|
public RequestAuthorizeSignature(SignatureWire signatureWire, @Named(PROPERTY_AUTH_TAG) String authTag,
|
||||||
@Named(PROPERTY_AUTH_TAG) String authTag,
|
|
||||||
@Named(PROPERTY_S3_VIRTUAL_HOST_BUCKETS) boolean isVhostStyle,
|
@Named(PROPERTY_S3_VIRTUAL_HOST_BUCKETS) boolean isVhostStyle,
|
||||||
@Named(PROPERTY_S3_SERVICE_PATH) String servicePath,
|
@Named(PROPERTY_S3_SERVICE_PATH) String servicePath, @Named(PROPERTY_HEADER_TAG) String headerTag,
|
||||||
@Named(PROPERTY_HEADER_TAG) String headerTag,
|
@Named(PROPERTY_IDENTITY) String accessKey, @Named(PROPERTY_CREDENTIAL) String secretKey,
|
||||||
@Named(PROPERTY_IDENTITY) String accessKey,
|
@TimeStamp Provider<String> timeStampProvider, Crypto crypto, HttpUtils utils) {
|
||||||
@Named(PROPERTY_CREDENTIAL) String secretKey,
|
|
||||||
@TimeStamp Provider<String> timeStampProvider, EncryptionService encryptionService,
|
|
||||||
HttpUtils utils) {
|
|
||||||
this.isVhostStyle = isVhostStyle;
|
this.isVhostStyle = isVhostStyle;
|
||||||
this.servicePath = servicePath;
|
this.servicePath = servicePath;
|
||||||
this.headerTag = headerTag;
|
this.headerTag = headerTag;
|
||||||
|
@ -107,7 +105,7 @@ public class RequestAuthorizeSignature implements HttpRequestFilter, RequestSign
|
||||||
this.accessKey = accessKey;
|
this.accessKey = accessKey;
|
||||||
this.secretKey = secretKey;
|
this.secretKey = secretKey;
|
||||||
this.timeStampProvider = timeStampProvider;
|
this.timeStampProvider = timeStampProvider;
|
||||||
this.encryptionService = encryptionService;
|
this.crypto = crypto;
|
||||||
this.utils = utils;
|
this.utils = utils;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,8 +143,8 @@ public class RequestAuthorizeSignature implements HttpRequestFilter, RequestSign
|
||||||
public String sign(String toSign) {
|
public String sign(String toSign) {
|
||||||
String signature;
|
String signature;
|
||||||
try {
|
try {
|
||||||
signature = encryptionService.base64(encryptionService.hmacSha1(toSign, secretKey
|
signature = CryptoStreams.base64(CryptoStreams.mac(InputSuppliers.of(toSign), crypto.hmacSHA1(secretKey
|
||||||
.getBytes()));
|
.getBytes())));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new HttpException("error signing request", e);
|
throw new HttpException("error signing request", e);
|
||||||
}
|
}
|
||||||
|
@ -158,8 +156,7 @@ public class RequestAuthorizeSignature implements HttpRequestFilter, RequestSign
|
||||||
}
|
}
|
||||||
|
|
||||||
void replaceDateHeader(HttpRequest request) {
|
void replaceDateHeader(HttpRequest request) {
|
||||||
request.getHeaders().replaceValues(HttpHeaders.DATE,
|
request.getHeaders().replaceValues(HttpHeaders.DATE, Collections.singletonList(timeStampProvider.get()));
|
||||||
Collections.singletonList(timeStampProvider.get()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void appendAmzHeaders(HttpRequest request, StringBuilder toSign) {
|
void appendAmzHeaders(HttpRequest request, StringBuilder toSign) {
|
||||||
|
@ -177,12 +174,10 @@ public class RequestAuthorizeSignature implements HttpRequestFilter, RequestSign
|
||||||
}
|
}
|
||||||
|
|
||||||
void appendPayloadMetadata(HttpRequest request, StringBuilder buffer) {
|
void appendPayloadMetadata(HttpRequest request, StringBuilder buffer) {
|
||||||
buffer.append(
|
buffer.append(utils.valueOrEmpty(request.getPayload() == null ? null : request.getPayload().getContentMD5()))
|
||||||
utils.valueOrEmpty(request.getPayload() == null ? null : request.getPayload()
|
.append("\n");
|
||||||
.getContentMD5())).append("\n");
|
buffer.append(utils.valueOrEmpty(request.getPayload() == null ? null : request.getPayload().getContentType()))
|
||||||
buffer.append(
|
.append("\n");
|
||||||
utils.valueOrEmpty(request.getPayload() == null ? null : request.getPayload()
|
|
||||||
.getContentType())).append("\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void appendHttpHeaders(HttpRequest request, StringBuilder toSign) {
|
void appendHttpHeaders(HttpRequest request, StringBuilder toSign) {
|
||||||
|
@ -192,8 +187,7 @@ public class RequestAuthorizeSignature implements HttpRequestFilter, RequestSign
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
void appendBucketName(HttpRequest req, StringBuilder toSign) {
|
void appendBucketName(HttpRequest req, StringBuilder toSign) {
|
||||||
checkArgument(req instanceof GeneratedHttpRequest<?>,
|
checkArgument(req instanceof GeneratedHttpRequest<?>, "this should be a generated http request");
|
||||||
"this should be a generated http request");
|
|
||||||
GeneratedHttpRequest<?> request = GeneratedHttpRequest.class.cast(req);
|
GeneratedHttpRequest<?> request = GeneratedHttpRequest.class.cast(req);
|
||||||
|
|
||||||
String bucketName = null;
|
String bucketName = null;
|
||||||
|
|
|
@ -29,7 +29,7 @@ import org.jclouds.aws.s3.blobstore.functions.BlobToObjectMetadata;
|
||||||
import org.jclouds.aws.s3.domain.MutableObjectMetadata;
|
import org.jclouds.aws.s3.domain.MutableObjectMetadata;
|
||||||
import org.jclouds.blobstore.domain.BlobMetadata;
|
import org.jclouds.blobstore.domain.BlobMetadata;
|
||||||
import org.jclouds.blobstore.functions.ParseSystemAndUserMetadataFromHeaders;
|
import org.jclouds.blobstore.functions.ParseSystemAndUserMetadataFromHeaders;
|
||||||
import org.jclouds.encryption.EncryptionService;
|
import org.jclouds.crypto.CryptoStreams;
|
||||||
import org.jclouds.http.HttpRequest;
|
import org.jclouds.http.HttpRequest;
|
||||||
import org.jclouds.http.HttpResponse;
|
import org.jclouds.http.HttpResponse;
|
||||||
import org.jclouds.rest.InvocationContext;
|
import org.jclouds.rest.InvocationContext;
|
||||||
|
@ -45,20 +45,16 @@ import com.google.common.base.Function;
|
||||||
* @see <a href="http://docs.amazonwebservices.com/AmazonS3/latest/RESTObjectGET.html" />
|
* @see <a href="http://docs.amazonwebservices.com/AmazonS3/latest/RESTObjectGET.html" />
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
*/
|
*/
|
||||||
public class ParseObjectMetadataFromHeaders implements
|
public class ParseObjectMetadataFromHeaders implements Function<HttpResponse, MutableObjectMetadata>, InvocationContext {
|
||||||
Function<HttpResponse, MutableObjectMetadata>, InvocationContext {
|
|
||||||
private final ParseSystemAndUserMetadataFromHeaders blobMetadataParser;
|
private final ParseSystemAndUserMetadataFromHeaders blobMetadataParser;
|
||||||
private final BlobToObjectMetadata blobToObjectMetadata;
|
private final BlobToObjectMetadata blobToObjectMetadata;
|
||||||
private final EncryptionService encryptionService;
|
|
||||||
private final String userMdPrefix;
|
private final String userMdPrefix;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public ParseObjectMetadataFromHeaders(ParseSystemAndUserMetadataFromHeaders blobMetadataParser,
|
public ParseObjectMetadataFromHeaders(ParseSystemAndUserMetadataFromHeaders blobMetadataParser,
|
||||||
BlobToObjectMetadata blobToObjectMetadata, EncryptionService encryptionService,
|
BlobToObjectMetadata blobToObjectMetadata, @Named(PROPERTY_USER_METADATA_PREFIX) String userMdPrefix) {
|
||||||
@Named(PROPERTY_USER_METADATA_PREFIX) String userMdPrefix) {
|
|
||||||
this.blobMetadataParser = blobMetadataParser;
|
this.blobMetadataParser = blobMetadataParser;
|
||||||
this.blobToObjectMetadata = blobToObjectMetadata;
|
this.blobToObjectMetadata = blobToObjectMetadata;
|
||||||
this.encryptionService = encryptionService;
|
|
||||||
this.userMdPrefix = userMdPrefix;
|
this.userMdPrefix = userMdPrefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,7 +67,7 @@ public class ParseObjectMetadataFromHeaders implements
|
||||||
MutableObjectMetadata to = blobToObjectMetadata.apply(base);
|
MutableObjectMetadata to = blobToObjectMetadata.apply(base);
|
||||||
addETagTo(from, to);
|
addETagTo(from, to);
|
||||||
to.setSize(attemptToParseSizeAndRangeFromHeaders(from));
|
to.setSize(attemptToParseSizeAndRangeFromHeaders(from));
|
||||||
to.setContentMD5(encryptionService.fromHex(Utils.replaceAll(to.getETag(), '"', "")));
|
to.setContentMD5(CryptoStreams.hex(Utils.replaceAll(to.getETag(), '"', "")));
|
||||||
to.setCacheControl(from.getFirstHeaderOrNull(HttpHeaders.CACHE_CONTROL));
|
to.setCacheControl(from.getFirstHeaderOrNull(HttpHeaders.CACHE_CONTROL));
|
||||||
to.setContentDisposition(from.getFirstHeaderOrNull("Content-Disposition"));
|
to.setContentDisposition(from.getFirstHeaderOrNull("Content-Disposition"));
|
||||||
to.setContentEncoding(from.getFirstHeaderOrNull(HttpHeaders.CONTENT_ENCODING));
|
to.setContentEncoding(from.getFirstHeaderOrNull(HttpHeaders.CONTENT_ENCODING));
|
||||||
|
|
|
@ -29,8 +29,8 @@ import org.jclouds.aws.s3.domain.ObjectMetadata;
|
||||||
import org.jclouds.aws.s3.domain.ObjectMetadata.StorageClass;
|
import org.jclouds.aws.s3.domain.ObjectMetadata.StorageClass;
|
||||||
import org.jclouds.aws.s3.domain.internal.BucketListObjectMetadata;
|
import org.jclouds.aws.s3.domain.internal.BucketListObjectMetadata;
|
||||||
import org.jclouds.aws.s3.domain.internal.ListBucketResponseImpl;
|
import org.jclouds.aws.s3.domain.internal.ListBucketResponseImpl;
|
||||||
|
import org.jclouds.crypto.CryptoStreams;
|
||||||
import org.jclouds.date.DateService;
|
import org.jclouds.date.DateService;
|
||||||
import org.jclouds.encryption.EncryptionService;
|
|
||||||
import org.jclouds.http.functions.ParseSax;
|
import org.jclouds.http.functions.ParseSax;
|
||||||
import org.jclouds.util.Utils;
|
import org.jclouds.util.Utils;
|
||||||
import org.xml.sax.Attributes;
|
import org.xml.sax.Attributes;
|
||||||
|
@ -54,7 +54,6 @@ public class ListBucketHandler extends ParseSax.HandlerWithResult<ListBucketResp
|
||||||
private StringBuilder currentText = new StringBuilder();
|
private StringBuilder currentText = new StringBuilder();
|
||||||
|
|
||||||
private final DateService dateParser;
|
private final DateService dateParser;
|
||||||
private final EncryptionService encryptionService;
|
|
||||||
|
|
||||||
private String bucketName;
|
private String bucketName;
|
||||||
private String prefix;
|
private String prefix;
|
||||||
|
@ -64,16 +63,15 @@ public class ListBucketHandler extends ParseSax.HandlerWithResult<ListBucketResp
|
||||||
private boolean isTruncated;
|
private boolean isTruncated;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public ListBucketHandler(DateService dateParser, EncryptionService encryptionService) {
|
public ListBucketHandler(DateService dateParser) {
|
||||||
this.dateParser = dateParser;
|
this.dateParser = dateParser;
|
||||||
this.encryptionService = encryptionService;
|
|
||||||
this.contents = Sets.newLinkedHashSet();
|
this.contents = Sets.newLinkedHashSet();
|
||||||
this.commonPrefixes = Sets.newLinkedHashSet();
|
this.commonPrefixes = Sets.newLinkedHashSet();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ListBucketResponse getResult() {
|
public ListBucketResponse getResult() {
|
||||||
return new ListBucketResponseImpl(bucketName, contents, prefix, marker, nextMarker,
|
return new ListBucketResponseImpl(bucketName, contents, prefix, marker, nextMarker, maxResults, delimiter,
|
||||||
maxResults, delimiter, isTruncated, commonPrefixes);
|
isTruncated, commonPrefixes);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean inCommonPrefixes;
|
private boolean inCommonPrefixes;
|
||||||
|
@ -102,15 +100,15 @@ public class ListBucketHandler extends ParseSax.HandlerWithResult<ListBucketResp
|
||||||
currentLastModified = dateParser.iso8601DateParse(currentText.toString().trim());
|
currentLastModified = dateParser.iso8601DateParse(currentText.toString().trim());
|
||||||
} else if (qName.equals("ETag")) {
|
} else if (qName.equals("ETag")) {
|
||||||
currentETag = currentText.toString().trim();
|
currentETag = currentText.toString().trim();
|
||||||
currentMD5 = encryptionService.fromHex(Utils.replaceAll(currentETag, '"', ""));
|
currentMD5 = CryptoStreams.hex(Utils.replaceAll(currentETag, '"', ""));
|
||||||
} else if (qName.equals("Size")) {
|
} else if (qName.equals("Size")) {
|
||||||
currentSize = new Long(currentText.toString().trim());
|
currentSize = new Long(currentText.toString().trim());
|
||||||
} else if (qName.equals("Owner")) {
|
} else if (qName.equals("Owner")) {
|
||||||
} else if (qName.equals("StorageClass")) {
|
} else if (qName.equals("StorageClass")) {
|
||||||
currentStorageClass = ObjectMetadata.StorageClass.valueOf(currentText.toString().trim());
|
currentStorageClass = ObjectMetadata.StorageClass.valueOf(currentText.toString().trim());
|
||||||
} else if (qName.equals("Contents")) {
|
} else if (qName.equals("Contents")) {
|
||||||
contents.add(new BucketListObjectMetadata(currentKey, currentLastModified, currentETag,
|
contents.add(new BucketListObjectMetadata(currentKey, currentLastModified, currentETag, currentMD5,
|
||||||
currentMD5, currentSize, currentOwner, currentStorageClass));
|
currentSize, currentOwner, currentStorageClass));
|
||||||
} else if (qName.equals("Name")) {
|
} else if (qName.equals("Name")) {
|
||||||
this.bucketName = currentText.toString().trim();
|
this.bucketName = currentText.toString().trim();
|
||||||
} else if (qName.equals("Prefix")) {
|
} else if (qName.equals("Prefix")) {
|
||||||
|
|
|
@ -18,9 +18,7 @@
|
||||||
*/
|
*/
|
||||||
package org.jclouds.aws.sqs.xml;
|
package org.jclouds.aws.sqs.xml;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import org.jclouds.crypto.CryptoStreams;
|
||||||
|
|
||||||
import org.jclouds.encryption.EncryptionService;
|
|
||||||
import org.jclouds.http.functions.ParseSax;
|
import org.jclouds.http.functions.ParseSax;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -34,13 +32,6 @@ public class MD5Handler extends ParseSax.HandlerWithResult<byte[]> {
|
||||||
private StringBuilder currentText = new StringBuilder();
|
private StringBuilder currentText = new StringBuilder();
|
||||||
byte[] md5;
|
byte[] md5;
|
||||||
|
|
||||||
private final EncryptionService encryptionService;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
MD5Handler(EncryptionService encryptionService) {
|
|
||||||
this.encryptionService = encryptionService;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getResult() {
|
public byte[] getResult() {
|
||||||
return md5;
|
return md5;
|
||||||
}
|
}
|
||||||
|
@ -48,7 +39,7 @@ public class MD5Handler extends ParseSax.HandlerWithResult<byte[]> {
|
||||||
public void endElement(String uri, String name, String qName) {
|
public void endElement(String uri, String name, String qName) {
|
||||||
if (qName.equals("MD5OfMessageBody")) {
|
if (qName.equals("MD5OfMessageBody")) {
|
||||||
String md5Hex = currentText.toString().trim();
|
String md5Hex = currentText.toString().trim();
|
||||||
this.md5 = encryptionService.fromHex(md5Hex);
|
this.md5 = CryptoStreams.hex(md5Hex);
|
||||||
}
|
}
|
||||||
currentText = new StringBuilder();
|
currentText = new StringBuilder();
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ import java.util.regex.Pattern;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import org.jclouds.encryption.EncryptionService;
|
import org.jclouds.crypto.CryptoStreams;
|
||||||
import org.jclouds.http.HttpResponse;
|
import org.jclouds.http.HttpResponse;
|
||||||
import org.jclouds.http.functions.ReturnStringIf2xx;
|
import org.jclouds.http.functions.ReturnStringIf2xx;
|
||||||
|
|
||||||
|
@ -40,11 +40,9 @@ import com.google.inject.Singleton;
|
||||||
public class RegexMD5Handler implements Function<HttpResponse, byte[]> {
|
public class RegexMD5Handler implements Function<HttpResponse, byte[]> {
|
||||||
Pattern pattern = Pattern.compile("<MD5OfMessageBody>([\\S&&[^<]]+)</MD5OfMessageBody>");
|
Pattern pattern = Pattern.compile("<MD5OfMessageBody>([\\S&&[^<]]+)</MD5OfMessageBody>");
|
||||||
private final ReturnStringIf2xx returnStringIf200;
|
private final ReturnStringIf2xx returnStringIf200;
|
||||||
private final EncryptionService encryptionService;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
RegexMD5Handler(EncryptionService encryptionService, ReturnStringIf2xx returnStringIf200) {
|
RegexMD5Handler(ReturnStringIf2xx returnStringIf200) {
|
||||||
this.encryptionService = encryptionService;
|
|
||||||
this.returnStringIf200 = returnStringIf200;
|
this.returnStringIf200 = returnStringIf200;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,7 +53,7 @@ public class RegexMD5Handler implements Function<HttpResponse, byte[]> {
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
Matcher matcher = pattern.matcher(content);
|
Matcher matcher = pattern.matcher(content);
|
||||||
if (matcher.find()) {
|
if (matcher.find()) {
|
||||||
value = encryptionService.fromHex(matcher.group(1));
|
value = CryptoStreams.hex(matcher.group(1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
|
|
|
@ -37,7 +37,6 @@ import org.jclouds.aws.ec2.domain.Image;
|
||||||
import org.jclouds.aws.ec2.domain.RootDeviceType;
|
import org.jclouds.aws.ec2.domain.RootDeviceType;
|
||||||
import org.jclouds.aws.ec2.domain.Image.ImageType;
|
import org.jclouds.aws.ec2.domain.Image.ImageType;
|
||||||
import org.jclouds.logging.log4j.config.Log4JLoggingModule;
|
import org.jclouds.logging.log4j.config.Log4JLoggingModule;
|
||||||
import org.jclouds.rest.ResourceNotFoundException;
|
|
||||||
import org.jclouds.rest.RestContext;
|
import org.jclouds.rest.RestContext;
|
||||||
import org.jclouds.rest.RestContextFactory;
|
import org.jclouds.rest.RestContextFactory;
|
||||||
import org.testng.annotations.AfterTest;
|
import org.testng.annotations.AfterTest;
|
||||||
|
@ -78,9 +77,8 @@ public class AMIClientLiveTest {
|
||||||
client = context.getApi().getAMIServices();
|
client = context.getApi().getAMIServices();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expectedExceptions = ResourceNotFoundException.class)
|
|
||||||
public void testDescribeImageNotExists() {
|
public void testDescribeImageNotExists() {
|
||||||
client.describeImagesInRegion(null, imageIds("ami-cdf819a3"));
|
assertEquals(client.describeImagesInRegion(null, imageIds("ami-cdf819a3")).size(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expectedExceptions = AWSResponseException.class)
|
@Test(expectedExceptions = AWSResponseException.class)
|
||||||
|
|
|
@ -23,8 +23,6 @@ import static org.easymock.classextension.EasyMock.createMock;
|
||||||
import static org.easymock.classextension.EasyMock.replay;
|
import static org.easymock.classextension.EasyMock.replay;
|
||||||
import static org.testng.Assert.assertEquals;
|
import static org.testng.Assert.assertEquals;
|
||||||
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.security.cert.CertificateException;
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
@ -37,14 +35,12 @@ import org.jclouds.aws.s3.domain.internal.MutableObjectMetadataImpl;
|
||||||
import org.jclouds.blobstore.domain.MutableBlobMetadata;
|
import org.jclouds.blobstore.domain.MutableBlobMetadata;
|
||||||
import org.jclouds.blobstore.domain.internal.MutableBlobMetadataImpl;
|
import org.jclouds.blobstore.domain.internal.MutableBlobMetadataImpl;
|
||||||
import org.jclouds.blobstore.functions.ParseSystemAndUserMetadataFromHeaders;
|
import org.jclouds.blobstore.functions.ParseSystemAndUserMetadataFromHeaders;
|
||||||
import org.jclouds.encryption.EncryptionService;
|
import org.jclouds.crypto.CryptoStreams;
|
||||||
import org.jclouds.encryption.internal.JCEEncryptionService;
|
|
||||||
import org.jclouds.http.HttpResponse;
|
import org.jclouds.http.HttpResponse;
|
||||||
import org.jclouds.io.Payloads;
|
import org.jclouds.io.Payloads;
|
||||||
import org.testng.annotations.BeforeTest;
|
import org.testng.annotations.BeforeTest;
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
import com.google.common.base.Throwables;
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -53,17 +49,6 @@ import com.google.common.collect.ImmutableMap;
|
||||||
@Test(testName = "s3.ParseObjectMetadataFromHeadersTest")
|
@Test(testName = "s3.ParseObjectMetadataFromHeadersTest")
|
||||||
public class ParseObjectMetadataFromHeadersTest {
|
public class ParseObjectMetadataFromHeadersTest {
|
||||||
|
|
||||||
protected volatile static EncryptionService encryptionService;
|
|
||||||
static {
|
|
||||||
try {
|
|
||||||
encryptionService = new JCEEncryptionService();
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
Throwables.propagate(e);
|
|
||||||
} catch (CertificateException e) {
|
|
||||||
Throwables.propagate(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testNormal() throws Exception {
|
void testNormal() throws Exception {
|
||||||
HttpResponse http = new HttpResponse(400, "boa", Payloads.newStringPayload(""));
|
HttpResponse http = new HttpResponse(400, "boa", Payloads.newStringPayload(""));
|
||||||
|
@ -72,8 +57,8 @@ public class ParseObjectMetadataFromHeadersTest {
|
||||||
http.getHeaders().put(HttpHeaders.CACHE_CONTROL, "cacheControl");
|
http.getHeaders().put(HttpHeaders.CACHE_CONTROL, "cacheControl");
|
||||||
http.getHeaders().put("Content-Disposition", "contentDisposition");
|
http.getHeaders().put("Content-Disposition", "contentDisposition");
|
||||||
http.getHeaders().put(HttpHeaders.CONTENT_ENCODING, "encoding");
|
http.getHeaders().put(HttpHeaders.CONTENT_ENCODING, "encoding");
|
||||||
ParseObjectMetadataFromHeaders parser = new ParseObjectMetadataFromHeaders(blobParser(http, "\"abc\""),
|
ParseObjectMetadataFromHeaders parser = new ParseObjectMetadataFromHeaders(blobParser(http, "\"abcd\""),
|
||||||
blobToObjectMetadata, encryptionService, "x-amz-meta-");
|
blobToObjectMetadata, "x-amz-meta-");
|
||||||
MutableObjectMetadata response = parser.apply(http);
|
MutableObjectMetadata response = parser.apply(http);
|
||||||
assertEquals(response, expects);
|
assertEquals(response, expects);
|
||||||
}
|
}
|
||||||
|
@ -87,9 +72,9 @@ public class ParseObjectMetadataFromHeadersTest {
|
||||||
http.getHeaders().put(HttpHeaders.CACHE_CONTROL, "cacheControl");
|
http.getHeaders().put(HttpHeaders.CACHE_CONTROL, "cacheControl");
|
||||||
http.getHeaders().put("Content-Disposition", "contentDisposition");
|
http.getHeaders().put("Content-Disposition", "contentDisposition");
|
||||||
http.getHeaders().put(HttpHeaders.CONTENT_ENCODING, "encoding");
|
http.getHeaders().put(HttpHeaders.CONTENT_ENCODING, "encoding");
|
||||||
http.getHeaders().put("x-amz-meta-object-eTag", "\"abc\"");
|
http.getHeaders().put("x-amz-meta-object-eTag", "\"abcd\"");
|
||||||
ParseObjectMetadataFromHeaders parser = new ParseObjectMetadataFromHeaders(blobParser(http, null),
|
ParseObjectMetadataFromHeaders parser = new ParseObjectMetadataFromHeaders(blobParser(http, null),
|
||||||
blobToObjectMetadata, encryptionService, "x-amz-meta-");
|
blobToObjectMetadata, "x-amz-meta-");
|
||||||
MutableObjectMetadata response = parser.apply(http);
|
MutableObjectMetadata response = parser.apply(http);
|
||||||
assertEquals(response, expects);
|
assertEquals(response, expects);
|
||||||
}
|
}
|
||||||
|
@ -120,9 +105,9 @@ public class ParseObjectMetadataFromHeadersTest {
|
||||||
expects.setCacheControl("cacheControl");
|
expects.setCacheControl("cacheControl");
|
||||||
expects.setContentDisposition("contentDisposition");
|
expects.setContentDisposition("contentDisposition");
|
||||||
expects.setContentEncoding("encoding");
|
expects.setContentEncoding("encoding");
|
||||||
expects.setContentMD5(encryptionService.fromHex("abc"));
|
expects.setContentMD5(CryptoStreams.hex("abcd"));
|
||||||
expects.setContentType("type");
|
expects.setContentType("type");
|
||||||
expects.setETag("\"abc\"");
|
expects.setETag("\"abcd\"");
|
||||||
expects.setKey("key");
|
expects.setKey("key");
|
||||||
expects.setLastModified(now);
|
expects.setLastModified(now);
|
||||||
expects.setOwner(null);
|
expects.setOwner(null);
|
||||||
|
|
|
@ -21,7 +21,6 @@ package org.jclouds.aws.s3.internal;
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
import static com.google.common.util.concurrent.Futures.immediateFailedFuture;
|
import static com.google.common.util.concurrent.Futures.immediateFailedFuture;
|
||||||
import static com.google.common.util.concurrent.Futures.immediateFuture;
|
import static com.google.common.util.concurrent.Futures.immediateFuture;
|
||||||
import static org.jclouds.concurrent.ConcurrentUtils.compose;
|
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -67,6 +66,7 @@ import org.jclouds.blobstore.domain.BlobMetadata;
|
||||||
import org.jclouds.blobstore.domain.MutableBlobMetadata;
|
import org.jclouds.blobstore.domain.MutableBlobMetadata;
|
||||||
import org.jclouds.blobstore.functions.HttpGetOptionsListToGetOptions;
|
import org.jclouds.blobstore.functions.HttpGetOptionsListToGetOptions;
|
||||||
import org.jclouds.blobstore.options.ListContainerOptions;
|
import org.jclouds.blobstore.options.ListContainerOptions;
|
||||||
|
import org.jclouds.concurrent.Futures;
|
||||||
import org.jclouds.date.DateService;
|
import org.jclouds.date.DateService;
|
||||||
import org.jclouds.domain.Location;
|
import org.jclouds.domain.Location;
|
||||||
import org.jclouds.domain.LocationScope;
|
import org.jclouds.domain.LocationScope;
|
||||||
|
@ -144,7 +144,7 @@ public class StubS3AsyncClient implements S3AsyncClient {
|
||||||
|
|
||||||
public ListenableFuture<ListBucketResponse> listBucket(final String name, ListBucketOptions... optionsList) {
|
public ListenableFuture<ListBucketResponse> listBucket(final String name, ListBucketOptions... optionsList) {
|
||||||
ListContainerOptions options = bucket2ContainerListOptions.apply(optionsList);
|
ListContainerOptions options = bucket2ContainerListOptions.apply(optionsList);
|
||||||
return compose(blobStore.list(name, options), resource2BucketList, service);
|
return Futures.compose(blobStore.list(name, options), resource2BucketList, service);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ListenableFuture<ObjectMetadata> copyObject(final String sourceBucket, final String sourceObject,
|
public ListenableFuture<ObjectMetadata> copyObject(final String sourceBucket, final String sourceObject,
|
||||||
|
@ -272,11 +272,11 @@ public class StubS3AsyncClient implements S3AsyncClient {
|
||||||
|
|
||||||
public ListenableFuture<S3Object> getObject(final String bucketName, final String key, final GetOptions... options) {
|
public ListenableFuture<S3Object> getObject(final String bucketName, final String key, final GetOptions... options) {
|
||||||
org.jclouds.blobstore.options.GetOptions getOptions = httpGetOptionsConverter.apply(options);
|
org.jclouds.blobstore.options.GetOptions getOptions = httpGetOptionsConverter.apply(options);
|
||||||
return compose(blobStore.getBlob(bucketName, key, getOptions), blob2Object, service);
|
return Futures.compose(blobStore.getBlob(bucketName, key, getOptions), blob2Object, service);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ListenableFuture<ObjectMetadata> headObject(String bucketName, String key) {
|
public ListenableFuture<ObjectMetadata> headObject(String bucketName, String key) {
|
||||||
return compose(blobStore.blobMetadata(bucketName, key), new Function<BlobMetadata, ObjectMetadata>() {
|
return Futures.compose(blobStore.blobMetadata(bucketName, key), new Function<BlobMetadata, ObjectMetadata>() {
|
||||||
@Override
|
@Override
|
||||||
public ObjectMetadata apply(BlobMetadata from) {
|
public ObjectMetadata apply(BlobMetadata from) {
|
||||||
return blob2ObjectMetadata.apply(from);
|
return blob2ObjectMetadata.apply(from);
|
||||||
|
|
|
@ -21,8 +21,6 @@ package org.jclouds.aws.s3.xml;
|
||||||
import static org.testng.Assert.assertEquals;
|
import static org.testng.Assert.assertEquals;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.security.cert.CertificateException;
|
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
|
|
||||||
import org.jclouds.aws.s3.domain.CanonicalUser;
|
import org.jclouds.aws.s3.domain.CanonicalUser;
|
||||||
|
@ -31,9 +29,8 @@ import org.jclouds.aws.s3.domain.ObjectMetadata;
|
||||||
import org.jclouds.aws.s3.domain.ObjectMetadata.StorageClass;
|
import org.jclouds.aws.s3.domain.ObjectMetadata.StorageClass;
|
||||||
import org.jclouds.aws.s3.domain.internal.BucketListObjectMetadata;
|
import org.jclouds.aws.s3.domain.internal.BucketListObjectMetadata;
|
||||||
import org.jclouds.aws.s3.domain.internal.ListBucketResponseImpl;
|
import org.jclouds.aws.s3.domain.internal.ListBucketResponseImpl;
|
||||||
|
import org.jclouds.crypto.CryptoStreams;
|
||||||
import org.jclouds.date.DateService;
|
import org.jclouds.date.DateService;
|
||||||
import org.jclouds.encryption.EncryptionService;
|
|
||||||
import org.jclouds.encryption.internal.JCEEncryptionService;
|
|
||||||
import org.jclouds.http.HttpException;
|
import org.jclouds.http.HttpException;
|
||||||
import org.jclouds.http.functions.BaseHandlerTest;
|
import org.jclouds.http.functions.BaseHandlerTest;
|
||||||
import org.jclouds.http.functions.ParseSax;
|
import org.jclouds.http.functions.ParseSax;
|
||||||
|
@ -41,7 +38,6 @@ import org.jclouds.util.Utils;
|
||||||
import org.testng.annotations.BeforeTest;
|
import org.testng.annotations.BeforeTest;
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
import com.google.common.base.Throwables;
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
|
||||||
@Test(groups = "unit", testName = "s3.ListBucketHandlerTest")
|
@Test(groups = "unit", testName = "s3.ListBucketHandlerTest")
|
||||||
|
@ -50,17 +46,6 @@ public class ListBucketHandlerTest extends BaseHandlerTest {
|
||||||
public static final String listBucketWithSlashDelimiterAndCommonPrefixApps = "<ListBucketResult xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\"> <Delimiter>/</Delimiter> <CommonPrefixes><Prefix>apps/</Prefix></CommonPrefixes></ListBucketResult>";
|
public static final String listBucketWithSlashDelimiterAndCommonPrefixApps = "<ListBucketResult xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\"> <Delimiter>/</Delimiter> <CommonPrefixes><Prefix>apps/</Prefix></CommonPrefixes></ListBucketResult>";
|
||||||
private DateService dateService;
|
private DateService dateService;
|
||||||
|
|
||||||
protected volatile static EncryptionService encryptionService;
|
|
||||||
static {
|
|
||||||
try {
|
|
||||||
encryptionService = new JCEEncryptionService();
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
Throwables.propagate(e);
|
|
||||||
} catch (CertificateException e) {
|
|
||||||
Throwables.propagate(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@BeforeTest
|
@BeforeTest
|
||||||
@Override
|
@Override
|
||||||
protected void setUpInjector() {
|
protected void setUpInjector() {
|
||||||
|
@ -74,47 +59,50 @@ public class ListBucketHandlerTest extends BaseHandlerTest {
|
||||||
CanonicalUser owner = new CanonicalUser("e1a5f66a480ca99a4fdfe8e318c3020446c9989d7004e7778029fbcc5d990fa0",
|
CanonicalUser owner = new CanonicalUser("e1a5f66a480ca99a4fdfe8e318c3020446c9989d7004e7778029fbcc5d990fa0",
|
||||||
"ferncam");
|
"ferncam");
|
||||||
ListBucketResponse expected = new ListBucketResponseImpl("adriancole.org.jclouds.aws.s3.amazons3testdelimiter",
|
ListBucketResponse expected = new ListBucketResponseImpl("adriancole.org.jclouds.aws.s3.amazons3testdelimiter",
|
||||||
ImmutableList.of(
|
ImmutableList
|
||||||
(ObjectMetadata) new BucketListObjectMetadata("apps/0", dateService
|
.of(
|
||||||
.iso8601DateParse("2009-05-07T18:27:08.000Z"), "\"c82e6a0025c31c5de5947fda62ac51ab\"",
|
(ObjectMetadata) new BucketListObjectMetadata("apps/0", dateService
|
||||||
encryptionService.fromHex("c82e6a0025c31c5de5947fda62ac51ab"), 8, owner,
|
.iso8601DateParse("2009-05-07T18:27:08.000Z"),
|
||||||
StorageClass.STANDARD),
|
"\"c82e6a0025c31c5de5947fda62ac51ab\"", CryptoStreams
|
||||||
(ObjectMetadata) new BucketListObjectMetadata("apps/1", dateService
|
.hex("c82e6a0025c31c5de5947fda62ac51ab"), 8, owner,
|
||||||
.iso8601DateParse("2009-05-07T18:27:09.000Z"), "\"944fab2c5a9a6bacf07db5e688310d7a\"",
|
StorageClass.STANDARD), (ObjectMetadata) new BucketListObjectMetadata(
|
||||||
encryptionService.fromHex("944fab2c5a9a6bacf07db5e688310d7a"), 8, owner,
|
"apps/1", dateService.iso8601DateParse("2009-05-07T18:27:09.000Z"),
|
||||||
StorageClass.STANDARD),
|
"\"944fab2c5a9a6bacf07db5e688310d7a\"", CryptoStreams
|
||||||
(ObjectMetadata) new BucketListObjectMetadata("apps/2", dateService
|
.hex("944fab2c5a9a6bacf07db5e688310d7a"), 8, owner,
|
||||||
.iso8601DateParse("2009-05-07T18:27:09.000Z"), "\"a227b8888045c8fd159fb495214000f0\"",
|
StorageClass.STANDARD), (ObjectMetadata) new BucketListObjectMetadata(
|
||||||
encryptionService.fromHex("a227b8888045c8fd159fb495214000f0"), 8, owner,
|
"apps/2", dateService.iso8601DateParse("2009-05-07T18:27:09.000Z"),
|
||||||
StorageClass.STANDARD),
|
"\"a227b8888045c8fd159fb495214000f0\"", CryptoStreams
|
||||||
(ObjectMetadata) new BucketListObjectMetadata("apps/3", dateService
|
.hex("a227b8888045c8fd159fb495214000f0"), 8, owner,
|
||||||
.iso8601DateParse("2009-05-07T18:27:09.000Z"), "\"c9caa76c3dec53e2a192608ce73eef03\"",
|
StorageClass.STANDARD), (ObjectMetadata) new BucketListObjectMetadata(
|
||||||
encryptionService.fromHex("c9caa76c3dec53e2a192608ce73eef03"), 8, owner,
|
"apps/3", dateService.iso8601DateParse("2009-05-07T18:27:09.000Z"),
|
||||||
StorageClass.STANDARD),
|
"\"c9caa76c3dec53e2a192608ce73eef03\"", CryptoStreams
|
||||||
(ObjectMetadata) new BucketListObjectMetadata("apps/4", dateService
|
.hex("c9caa76c3dec53e2a192608ce73eef03"), 8, owner,
|
||||||
.iso8601DateParse("2009-05-07T18:27:09.000Z"), "\"1ce5d0dcc6154a647ea90c7bdf82a224\"",
|
StorageClass.STANDARD), (ObjectMetadata) new BucketListObjectMetadata(
|
||||||
encryptionService.fromHex("1ce5d0dcc6154a647ea90c7bdf82a224"), 8, owner,
|
"apps/4", dateService.iso8601DateParse("2009-05-07T18:27:09.000Z"),
|
||||||
StorageClass.STANDARD),
|
"\"1ce5d0dcc6154a647ea90c7bdf82a224\"", CryptoStreams
|
||||||
(ObjectMetadata) new BucketListObjectMetadata("apps/5", dateService
|
.hex("1ce5d0dcc6154a647ea90c7bdf82a224"), 8, owner,
|
||||||
.iso8601DateParse("2009-05-07T18:27:09.000Z"), "\"79433524d87462ee05708a8ef894ed55\"",
|
StorageClass.STANDARD), (ObjectMetadata) new BucketListObjectMetadata(
|
||||||
encryptionService.fromHex("79433524d87462ee05708a8ef894ed55"), 8, owner,
|
"apps/5", dateService.iso8601DateParse("2009-05-07T18:27:09.000Z"),
|
||||||
StorageClass.STANDARD),
|
"\"79433524d87462ee05708a8ef894ed55\"", CryptoStreams
|
||||||
(ObjectMetadata) new BucketListObjectMetadata("apps/6", dateService
|
.hex("79433524d87462ee05708a8ef894ed55"), 8, owner,
|
||||||
.iso8601DateParse("2009-05-07T18:27:10.000Z"), "\"dd00a060b28ddca8bc5a21a49e306f67\"",
|
StorageClass.STANDARD), (ObjectMetadata) new BucketListObjectMetadata(
|
||||||
encryptionService.fromHex("dd00a060b28ddca8bc5a21a49e306f67"), 8, owner,
|
"apps/6", dateService.iso8601DateParse("2009-05-07T18:27:10.000Z"),
|
||||||
StorageClass.STANDARD),
|
"\"dd00a060b28ddca8bc5a21a49e306f67\"", CryptoStreams
|
||||||
(ObjectMetadata) new BucketListObjectMetadata("apps/7", dateService
|
.hex("dd00a060b28ddca8bc5a21a49e306f67"), 8, owner,
|
||||||
.iso8601DateParse("2009-05-07T18:27:10.000Z"), "\"8cd06eca6e819a927b07a285d750b100\"",
|
StorageClass.STANDARD), (ObjectMetadata) new BucketListObjectMetadata(
|
||||||
encryptionService.fromHex("8cd06eca6e819a927b07a285d750b100"), 8, owner,
|
"apps/7", dateService.iso8601DateParse("2009-05-07T18:27:10.000Z"),
|
||||||
StorageClass.STANDARD),
|
"\"8cd06eca6e819a927b07a285d750b100\"", CryptoStreams
|
||||||
(ObjectMetadata) new BucketListObjectMetadata("apps/8", dateService
|
.hex("8cd06eca6e819a927b07a285d750b100"), 8, owner,
|
||||||
.iso8601DateParse("2009-05-07T18:27:10.000Z"), "\"174495094d0633b92cbe46603eee6bad\"",
|
StorageClass.STANDARD), (ObjectMetadata) new BucketListObjectMetadata(
|
||||||
encryptionService.fromHex("174495094d0633b92cbe46603eee6bad"), 8, owner,
|
"apps/8", dateService.iso8601DateParse("2009-05-07T18:27:10.000Z"),
|
||||||
StorageClass.STANDARD),
|
"\"174495094d0633b92cbe46603eee6bad\"", CryptoStreams
|
||||||
(ObjectMetadata) new BucketListObjectMetadata("apps/9", dateService
|
.hex("174495094d0633b92cbe46603eee6bad"), 8, owner,
|
||||||
.iso8601DateParse("2009-05-07T18:27:10.000Z"), "\"cd8a19b26fea8a827276df0ad11c580d\"",
|
StorageClass.STANDARD), (ObjectMetadata) new BucketListObjectMetadata(
|
||||||
encryptionService.fromHex("cd8a19b26fea8a827276df0ad11c580d"), 8, owner,
|
"apps/9", dateService.iso8601DateParse("2009-05-07T18:27:10.000Z"),
|
||||||
StorageClass.STANDARD)), "apps/", null, null, 1000, null, false, new TreeSet<String>());
|
"\"cd8a19b26fea8a827276df0ad11c580d\"", CryptoStreams
|
||||||
|
.hex("cd8a19b26fea8a827276df0ad11c580d"), 8, owner,
|
||||||
|
StorageClass.STANDARD)), "apps/", null, null, 1000, null, false,
|
||||||
|
new TreeSet<String>());
|
||||||
|
|
||||||
ListBucketResponse result = (ListBucketResponse) factory.create(injector.getInstance(ListBucketHandler.class))
|
ListBucketResponse result = (ListBucketResponse) factory.create(injector.getInstance(ListBucketHandler.class))
|
||||||
.parse(is);
|
.parse(is);
|
||||||
|
|
|
@ -30,10 +30,10 @@ import java.util.SortedSet;
|
||||||
import org.jclouds.aws.AWSResponseException;
|
import org.jclouds.aws.AWSResponseException;
|
||||||
import org.jclouds.aws.domain.Region;
|
import org.jclouds.aws.domain.Region;
|
||||||
import org.jclouds.aws.sqs.domain.Queue;
|
import org.jclouds.aws.sqs.domain.Queue;
|
||||||
|
import org.jclouds.crypto.CryptoStreams;
|
||||||
import org.jclouds.logging.log4j.config.Log4JLoggingModule;
|
import org.jclouds.logging.log4j.config.Log4JLoggingModule;
|
||||||
import org.jclouds.rest.RestContext;
|
import org.jclouds.rest.RestContext;
|
||||||
import org.jclouds.rest.RestContextFactory;
|
import org.jclouds.rest.RestContextFactory;
|
||||||
import org.jclouds.util.Utils;
|
|
||||||
import org.testng.annotations.AfterTest;
|
import org.testng.annotations.AfterTest;
|
||||||
import org.testng.annotations.BeforeGroups;
|
import org.testng.annotations.BeforeGroups;
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
|
@ -63,14 +63,14 @@ public class SQSClientLiveTest {
|
||||||
String credential = checkNotNull(System.getProperty("jclouds.test.credential"), "jclouds.test.credential");
|
String credential = checkNotNull(System.getProperty("jclouds.test.credential"), "jclouds.test.credential");
|
||||||
|
|
||||||
context = new RestContextFactory().createContext("sqs", identity, credential, ImmutableSet
|
context = new RestContextFactory().createContext("sqs", identity, credential, ImmutableSet
|
||||||
.<Module> of(new Log4JLoggingModule()));
|
.<Module> of(new Log4JLoggingModule()));
|
||||||
this.client = context.getApi();
|
this.client = context.getApi();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testListQueuesInRegion() throws InterruptedException {
|
void testListQueuesInRegion() throws InterruptedException {
|
||||||
for (String region : Lists.newArrayList(null, Region.EU_WEST_1, Region.US_EAST_1, Region.US_WEST_1,
|
for (String region : Lists.newArrayList(null, Region.EU_WEST_1, Region.US_EAST_1, Region.US_WEST_1,
|
||||||
Region.AP_SOUTHEAST_1)) {
|
Region.AP_SOUTHEAST_1)) {
|
||||||
SortedSet<Queue> allResults = Sets.newTreeSet(client.listQueuesInRegion(region));
|
SortedSet<Queue> allResults = Sets.newTreeSet(client.listQueuesInRegion(region));
|
||||||
assertNotNull(allResults);
|
assertNotNull(allResults);
|
||||||
if (allResults.size() >= 1) {
|
if (allResults.size() >= 1) {
|
||||||
|
@ -87,7 +87,7 @@ public class SQSClientLiveTest {
|
||||||
String queueName = PREFIX + "1";
|
String queueName = PREFIX + "1";
|
||||||
|
|
||||||
for (final String region : Lists.newArrayList(null, Region.EU_WEST_1, Region.US_EAST_1, Region.US_WEST_1,
|
for (final String region : Lists.newArrayList(null, Region.EU_WEST_1, Region.US_EAST_1, Region.US_WEST_1,
|
||||||
Region.AP_SOUTHEAST_1)) {
|
Region.AP_SOUTHEAST_1)) {
|
||||||
try {
|
try {
|
||||||
SortedSet<Queue> result = Sets.newTreeSet(client.listQueuesInRegion(region, queuePrefix(queueName)));
|
SortedSet<Queue> result = Sets.newTreeSet(client.listQueuesInRegion(region, queuePrefix(queueName)));
|
||||||
if (result.size() >= 1) {
|
if (result.size() >= 1) {
|
||||||
|
@ -121,9 +121,9 @@ public class SQSClientLiveTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(dependsOnMethods = "testCreateQueue")
|
@Test(dependsOnMethods = "testCreateQueue")
|
||||||
void testSendMessage() throws InterruptedException {
|
void testSendMessage() throws InterruptedException, IOException {
|
||||||
String message = "hardyharhar";
|
String message = "hardyharhar";
|
||||||
byte[] md5 = context.utils().encryption().md5(Utils.toInputStream(message));
|
byte[] md5 = CryptoStreams.md5(message.getBytes());
|
||||||
for (Queue queue : queues) {
|
for (Queue queue : queues) {
|
||||||
assertEquals(client.sendMessage(queue, message), md5);
|
assertEquals(client.sendMessage(queue, message), md5);
|
||||||
}
|
}
|
||||||
|
@ -144,9 +144,8 @@ public class SQSClientLiveTest {
|
||||||
private static final int INCONSISTENCY_WINDOW = 10000;
|
private static final int INCONSISTENCY_WINDOW = 10000;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Due to eventual consistency, container commands may not return correctly
|
* Due to eventual consistency, container commands may not return correctly immediately. Hence,
|
||||||
* immediately. Hence, we will try up to the inconsistency window to see if
|
* we will try up to the inconsistency window to see if the assertion completes.
|
||||||
* the assertion completes.
|
|
||||||
*/
|
*/
|
||||||
protected static void assertEventually(Runnable assertion) throws InterruptedException {
|
protected static void assertEventually(Runnable assertion) throws InterruptedException {
|
||||||
long start = System.currentTimeMillis();
|
long start = System.currentTimeMillis();
|
||||||
|
@ -156,7 +155,7 @@ public class SQSClientLiveTest {
|
||||||
assertion.run();
|
assertion.run();
|
||||||
if (i > 0)
|
if (i > 0)
|
||||||
System.err.printf("%d attempts and %dms asserting %s%n", i + 1, System.currentTimeMillis() - start,
|
System.err.printf("%d attempts and %dms asserting %s%n", i + 1, System.currentTimeMillis() - start,
|
||||||
assertion.getClass().getSimpleName());
|
assertion.getClass().getSimpleName());
|
||||||
return;
|
return;
|
||||||
} catch (AssertionError e) {
|
} catch (AssertionError e) {
|
||||||
error = e;
|
error = e;
|
||||||
|
|
|
@ -18,16 +18,17 @@
|
||||||
*/
|
*/
|
||||||
package org.jclouds.aws.sqs;
|
package org.jclouds.aws.sqs;
|
||||||
|
|
||||||
import static org.jclouds.concurrent.ConcurrentUtils.sameThreadExecutor;
|
|
||||||
import static org.jclouds.aws.sqs.options.ListQueuesOptions.Builder.queuePrefix;
|
import static org.jclouds.aws.sqs.options.ListQueuesOptions.Builder.queuePrefix;
|
||||||
import static org.jclouds.concurrent.ConcurrentUtils.awaitCompletion;
|
import static org.jclouds.concurrent.FutureIterables.awaitCompletion;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.SortedSet;
|
import java.util.SortedSet;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
|
||||||
import org.jclouds.aws.domain.Region;
|
import org.jclouds.aws.domain.Region;
|
||||||
import org.jclouds.aws.sqs.domain.Queue;
|
import org.jclouds.aws.sqs.domain.Queue;
|
||||||
|
import org.jclouds.concurrent.MoreExecutors;
|
||||||
import org.jclouds.enterprise.config.EnterpriseConfigurationModule;
|
import org.jclouds.enterprise.config.EnterpriseConfigurationModule;
|
||||||
import org.jclouds.logging.ConsoleLogger;
|
import org.jclouds.logging.ConsoleLogger;
|
||||||
import org.jclouds.logging.Logger;
|
import org.jclouds.logging.Logger;
|
||||||
|
@ -38,7 +39,6 @@ import org.jclouds.rest.RestContextFactory;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
import java.util.concurrent.Future;
|
|
||||||
import com.google.inject.Module;
|
import com.google.inject.Module;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -50,8 +50,8 @@ import com.google.inject.Module;
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
*/
|
*/
|
||||||
public class SpeedTest {
|
public class SpeedTest {
|
||||||
private static final ImmutableSet<String> REGIONS = ImmutableSet.of(Region.EU_WEST_1,
|
private static final ImmutableSet<String> REGIONS = ImmutableSet.of(Region.EU_WEST_1, Region.US_EAST_1,
|
||||||
Region.US_EAST_1, Region.US_WEST_1, Region.AP_SOUTHEAST_1);
|
Region.US_WEST_1, Region.AP_SOUTHEAST_1);
|
||||||
public static final int PARAMETERS = 4;
|
public static final int PARAMETERS = 4;
|
||||||
public static final String INVALID_SYNTAX = "Invalid number of parameters. Syntax is: \"accesskeyid\" \"secretkey\" \"queueName\" \"messageCount\" ";
|
public static final String INVALID_SYNTAX = "Invalid number of parameters. Syntax is: \"accesskeyid\" \"secretkey\" \"queueName\" \"messageCount\" ";
|
||||||
|
|
||||||
|
@ -84,11 +84,10 @@ public class SpeedTest {
|
||||||
int messageCount = Integer.parseInt(args[3]);
|
int messageCount = Integer.parseInt(args[3]);
|
||||||
|
|
||||||
Set<Module> modules = isEnterprise ? ImmutableSet.<Module> of(new NullLoggingModule(),
|
Set<Module> modules = isEnterprise ? ImmutableSet.<Module> of(new NullLoggingModule(),
|
||||||
new EnterpriseConfigurationModule()) : ImmutableSet
|
new EnterpriseConfigurationModule()) : ImmutableSet.<Module> of(new NullLoggingModule());
|
||||||
.<Module> of(new NullLoggingModule());
|
|
||||||
|
|
||||||
RestContext<SQSClient, SQSAsyncClient> context = new RestContextFactory().createContext(
|
RestContext<SQSClient, SQSAsyncClient> context = new RestContextFactory().createContext("sqs", accesskeyid,
|
||||||
"sqs", accesskeyid, secretkey, modules);
|
secretkey, modules);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Set<Queue> queues = Sets.newHashSet();
|
Set<Queue> queues = Sets.newHashSet();
|
||||||
|
@ -122,30 +121,25 @@ public class SpeedTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void runTests(int messageCount, String contextName,
|
private static void runTests(int messageCount, String contextName, RestContext<SQSClient, SQSAsyncClient> context,
|
||||||
RestContext<SQSClient, SQSAsyncClient> context, Set<Queue> queues)
|
Set<Queue> queues) throws InterruptedException {
|
||||||
throws InterruptedException {
|
|
||||||
String message = "1";
|
String message = "1";
|
||||||
long timeOut = messageCount * 200; // minimum rate should be at least 5/second
|
long timeOut = messageCount * 200; // minimum rate should be at least 5/second
|
||||||
|
|
||||||
for (Queue queue : queues) {
|
for (Queue queue : queues) {
|
||||||
logger.info("context: %s, region: %s, queueName: %s", contextName, queue.getRegion(),
|
logger.info("context: %s, region: %s, queueName: %s", contextName, queue.getRegion(), queue.getName());
|
||||||
queue.getName());
|
|
||||||
|
|
||||||
// fire off all the messages for the test
|
// fire off all the messages for the test
|
||||||
Map<QueueMessage, Future<byte[]>> responses = Maps.newHashMap();
|
Map<QueueMessage, Future<byte[]>> responses = Maps.newHashMap();
|
||||||
for (int i = 0; i < messageCount; i++) {
|
for (int i = 0; i < messageCount; i++) {
|
||||||
responses.put(new QueueMessage(queue, message), context.getAsyncApi().sendMessage(
|
responses.put(new QueueMessage(queue, message), context.getAsyncApi().sendMessage(queue, message));
|
||||||
queue, message));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<QueueMessage, Exception> exceptions = awaitCompletion(responses, sameThreadExecutor(),
|
Map<QueueMessage, Exception> exceptions = awaitCompletion(responses, MoreExecutors.sameThreadExecutor(),
|
||||||
timeOut, traceLogger, String.format("context: %s, region: %s", contextName, queue
|
timeOut, traceLogger, String.format("context: %s, region: %s", contextName, queue.getRegion()));
|
||||||
.getRegion()));
|
|
||||||
|
|
||||||
if (exceptions.size() > 0)
|
if (exceptions.size() > 0)
|
||||||
logger.error("problems in context: %s, region: %s: %s", contextName, queue.getRegion(),
|
logger.error("problems in context: %s, region: %s: %s", contextName, queue.getRegion(), exceptions);
|
||||||
exceptions);
|
|
||||||
|
|
||||||
System.gc();
|
System.gc();
|
||||||
logger.info("pausing 5 seconds before the next run");
|
logger.info("pausing 5 seconds before the next run");
|
||||||
|
@ -153,21 +147,20 @@ public class SpeedTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void createQueues(String queueName,
|
private static void createQueues(String queueName, RestContext<SQSClient, SQSAsyncClient> nullLoggingDefaultContext,
|
||||||
RestContext<SQSClient, SQSAsyncClient> nullLoggingDefaultContext, Set<Queue> queues) {
|
Set<Queue> queues) {
|
||||||
for (String region : REGIONS) {
|
for (String region : REGIONS) {
|
||||||
logger.info("creating queue: %s in region %s", queueName, region);
|
logger.info("creating queue: %s in region %s", queueName, region);
|
||||||
queues.add(nullLoggingDefaultContext.getApi().createQueueInRegion(region, queueName));
|
queues.add(nullLoggingDefaultContext.getApi().createQueueInRegion(region, queueName));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean purgeQueues(String queueName,
|
private static boolean purgeQueues(String queueName, RestContext<SQSClient, SQSAsyncClient> nullLoggingDefaultContext) {
|
||||||
RestContext<SQSClient, SQSAsyncClient> nullLoggingDefaultContext) {
|
|
||||||
boolean deleted = false;
|
boolean deleted = false;
|
||||||
for (String region : REGIONS) {
|
for (String region : REGIONS) {
|
||||||
try {
|
try {
|
||||||
SortedSet<Queue> result = Sets.newTreeSet(nullLoggingDefaultContext.getApi()
|
SortedSet<Queue> result = Sets.newTreeSet(nullLoggingDefaultContext.getApi().listQueuesInRegion(region,
|
||||||
.listQueuesInRegion(region, queuePrefix(queueName)));
|
queuePrefix(queueName)));
|
||||||
if (result.size() >= 1) {
|
if (result.size() >= 1) {
|
||||||
nullLoggingDefaultContext.getApi().deleteQueue(result.last());
|
nullLoggingDefaultContext.getApi().deleteQueue(result.last());
|
||||||
logger.info("deleted queue: %s in region %s", queueName, region);
|
logger.info("deleted queue: %s in region %s", queueName, region);
|
||||||
|
|
|
@ -19,12 +19,10 @@
|
||||||
package org.jclouds.azure.storage.blob.blobstore;
|
package org.jclouds.azure.storage.blob.blobstore;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
import static org.jclouds.concurrent.ConcurrentUtils.compose;
|
|
||||||
import static org.jclouds.azure.storage.options.ListOptions.Builder.includeMetadata;
|
import static org.jclouds.azure.storage.options.ListOptions.Builder.includeMetadata;
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
|
@ -54,11 +52,13 @@ import org.jclouds.blobstore.functions.BlobToHttpGetOptions;
|
||||||
import org.jclouds.blobstore.internal.BaseAsyncBlobStore;
|
import org.jclouds.blobstore.internal.BaseAsyncBlobStore;
|
||||||
import org.jclouds.blobstore.options.ListContainerOptions;
|
import org.jclouds.blobstore.options.ListContainerOptions;
|
||||||
import org.jclouds.blobstore.util.BlobUtils;
|
import org.jclouds.blobstore.util.BlobUtils;
|
||||||
|
import org.jclouds.concurrent.Futures;
|
||||||
import org.jclouds.domain.Location;
|
import org.jclouds.domain.Location;
|
||||||
import org.jclouds.http.options.GetOptions;
|
import org.jclouds.http.options.GetOptions;
|
||||||
|
|
||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
|
@ -101,15 +101,16 @@ public class AzureAsyncBlobStore extends BaseAsyncBlobStore {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public ListenableFuture<org.jclouds.blobstore.domain.PageSet<? extends StorageMetadata>> list() {
|
public ListenableFuture<org.jclouds.blobstore.domain.PageSet<? extends StorageMetadata>> list() {
|
||||||
return compose(
|
return Futures
|
||||||
async.listContainers(includeMetadata()),
|
.compose(
|
||||||
new Function<BoundedSet<ContainerProperties>, org.jclouds.blobstore.domain.PageSet<? extends StorageMetadata>>() {
|
async.listContainers(includeMetadata()),
|
||||||
public org.jclouds.blobstore.domain.PageSet<? extends StorageMetadata> apply(
|
new Function<BoundedSet<ContainerProperties>, org.jclouds.blobstore.domain.PageSet<? extends StorageMetadata>>() {
|
||||||
BoundedSet<ContainerProperties> from) {
|
public org.jclouds.blobstore.domain.PageSet<? extends StorageMetadata> apply(
|
||||||
return new PageSetImpl<StorageMetadata>(Iterables.transform(from, container2ResourceMd), from
|
BoundedSet<ContainerProperties> from) {
|
||||||
.getNextMarker());
|
return new PageSetImpl<StorageMetadata>(Iterables.transform(from, container2ResourceMd),
|
||||||
}
|
from.getNextMarker());
|
||||||
}, service);
|
}
|
||||||
|
}, service);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -146,7 +147,7 @@ public class AzureAsyncBlobStore extends BaseAsyncBlobStore {
|
||||||
public ListenableFuture<PageSet<? extends StorageMetadata>> list(String container, ListContainerOptions options) {
|
public ListenableFuture<PageSet<? extends StorageMetadata>> list(String container, ListContainerOptions options) {
|
||||||
ListBlobsOptions azureOptions = blobStore2AzureContainerListOptions.apply(options);
|
ListBlobsOptions azureOptions = blobStore2AzureContainerListOptions.apply(options);
|
||||||
ListenableFuture<ListBlobsResponse> returnVal = async.listBlobs(container, azureOptions.includeMetadata());
|
ListenableFuture<ListBlobsResponse> returnVal = async.listBlobs(container, azureOptions.includeMetadata());
|
||||||
return compose(returnVal, azure2BlobStoreResourceList, service);
|
return Futures.compose(returnVal, azure2BlobStoreResourceList, service);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -172,7 +173,7 @@ public class AzureAsyncBlobStore extends BaseAsyncBlobStore {
|
||||||
public ListenableFuture<Blob> getBlob(String container, String key, org.jclouds.blobstore.options.GetOptions options) {
|
public ListenableFuture<Blob> getBlob(String container, String key, org.jclouds.blobstore.options.GetOptions options) {
|
||||||
GetOptions azureOptions = blob2ObjectGetOptions.apply(options);
|
GetOptions azureOptions = blob2ObjectGetOptions.apply(options);
|
||||||
ListenableFuture<AzureBlob> returnVal = async.getBlob(container, key, azureOptions);
|
ListenableFuture<AzureBlob> returnVal = async.getBlob(container, key, azureOptions);
|
||||||
return compose(returnVal, azureBlob2Blob, service);
|
return Futures.compose(returnVal, azureBlob2Blob, service);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -224,7 +225,7 @@ public class AzureAsyncBlobStore extends BaseAsyncBlobStore {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public ListenableFuture<BlobMetadata> blobMetadata(String container, String key) {
|
public ListenableFuture<BlobMetadata> blobMetadata(String container, String key) {
|
||||||
return compose(async.getBlobProperties(container, key), new Function<BlobProperties, BlobMetadata>() {
|
return Futures.compose(async.getBlobProperties(container, key), new Function<BlobProperties, BlobMetadata>() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BlobMetadata apply(BlobProperties from) {
|
public BlobMetadata apply(BlobProperties from) {
|
||||||
|
|
|
@ -20,7 +20,7 @@ package org.jclouds.azure.storage.blob.domain;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import org.jclouds.http.PayloadEnclosing;
|
import org.jclouds.io.PayloadEnclosing;
|
||||||
|
|
||||||
import com.google.common.collect.Multimap;
|
import com.google.common.collect.Multimap;
|
||||||
|
|
||||||
|
|
|
@ -24,9 +24,9 @@ import javax.inject.Inject;
|
||||||
|
|
||||||
import org.jclouds.azure.storage.blob.domain.AzureBlob;
|
import org.jclouds.azure.storage.blob.domain.AzureBlob;
|
||||||
import org.jclouds.azure.storage.blob.domain.MutableBlobProperties;
|
import org.jclouds.azure.storage.blob.domain.MutableBlobProperties;
|
||||||
import org.jclouds.http.PayloadEnclosing;
|
|
||||||
import org.jclouds.http.internal.PayloadEnclosingImpl;
|
import org.jclouds.http.internal.PayloadEnclosingImpl;
|
||||||
import org.jclouds.io.Payload;
|
import org.jclouds.io.Payload;
|
||||||
|
import org.jclouds.io.PayloadEnclosing;
|
||||||
import org.jclouds.io.payloads.DelegatingPayload;
|
import org.jclouds.io.payloads.DelegatingPayload;
|
||||||
|
|
||||||
import com.google.common.collect.LinkedHashMultimap;
|
import com.google.common.collect.LinkedHashMultimap;
|
||||||
|
|
|
@ -31,8 +31,8 @@ import org.jclouds.azure.storage.blob.domain.LeaseStatus;
|
||||||
import org.jclouds.azure.storage.blob.domain.ListBlobsResponse;
|
import org.jclouds.azure.storage.blob.domain.ListBlobsResponse;
|
||||||
import org.jclouds.azure.storage.blob.domain.internal.BlobPropertiesImpl;
|
import org.jclouds.azure.storage.blob.domain.internal.BlobPropertiesImpl;
|
||||||
import org.jclouds.azure.storage.blob.domain.internal.HashSetListBlobsResponse;
|
import org.jclouds.azure.storage.blob.domain.internal.HashSetListBlobsResponse;
|
||||||
|
import org.jclouds.crypto.CryptoStreams;
|
||||||
import org.jclouds.date.DateService;
|
import org.jclouds.date.DateService;
|
||||||
import org.jclouds.encryption.EncryptionService;
|
|
||||||
import org.jclouds.http.HttpUtils;
|
import org.jclouds.http.HttpUtils;
|
||||||
import org.jclouds.http.functions.ParseSax;
|
import org.jclouds.http.functions.ParseSax;
|
||||||
import org.xml.sax.Attributes;
|
import org.xml.sax.Attributes;
|
||||||
|
@ -49,8 +49,7 @@ import com.google.common.collect.Sets;
|
||||||
* @see <a href="http://msdn.microsoft.com/en-us/library/dd135734.aspx#samplerequestandresponse" />
|
* @see <a href="http://msdn.microsoft.com/en-us/library/dd135734.aspx#samplerequestandresponse" />
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
*/
|
*/
|
||||||
public class ContainerNameEnumerationResultsHandler extends
|
public class ContainerNameEnumerationResultsHandler extends ParseSax.HandlerWithResult<ListBlobsResponse> {
|
||||||
ParseSax.HandlerWithResult<ListBlobsResponse> {
|
|
||||||
private Set<BlobProperties> blobMetadata = Sets.newLinkedHashSet();
|
private Set<BlobProperties> blobMetadata = Sets.newLinkedHashSet();
|
||||||
private String prefix;
|
private String prefix;
|
||||||
private String marker;
|
private String marker;
|
||||||
|
@ -63,7 +62,6 @@ public class ContainerNameEnumerationResultsHandler extends
|
||||||
|
|
||||||
private StringBuilder currentText = new StringBuilder();
|
private StringBuilder currentText = new StringBuilder();
|
||||||
|
|
||||||
private final EncryptionService encryptionService;
|
|
||||||
private final DateService dateParser;
|
private final DateService dateParser;
|
||||||
private String delimiter;
|
private String delimiter;
|
||||||
private String currentName;
|
private String currentName;
|
||||||
|
@ -81,20 +79,17 @@ public class ContainerNameEnumerationResultsHandler extends
|
||||||
private LeaseStatus currentLeaseStatus;
|
private LeaseStatus currentLeaseStatus;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public ContainerNameEnumerationResultsHandler(EncryptionService encryptionService,
|
public ContainerNameEnumerationResultsHandler(DateService dateParser) {
|
||||||
DateService dateParser) {
|
|
||||||
this.encryptionService = encryptionService;
|
|
||||||
this.dateParser = dateParser;
|
this.dateParser = dateParser;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ListBlobsResponse getResult() {
|
public ListBlobsResponse getResult() {
|
||||||
return new HashSetListBlobsResponse(blobMetadata, containerUrl, prefix, marker, maxResults,
|
return new HashSetListBlobsResponse(blobMetadata, containerUrl, prefix, marker, maxResults, nextMarker,
|
||||||
nextMarker, delimiter, blobPrefixes);
|
delimiter, blobPrefixes);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void startElement(String uri, String localName, String qName, Attributes attributes)
|
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
|
||||||
throws SAXException {
|
|
||||||
if (qName.equals("Blob")) {
|
if (qName.equals("Blob")) {
|
||||||
inBlob = true;
|
inBlob = true;
|
||||||
inBlobPrefix = false;
|
inBlobPrefix = false;
|
||||||
|
@ -134,10 +129,9 @@ public class ContainerNameEnumerationResultsHandler extends
|
||||||
} else if (qName.equals("LeaseStatus")) {
|
} else if (qName.equals("LeaseStatus")) {
|
||||||
currentLeaseStatus = LeaseStatus.fromValue(currentText.toString().trim());
|
currentLeaseStatus = LeaseStatus.fromValue(currentText.toString().trim());
|
||||||
} else if (qName.equals("Blob")) {
|
} else if (qName.equals("Blob")) {
|
||||||
BlobProperties md = new BlobPropertiesImpl(currentBlobType, currentName, currentUrl,
|
BlobProperties md = new BlobPropertiesImpl(currentBlobType, currentName, currentUrl, currentLastModified,
|
||||||
currentLastModified, currentETag, currentSize, currentContentType,
|
currentETag, currentSize, currentContentType, currentContentMD5, currentContentEncoding,
|
||||||
currentContentMD5, currentContentEncoding, currentContentLanguage,
|
currentContentLanguage, currentLeaseStatus, currentMetadata);
|
||||||
currentLeaseStatus, currentMetadata);
|
|
||||||
blobMetadata.add(md);
|
blobMetadata.add(md);
|
||||||
currentBlobType = null;
|
currentBlobType = null;
|
||||||
currentName = null;
|
currentName = null;
|
||||||
|
@ -166,7 +160,7 @@ public class ContainerNameEnumerationResultsHandler extends
|
||||||
currentSize = Long.parseLong(currentText.toString().trim());
|
currentSize = Long.parseLong(currentText.toString().trim());
|
||||||
} else if (qName.equals("Content-MD5")) {
|
} else if (qName.equals("Content-MD5")) {
|
||||||
if (!currentText.toString().trim().equals(""))
|
if (!currentText.toString().trim().equals(""))
|
||||||
currentContentMD5 = encryptionService.fromBase64(currentText.toString().trim());
|
currentContentMD5 = CryptoStreams.base64(currentText.toString().trim());
|
||||||
} else if (qName.equals("Content-Type")) {
|
} else if (qName.equals("Content-Type")) {
|
||||||
currentContentType = currentText.toString().trim();
|
currentContentType = currentText.toString().trim();
|
||||||
} else if (qName.equals("Content-Encoding")) {
|
} else if (qName.equals("Content-Encoding")) {
|
||||||
|
|
|
@ -32,13 +32,15 @@ import javax.inject.Singleton;
|
||||||
import javax.ws.rs.core.HttpHeaders;
|
import javax.ws.rs.core.HttpHeaders;
|
||||||
|
|
||||||
import org.jclouds.Constants;
|
import org.jclouds.Constants;
|
||||||
|
import org.jclouds.crypto.Crypto;
|
||||||
|
import org.jclouds.crypto.CryptoStreams;
|
||||||
import org.jclouds.date.TimeStamp;
|
import org.jclouds.date.TimeStamp;
|
||||||
import org.jclouds.encryption.EncryptionService;
|
|
||||||
import org.jclouds.http.HttpException;
|
import org.jclouds.http.HttpException;
|
||||||
import org.jclouds.http.HttpRequest;
|
import org.jclouds.http.HttpRequest;
|
||||||
import org.jclouds.http.HttpRequestFilter;
|
import org.jclouds.http.HttpRequestFilter;
|
||||||
import org.jclouds.http.HttpUtils;
|
import org.jclouds.http.HttpUtils;
|
||||||
import org.jclouds.http.internal.SignatureWire;
|
import org.jclouds.http.internal.SignatureWire;
|
||||||
|
import org.jclouds.io.InputSuppliers;
|
||||||
import org.jclouds.logging.Logger;
|
import org.jclouds.logging.Logger;
|
||||||
import org.jclouds.util.Utils;
|
import org.jclouds.util.Utils;
|
||||||
|
|
||||||
|
@ -59,7 +61,7 @@ public class SharedKeyLiteAuthentication implements HttpRequestFilter {
|
||||||
private final String identity;
|
private final String identity;
|
||||||
private final byte[] key;
|
private final byte[] key;
|
||||||
private final Provider<String> timeStampProvider;
|
private final Provider<String> timeStampProvider;
|
||||||
private final EncryptionService encryptionService;
|
private final Crypto crypto;
|
||||||
private final HttpUtils utils;
|
private final HttpUtils utils;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
|
@ -67,16 +69,14 @@ public class SharedKeyLiteAuthentication implements HttpRequestFilter {
|
||||||
Logger signatureLog = Logger.NULL;
|
Logger signatureLog = Logger.NULL;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public SharedKeyLiteAuthentication(SignatureWire signatureWire,
|
public SharedKeyLiteAuthentication(SignatureWire signatureWire, @Named(Constants.PROPERTY_IDENTITY) String identity,
|
||||||
@Named(Constants.PROPERTY_IDENTITY) String identity,
|
@Named(Constants.PROPERTY_CREDENTIAL) String encodedKey, @TimeStamp Provider<String> timeStampProvider,
|
||||||
@Named(Constants.PROPERTY_CREDENTIAL) String encodedKey,
|
Crypto crypto, HttpUtils utils) {
|
||||||
@TimeStamp Provider<String> timeStampProvider, EncryptionService encryptionService,
|
this.crypto = crypto;
|
||||||
HttpUtils utils) {
|
|
||||||
this.encryptionService = encryptionService;
|
|
||||||
this.utils = utils;
|
this.utils = utils;
|
||||||
this.signatureWire = signatureWire;
|
this.signatureWire = signatureWire;
|
||||||
this.identity = identity;
|
this.identity = identity;
|
||||||
this.key = encryptionService.fromBase64(encodedKey);
|
this.key = CryptoStreams.base64(encodedKey);
|
||||||
this.timeStampProvider = timeStampProvider;
|
this.timeStampProvider = timeStampProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,16 +102,13 @@ public class SharedKeyLiteAuthentication implements HttpRequestFilter {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void appendPayloadMetadata(HttpRequest request, StringBuilder buffer) {
|
private void appendPayloadMetadata(HttpRequest request, StringBuilder buffer) {
|
||||||
buffer.append(
|
buffer.append(utils.valueOrEmpty(request.getPayload() == null ? null : request.getPayload().getContentMD5()))
|
||||||
utils.valueOrEmpty(request.getPayload() == null ? null : request.getPayload()
|
.append("\n");
|
||||||
.getContentMD5())).append("\n");
|
buffer.append(utils.valueOrEmpty(request.getPayload() == null ? null : request.getPayload().getContentType()))
|
||||||
buffer.append(
|
.append("\n");
|
||||||
utils.valueOrEmpty(request.getPayload() == null ? null : request.getPayload()
|
|
||||||
.getContentType())).append("\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void calculateAndReplaceAuthHeader(HttpRequest request, String toSign)
|
private void calculateAndReplaceAuthHeader(HttpRequest request, String toSign) throws HttpException {
|
||||||
throws HttpException {
|
|
||||||
String signature = signString(toSign);
|
String signature = signString(toSign);
|
||||||
if (signatureWire.enabled())
|
if (signatureWire.enabled())
|
||||||
signatureWire.input(Utils.toInputStream(signature));
|
signatureWire.input(Utils.toInputStream(signature));
|
||||||
|
@ -122,7 +119,7 @@ public class SharedKeyLiteAuthentication implements HttpRequestFilter {
|
||||||
public String signString(String toSign) {
|
public String signString(String toSign) {
|
||||||
String signature;
|
String signature;
|
||||||
try {
|
try {
|
||||||
signature = encryptionService.base64(encryptionService.hmacSha256(toSign, key));
|
signature = CryptoStreams.base64(CryptoStreams.mac(InputSuppliers.of(toSign), crypto.hmacSHA256(key)));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new HttpException("error signing request", e);
|
throw new HttpException("error signing request", e);
|
||||||
}
|
}
|
||||||
|
@ -134,8 +131,7 @@ public class SharedKeyLiteAuthentication implements HttpRequestFilter {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void replaceDateHeader(HttpRequest request) {
|
private void replaceDateHeader(HttpRequest request) {
|
||||||
request.getHeaders().replaceValues(HttpHeaders.DATE,
|
request.getHeaders().replaceValues(HttpHeaders.DATE, Collections.singletonList(timeStampProvider.get()));
|
||||||
Collections.singletonList(timeStampProvider.get()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void appendCanonicalizedHeaders(HttpRequest request, StringBuilder toSign) {
|
private void appendCanonicalizedHeaders(HttpRequest request, StringBuilder toSign) {
|
||||||
|
|
|
@ -42,8 +42,10 @@ import org.jclouds.azure.storage.options.ListOptions;
|
||||||
import org.jclouds.blobstore.BlobStoreContext;
|
import org.jclouds.blobstore.BlobStoreContext;
|
||||||
import org.jclouds.blobstore.BlobStoreContextFactory;
|
import org.jclouds.blobstore.BlobStoreContextFactory;
|
||||||
import org.jclouds.blobstore.ContainerNotFoundException;
|
import org.jclouds.blobstore.ContainerNotFoundException;
|
||||||
|
import org.jclouds.crypto.CryptoStreams;
|
||||||
import org.jclouds.http.HttpResponseException;
|
import org.jclouds.http.HttpResponseException;
|
||||||
import org.jclouds.http.options.GetOptions;
|
import org.jclouds.http.options.GetOptions;
|
||||||
|
import org.jclouds.io.Payloads;
|
||||||
import org.jclouds.logging.log4j.config.Log4JLoggingModule;
|
import org.jclouds.logging.log4j.config.Log4JLoggingModule;
|
||||||
import org.jclouds.util.Utils;
|
import org.jclouds.util.Utils;
|
||||||
import org.testng.annotations.BeforeTest;
|
import org.testng.annotations.BeforeTest;
|
||||||
|
@ -73,8 +75,8 @@ public class AzureBlobClientLiveTest {
|
||||||
public void setupClient() throws IOException {
|
public void setupClient() throws IOException {
|
||||||
identity = System.getProperty("jclouds.test.identity");
|
identity = System.getProperty("jclouds.test.identity");
|
||||||
String credential = System.getProperty("jclouds.test.credential");
|
String credential = System.getProperty("jclouds.test.credential");
|
||||||
context = new BlobStoreContextFactory().createContext("azureblob", identity, credential,
|
context = new BlobStoreContextFactory().createContext("azureblob", identity, credential, ImmutableSet
|
||||||
ImmutableSet.<Module> of(new Log4JLoggingModule()));
|
.<Module> of(new Log4JLoggingModule()));
|
||||||
client = (AzureBlobClient) context.getProviderSpecificContext().getApi();
|
client = (AzureBlobClient) context.getProviderSpecificContext().getApi();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,8 +99,7 @@ public class AzureBlobClientLiveTest {
|
||||||
while (!created) {
|
while (!created) {
|
||||||
privateContainer = containerPrefix + new SecureRandom().nextInt();
|
privateContainer = containerPrefix + new SecureRandom().nextInt();
|
||||||
try {
|
try {
|
||||||
created = client.createContainer(privateContainer, withMetadata(ImmutableMultimap.of(
|
created = client.createContainer(privateContainer, withMetadata(ImmutableMultimap.of("foo", "bar")));
|
||||||
"foo", "bar")));
|
|
||||||
} catch (UndeclaredThrowableException e) {
|
} catch (UndeclaredThrowableException e) {
|
||||||
HttpResponseException htpe = (HttpResponseException) e.getCause().getCause();
|
HttpResponseException htpe = (HttpResponseException) e.getCause().getCause();
|
||||||
if (htpe.getResponse().getStatusCode() == 409)
|
if (htpe.getResponse().getStatusCode() == 409)
|
||||||
|
@ -111,8 +112,8 @@ public class AzureBlobClientLiveTest {
|
||||||
long containerCount = response.size();
|
long containerCount = response.size();
|
||||||
assertTrue(containerCount >= 1);
|
assertTrue(containerCount >= 1);
|
||||||
ListBlobsResponse list = client.listBlobs(privateContainer);
|
ListBlobsResponse list = client.listBlobs(privateContainer);
|
||||||
assertEquals(list.getUrl(), URI.create(String.format("https://%s.blob.core.windows.net/%s",
|
assertEquals(list.getUrl(), URI.create(String.format("https://%s.blob.core.windows.net/%s", identity,
|
||||||
identity, privateContainer)));
|
privateContainer)));
|
||||||
// TODO .. check to see the container actually exists
|
// TODO .. check to see the container actually exists
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,15 +165,14 @@ public class AzureBlobClientLiveTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ListBlobsResponse list = client.listBlobs();
|
ListBlobsResponse list = client.listBlobs();
|
||||||
assertEquals(list.getUrl(), URI.create(String.format(
|
assertEquals(list.getUrl(), URI.create(String.format("https://%s.blob.core.windows.net/%%24root", identity)));
|
||||||
"https://%s.blob.core.windows.net/%%24root", identity)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testListContainersWithOptions() throws Exception {
|
public void testListContainersWithOptions() throws Exception {
|
||||||
|
|
||||||
BoundedSet<ContainerProperties> response = client.listContainers(ListOptions.Builder.prefix(
|
BoundedSet<ContainerProperties> response = client.listContainers(ListOptions.Builder.prefix(privateContainer)
|
||||||
privateContainer).maxResults(1).includeMetadata());
|
.maxResults(1).includeMetadata());
|
||||||
assert null != response;
|
assert null != response;
|
||||||
long initialContainerCount = response.size();
|
long initialContainerCount = response.size();
|
||||||
assertTrue(initialContainerCount >= 0);
|
assertTrue(initialContainerCount >= 0);
|
||||||
|
@ -186,8 +186,7 @@ public class AzureBlobClientLiveTest {
|
||||||
// TODO loop for up to 30 seconds checking if they are really gone
|
// TODO loop for up to 30 seconds checking if they are really gone
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(timeOut = 5 * 60 * 1000, dependsOnMethods = { "testCreateContainer",
|
@Test(timeOut = 5 * 60 * 1000, dependsOnMethods = { "testCreateContainer", "testCreatePublicContainer" })
|
||||||
"testCreatePublicContainer" })
|
|
||||||
public void testListOwnedContainers() throws Exception {
|
public void testListOwnedContainers() throws Exception {
|
||||||
|
|
||||||
// Test default listing
|
// Test default listing
|
||||||
|
@ -197,8 +196,7 @@ public class AzureBlobClientLiveTest {
|
||||||
|
|
||||||
// Test listing with options
|
// Test listing with options
|
||||||
response = client.listContainers(ListOptions.Builder.prefix(
|
response = client.listContainers(ListOptions.Builder.prefix(
|
||||||
privateContainer.substring(0, privateContainer.length() - 1)).maxResults(1)
|
privateContainer.substring(0, privateContainer.length() - 1)).maxResults(1).includeMetadata());
|
||||||
.includeMetadata());
|
|
||||||
assertEquals(response.size(), 1);
|
assertEquals(response.size(), 1);
|
||||||
assertEquals(Iterables.getOnlyElement(response).getName(), privateContainer);
|
assertEquals(Iterables.getOnlyElement(response).getName(), privateContainer);
|
||||||
assertEquals(Iterables.getOnlyElement(response).getMetadata(), ImmutableMap.of("foo", "bar"));
|
assertEquals(Iterables.getOnlyElement(response).getMetadata(), ImmutableMap.of("foo", "bar"));
|
||||||
|
@ -214,16 +212,14 @@ public class AzureBlobClientLiveTest {
|
||||||
client.deleteContainer("does-not-exist");
|
client.deleteContainer("does-not-exist");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(timeOut = 5 * 60 * 1000, dependsOnMethods = { "testListOwnedContainers",
|
@Test(timeOut = 5 * 60 * 1000, dependsOnMethods = { "testListOwnedContainers", "testObjectOperations" })
|
||||||
"testObjectOperations" })
|
|
||||||
public void testDeleteContainer() throws Exception {
|
public void testDeleteContainer() throws Exception {
|
||||||
client.deleteContainer(privateContainer);
|
client.deleteContainer(privateContainer);
|
||||||
client.deleteContainer(publicContainer);
|
client.deleteContainer(publicContainer);
|
||||||
// TODO loop for up to 30 seconds checking if they are really gone
|
// TODO loop for up to 30 seconds checking if they are really gone
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(timeOut = 5 * 60 * 1000, dependsOnMethods = { "testCreateContainer",
|
@Test(timeOut = 5 * 60 * 1000, dependsOnMethods = { "testCreateContainer", "testCreatePublicContainer" })
|
||||||
"testCreatePublicContainer" })
|
|
||||||
public void testObjectOperations() throws Exception {
|
public void testObjectOperations() throws Exception {
|
||||||
String data = "Here is my data";
|
String data = "Here is my data";
|
||||||
|
|
||||||
|
@ -231,20 +227,18 @@ public class AzureBlobClientLiveTest {
|
||||||
AzureBlob object = client.newBlob();
|
AzureBlob object = client.newBlob();
|
||||||
object.getProperties().setName("object");
|
object.getProperties().setName("object");
|
||||||
object.setPayload(data);
|
object.setPayload(data);
|
||||||
context.utils().encryption().generateMD5BufferingIfNotRepeatable(object);
|
Payloads.calculateMD5(object);
|
||||||
object.getProperties().setContentType("text/plain");
|
object.getProperties().setContentType("text/plain");
|
||||||
object.getProperties().getMetadata().put("mykey", "metadata-value");
|
object.getProperties().getMetadata().put("mykey", "metadata-value");
|
||||||
byte[] md5 = object.getProperties().getContentMD5();
|
byte[] md5 = object.getProperties().getContentMD5();
|
||||||
String newEtag = client.putBlob(privateContainer, object);
|
String newEtag = client.putBlob(privateContainer, object);
|
||||||
assertEquals(context.utils().encryption().hex(md5), context.utils().encryption().hex(object.getProperties()
|
assertEquals(CryptoStreams.hex(md5), CryptoStreams.hex(object.getProperties().getContentMD5()));
|
||||||
.getContentMD5()));
|
|
||||||
|
|
||||||
// Test HEAD of missing object
|
// Test HEAD of missing object
|
||||||
assert client.getBlobProperties(privateContainer, "non-existent-object") == null;
|
assert client.getBlobProperties(privateContainer, "non-existent-object") == null;
|
||||||
|
|
||||||
// Test HEAD of object
|
// Test HEAD of object
|
||||||
BlobProperties metadata = client.getBlobProperties(privateContainer, object.getProperties()
|
BlobProperties metadata = client.getBlobProperties(privateContainer, object.getProperties().getName());
|
||||||
.getName());
|
|
||||||
// TODO assertEquals(metadata.getName(), object.getProperties().getName());
|
// TODO assertEquals(metadata.getName(), object.getProperties().getName());
|
||||||
// we can't check this while hacking around lack of content-md5, as GET of the first byte will
|
// we can't check this while hacking around lack of content-md5, as GET of the first byte will
|
||||||
// show incorrect length 1, the returned size, as opposed to the real length. This is an ok
|
// show incorrect length 1, the returned size, as opposed to the real length. This is an ok
|
||||||
|
@ -254,8 +248,7 @@ public class AzureBlobClientLiveTest {
|
||||||
// assertEquals(metadata.getSize(), data.length());
|
// assertEquals(metadata.getSize(), data.length());
|
||||||
assertEquals(metadata.getContentType(), "text/plain");
|
assertEquals(metadata.getContentType(), "text/plain");
|
||||||
// Azure doesn't return the Content-MD5 on head request..
|
// Azure doesn't return the Content-MD5 on head request..
|
||||||
assertEquals(context.utils().encryption().hex(md5), context.utils().encryption().hex(object.getProperties()
|
assertEquals(CryptoStreams.hex(md5), CryptoStreams.hex(object.getProperties().getContentMD5()));
|
||||||
.getContentMD5()));
|
|
||||||
assertEquals(metadata.getETag(), newEtag);
|
assertEquals(metadata.getETag(), newEtag);
|
||||||
assertEquals(metadata.getMetadata().entrySet().size(), 1);
|
assertEquals(metadata.getMetadata().entrySet().size(), 1);
|
||||||
assertEquals(metadata.getMetadata().get("mykey"), "metadata-value");
|
assertEquals(metadata.getMetadata().get("mykey"), "metadata-value");
|
||||||
|
@ -276,8 +269,7 @@ public class AzureBlobClientLiveTest {
|
||||||
// TODO assertEquals(getBlob.getName(), object.getProperties().getName());
|
// TODO assertEquals(getBlob.getName(), object.getProperties().getName());
|
||||||
assertEquals(getBlob.getPayload().getContentLength(), new Long(data.length()));
|
assertEquals(getBlob.getPayload().getContentLength(), new Long(data.length()));
|
||||||
assertEquals(getBlob.getProperties().getContentType(), "text/plain");
|
assertEquals(getBlob.getProperties().getContentType(), "text/plain");
|
||||||
assertEquals(context.utils().encryption().hex(md5), context.utils().encryption().hex(getBlob.getProperties()
|
assertEquals(CryptoStreams.hex(md5), CryptoStreams.hex(getBlob.getProperties().getContentMD5()));
|
||||||
.getContentMD5()));
|
|
||||||
assertEquals(newEtag, getBlob.getProperties().getETag());
|
assertEquals(newEtag, getBlob.getProperties().getETag());
|
||||||
// wait until we can update metadata
|
// wait until we can update metadata
|
||||||
// assertEquals(getBlob.getProperties().getMetadata().entries().size(), 2);
|
// assertEquals(getBlob.getProperties().getMetadata().entries().size(), 2);
|
||||||
|
@ -291,15 +283,12 @@ public class AzureBlobClientLiveTest {
|
||||||
assertEquals(metadata.getMetadata().get("mykey"), "metadata-value");
|
assertEquals(metadata.getMetadata().get("mykey"), "metadata-value");
|
||||||
|
|
||||||
// test listing
|
// test listing
|
||||||
ListBlobsResponse response = client.listBlobs(privateContainer, ListBlobsOptions.Builder
|
ListBlobsResponse response = client.listBlobs(privateContainer, ListBlobsOptions.Builder.prefix(
|
||||||
.prefix(
|
object.getProperties().getName().substring(0, object.getProperties().getName().length() - 1))
|
||||||
object.getProperties().getName().substring(0,
|
.maxResults(1).includeMetadata());
|
||||||
object.getProperties().getName().length() - 1)).maxResults(1)
|
|
||||||
.includeMetadata());
|
|
||||||
assertEquals(response.size(), 1);
|
assertEquals(response.size(), 1);
|
||||||
assertEquals(Iterables.getOnlyElement(response).getName(), object.getProperties().getName());
|
assertEquals(Iterables.getOnlyElement(response).getName(), object.getProperties().getName());
|
||||||
assertEquals(Iterables.getOnlyElement(response).getMetadata(), ImmutableMap.of("mykey",
|
assertEquals(Iterables.getOnlyElement(response).getMetadata(), ImmutableMap.of("mykey", "metadata-value"));
|
||||||
"metadata-value"));
|
|
||||||
|
|
||||||
// Test PUT with invalid ETag (as if object's data was corrupted in transit)
|
// Test PUT with invalid ETag (as if object's data was corrupted in transit)
|
||||||
String correctEtag = newEtag;
|
String correctEtag = newEtag;
|
||||||
|
@ -318,8 +307,7 @@ public class AzureBlobClientLiveTest {
|
||||||
object.setPayload(bais);
|
object.setPayload(bais);
|
||||||
object.getPayload().setContentLength(new Long(data.getBytes().length));
|
object.getPayload().setContentLength(new Long(data.getBytes().length));
|
||||||
newEtag = client.putBlob(privateContainer, object);
|
newEtag = client.putBlob(privateContainer, object);
|
||||||
assertEquals(context.utils().encryption().hex(md5), context.utils().encryption().hex(getBlob.getProperties()
|
assertEquals(CryptoStreams.hex(md5), CryptoStreams.hex(getBlob.getProperties().getContentMD5()));
|
||||||
.getContentMD5()));
|
|
||||||
|
|
||||||
// Test GET with options
|
// Test GET with options
|
||||||
// Non-matching ETag
|
// Non-matching ETag
|
||||||
|
@ -333,8 +321,8 @@ public class AzureBlobClientLiveTest {
|
||||||
|
|
||||||
// Matching ETag TODO this shouldn't fail!!!
|
// Matching ETag TODO this shouldn't fail!!!
|
||||||
try {
|
try {
|
||||||
getBlob = client.getBlob(privateContainer, object.getProperties().getName(),
|
getBlob = client.getBlob(privateContainer, object.getProperties().getName(), GetOptions.Builder
|
||||||
GetOptions.Builder.ifETagMatches(newEtag));
|
.ifETagMatches(newEtag));
|
||||||
assertEquals(getBlob.getProperties().getETag(), newEtag);
|
assertEquals(getBlob.getProperties().getETag(), newEtag);
|
||||||
} catch (HttpResponseException e) {
|
} catch (HttpResponseException e) {
|
||||||
assertEquals(e.getResponse().getStatusCode(), 412);
|
assertEquals(e.getResponse().getStatusCode(), 412);
|
||||||
|
|
|
@ -43,7 +43,7 @@ See http://code.google.com/p/jclouds for details."
|
||||||
AsyncBlobStore BlobStore BlobStoreContext BlobStoreContextFactory
|
AsyncBlobStore BlobStore BlobStoreContext BlobStoreContextFactory
|
||||||
domain.BlobMetadata domain.StorageMetadata domain.Blob
|
domain.BlobMetadata domain.StorageMetadata domain.Blob
|
||||||
options.ListContainerOptions]
|
options.ListContainerOptions]
|
||||||
[org.jclouds.encryption.internal JCEEncryptionService]
|
[org.jclouds.io Payloads]
|
||||||
[java.util Arrays]
|
[java.util Arrays]
|
||||||
[java.security DigestOutputStream MessageDigest]
|
[java.security DigestOutputStream MessageDigest]
|
||||||
[com.google.common.collect ImmutableSet]))
|
[com.google.common.collect ImmutableSet]))
|
||||||
|
@ -135,12 +135,12 @@ Options can also be specified for extension modules
|
||||||
(.list blobstore container-name list-options))
|
(.list blobstore container-name list-options))
|
||||||
(apply list-container *blobstore* blobstore args)))
|
(apply list-container *blobstore* blobstore args)))
|
||||||
|
|
||||||
(defn- list-blobs-chunk [container prefix blobstore & [marker]]
|
(defn- list-blobs-chunk [container prefix #^BlobStore blobstore & [marker]]
|
||||||
(apply list-container blobstore container
|
(apply list-container blobstore container
|
||||||
:in-directory prefix (when (string? marker)
|
:in-directory prefix (when (string? marker)
|
||||||
[:after-marker marker])))
|
[:after-marker marker])))
|
||||||
|
|
||||||
(defn- list-blobs-chunks [container prefix blobstore marker]
|
(defn- list-blobs-chunks [container prefix #^BlobStore blobstore marker]
|
||||||
(when marker
|
(when marker
|
||||||
(let [chunk (list-blobs-chunk container prefix blobstore marker)]
|
(let [chunk (list-blobs-chunk container prefix blobstore marker)]
|
||||||
(lazy-seq (cons chunk
|
(lazy-seq (cons chunk
|
||||||
|
@ -149,7 +149,7 @@ Options can also be specified for extension modules
|
||||||
|
|
||||||
(defn list-blobs
|
(defn list-blobs
|
||||||
"Returns a lazy seq of all blobs in the given container."
|
"Returns a lazy seq of all blobs in the given container."
|
||||||
([container prefix blobstore]
|
([container prefix #^BlobStore blobstore]
|
||||||
(apply concat (list-blobs-chunks container prefix blobstore :start))))
|
(apply concat (list-blobs-chunks container prefix blobstore :start))))
|
||||||
|
|
||||||
(defn locations
|
(defn locations
|
||||||
|
@ -164,84 +164,84 @@ Options can also be specified for extension modules
|
||||||
(create-container container-name nil *blobstore*))
|
(create-container container-name nil *blobstore*))
|
||||||
([container-name location]
|
([container-name location]
|
||||||
(create-container container-name location *blobstore*))
|
(create-container container-name location *blobstore*))
|
||||||
([container-name location blobstore]
|
([container-name location #^BlobStore blobstore]
|
||||||
(.createContainerInLocation blobstore location container-name)))
|
(.createContainerInLocation blobstore location container-name)))
|
||||||
|
|
||||||
(defn clear-container
|
(defn clear-container
|
||||||
"Clear a container."
|
"Clear a container."
|
||||||
([container-name]
|
([container-name]
|
||||||
(clear-container container-name *blobstore*))
|
(clear-container container-name *blobstore*))
|
||||||
([container-name blobstore]
|
([container-name #^BlobStore blobstore]
|
||||||
(.clearContainer blobstore container-name)))
|
(.clearContainer blobstore container-name)))
|
||||||
|
|
||||||
(defn delete-container
|
(defn delete-container
|
||||||
"Delete a container."
|
"Delete a container."
|
||||||
([container-name]
|
([container-name]
|
||||||
(delete-container container-name *blobstore*))
|
(delete-container container-name *blobstore*))
|
||||||
([container-name blobstore]
|
([container-name #^BlobStore blobstore]
|
||||||
(.deleteContainer blobstore container-name)))
|
(.deleteContainer blobstore container-name)))
|
||||||
|
|
||||||
(defn container-exists?
|
(defn container-exists?
|
||||||
"Predicate to check presence of a container"
|
"Predicate to check presence of a container"
|
||||||
([container-name]
|
([container-name]
|
||||||
(container-exists? container-name *blobstore*))
|
(container-exists? container-name *blobstore*))
|
||||||
([container-name blobstore]
|
([container-name #^BlobStore blobstore]
|
||||||
(.containerExists blobstore container-name)))
|
(.containerExists blobstore container-name)))
|
||||||
|
|
||||||
(defn directory-exists?
|
(defn directory-exists?
|
||||||
"Predicate to check presence of a directory"
|
"Predicate to check presence of a directory"
|
||||||
([container-name path]
|
([container-name path]
|
||||||
(directory-exists? container-name path *blobstore*))
|
(directory-exists? container-name path *blobstore*))
|
||||||
([container-name path blobstore]
|
([container-name path #^BlobStore blobstore]
|
||||||
(.directoryExists blobstore container-name path)))
|
(.directoryExists blobstore container-name path)))
|
||||||
|
|
||||||
(defn create-directory
|
(defn create-directory
|
||||||
"Create a directory path."
|
"Create a directory path."
|
||||||
([container-name path]
|
([container-name path]
|
||||||
(create-directory container-name path *blobstore*))
|
(create-directory container-name path *blobstore*))
|
||||||
([container-name path blobstore]
|
([container-name path #^BlobStore blobstore]
|
||||||
(.createDirectory blobstore container-name path)))
|
(.createDirectory blobstore container-name path)))
|
||||||
|
|
||||||
(defn delete-directory
|
(defn delete-directory
|
||||||
"Delete a directory path."
|
"Delete a directory path."
|
||||||
([container-name path]
|
([container-name path]
|
||||||
(delete-directory container-name path *blobstore*))
|
(delete-directory container-name path *blobstore*))
|
||||||
([container-name path blobstore]
|
([container-name path #^BlobStore blobstore]
|
||||||
(.deleteDirectory blobstore container-name path)))
|
(.deleteDirectory blobstore container-name path)))
|
||||||
|
|
||||||
(defn blob-exists?
|
(defn blob-exists?
|
||||||
"Predicate to check presence of a blob"
|
"Predicate to check presence of a blob"
|
||||||
([container-name path]
|
([container-name path]
|
||||||
(blob-exists? container-name path *blobstore*))
|
(blob-exists? container-name path *blobstore*))
|
||||||
([container-name path blobstore]
|
([container-name path #^BlobStore blobstore]
|
||||||
(.blobExists blobstore container-name path)))
|
(.blobExists blobstore container-name path)))
|
||||||
|
|
||||||
(defn put-blob
|
(defn put-blob
|
||||||
"Put a blob. Metadata in the blob determines location."
|
"Put a blob. Metadata in the blob determines location."
|
||||||
([container-name blob]
|
([container-name blob]
|
||||||
(put-blob container-name blob *blobstore*))
|
(put-blob container-name blob *blobstore*))
|
||||||
([container-name blob blobstore]
|
([container-name blob #^BlobStore blobstore]
|
||||||
(.putBlob blobstore container-name blob)))
|
(.putBlob blobstore container-name blob)))
|
||||||
|
|
||||||
(defn blob-metadata
|
(defn blob-metadata
|
||||||
"Get metadata from given path"
|
"Get metadata from given path"
|
||||||
([container-name path]
|
([container-name path]
|
||||||
(blob-metadata container-name path *blobstore*))
|
(blob-metadata container-name path *blobstore*))
|
||||||
([container-name path blobstore]
|
([container-name path #^BlobStore blobstore]
|
||||||
(.blobMetadata blobstore container-name path)))
|
(.blobMetadata blobstore container-name path)))
|
||||||
|
|
||||||
(defn get-blob
|
(defn get-blob
|
||||||
"Get blob from given path"
|
"Get blob from given path"
|
||||||
([container-name path]
|
([container-name path]
|
||||||
(get-blob container-name path *blobstore*))
|
(get-blob container-name path *blobstore*))
|
||||||
([container-name path blobstore]
|
([container-name path #^BlobStore blobstore]
|
||||||
(.getBlob blobstore container-name path)))
|
(.getBlob blobstore container-name path)))
|
||||||
|
|
||||||
(defn remove-blob
|
(defn remove-blob
|
||||||
"Remove blob from given path"
|
"Remove blob from given path"
|
||||||
([container-name path]
|
([container-name path]
|
||||||
(remove-blob container-name path *blobstore*))
|
(remove-blob container-name path *blobstore*))
|
||||||
([container-name path blobstore]
|
([container-name path #^BlobStore blobstore]
|
||||||
(.removeBlob blobstore container-name path)))
|
(.removeBlob blobstore container-name path)))
|
||||||
|
|
||||||
(defn count-blobs
|
(defn count-blobs
|
||||||
|
@ -281,23 +281,18 @@ example:
|
||||||
"add a content md5 to a blob, or make a new blob that has an md5.
|
"add a content md5 to a blob, or make a new blob that has an md5.
|
||||||
note that this implies rebuffering, if the blob's payload isn't repeatable"
|
note that this implies rebuffering, if the blob's payload isn't repeatable"
|
||||||
([#^Blob blob]
|
([#^Blob blob]
|
||||||
(md5-blob *blobstore*))
|
(Payloads/calculateMD5 blob))
|
||||||
([blob-or-name blobstore-or-payload]
|
([#^String name payload]
|
||||||
(if (blobstore? blobstore-or-payload)
|
(blob name payload *blobstore*))
|
||||||
(-> (blobstore-context blobstore-or-payload)
|
|
||||||
.utils
|
|
||||||
.encryption
|
|
||||||
(.generateMD5BufferingIfNotRepeatable blob-or-name))
|
|
||||||
(md5-blob blob-or-name blobstore-or-payload *blobstore*)))
|
|
||||||
([#^String name payload #^BlobStore blobstore]
|
([#^String name payload #^BlobStore blobstore]
|
||||||
(md5-blob (blob name payload blobstore) blobstore)))
|
(md5-blob (blob name payload blobstore))))
|
||||||
|
|
||||||
(defn upload-blob
|
(defn upload-blob
|
||||||
"Create anrepresenting text data:
|
"Create anrepresenting text data:
|
||||||
container, name, string -> etag"
|
container, name, string -> etag"
|
||||||
([container-name name data]
|
([container-name name data]
|
||||||
(upload-blob container-name name data *blobstore*))
|
(upload-blob container-name name data *blobstore*))
|
||||||
([container-name name data blobstore]
|
([container-name name data #^BlobStore blobstore]
|
||||||
(put-blob container-name
|
(put-blob container-name
|
||||||
(md5-blob name data blobstore) blobstore)))
|
(md5-blob name data blobstore) blobstore)))
|
||||||
|
|
||||||
|
|
|
@ -77,19 +77,22 @@ import org.jclouds.blobstore.options.GetOptions;
|
||||||
import org.jclouds.blobstore.options.ListContainerOptions;
|
import org.jclouds.blobstore.options.ListContainerOptions;
|
||||||
import org.jclouds.blobstore.strategy.IfDirectoryReturnNameStrategy;
|
import org.jclouds.blobstore.strategy.IfDirectoryReturnNameStrategy;
|
||||||
import org.jclouds.blobstore.util.BlobUtils;
|
import org.jclouds.blobstore.util.BlobUtils;
|
||||||
|
import org.jclouds.crypto.Crypto;
|
||||||
|
import org.jclouds.crypto.CryptoStreams;
|
||||||
import org.jclouds.date.DateService;
|
import org.jclouds.date.DateService;
|
||||||
import org.jclouds.domain.Location;
|
import org.jclouds.domain.Location;
|
||||||
import org.jclouds.encryption.EncryptionService;
|
|
||||||
import org.jclouds.http.HttpCommand;
|
import org.jclouds.http.HttpCommand;
|
||||||
import org.jclouds.http.HttpRequest;
|
import org.jclouds.http.HttpRequest;
|
||||||
import org.jclouds.http.HttpResponse;
|
import org.jclouds.http.HttpResponse;
|
||||||
import org.jclouds.http.HttpResponseException;
|
import org.jclouds.http.HttpResponseException;
|
||||||
import org.jclouds.http.options.HttpRequestOptions;
|
import org.jclouds.http.options.HttpRequestOptions;
|
||||||
|
import org.jclouds.io.Payloads;
|
||||||
import org.jclouds.io.payloads.ByteArrayPayload;
|
import org.jclouds.io.payloads.ByteArrayPayload;
|
||||||
import org.jclouds.io.payloads.DelegatingPayload;
|
import org.jclouds.io.payloads.DelegatingPayload;
|
||||||
|
|
||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
|
import com.google.common.base.Throwables;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.common.collect.Multimaps;
|
import com.google.common.collect.Multimaps;
|
||||||
import com.google.common.io.Closeables;
|
import com.google.common.io.Closeables;
|
||||||
|
@ -106,7 +109,7 @@ import com.google.inject.internal.Nullable;
|
||||||
public class TransientAsyncBlobStore extends BaseAsyncBlobStore {
|
public class TransientAsyncBlobStore extends BaseAsyncBlobStore {
|
||||||
|
|
||||||
protected final DateService dateService;
|
protected final DateService dateService;
|
||||||
protected final EncryptionService encryptionService;
|
protected final Crypto crypto;
|
||||||
protected final ConcurrentMap<String, ConcurrentMap<String, Blob>> containerToBlobs;
|
protected final ConcurrentMap<String, ConcurrentMap<String, Blob>> containerToBlobs;
|
||||||
protected final ConcurrentMap<String, Location> containerToLocation;
|
protected final ConcurrentMap<String, Location> containerToLocation;
|
||||||
protected final HttpGetOptionsListToGetOptions httpGetOptionsConverter;
|
protected final HttpGetOptionsListToGetOptions httpGetOptionsConverter;
|
||||||
|
@ -114,8 +117,8 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore {
|
||||||
protected final Factory blobFactory;
|
protected final Factory blobFactory;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
protected TransientAsyncBlobStore(BlobStoreContext context, DateService dateService,
|
protected TransientAsyncBlobStore(BlobStoreContext context, DateService dateService, Crypto crypto,
|
||||||
EncryptionService encryptionService, ConcurrentMap<String, ConcurrentMap<String, Blob>> containerToBlobs,
|
ConcurrentMap<String, ConcurrentMap<String, Blob>> containerToBlobs,
|
||||||
ConcurrentMap<String, Location> containerToLocation,
|
ConcurrentMap<String, Location> containerToLocation,
|
||||||
HttpGetOptionsListToGetOptions httpGetOptionsConverter,
|
HttpGetOptionsListToGetOptions httpGetOptionsConverter,
|
||||||
IfDirectoryReturnNameStrategy ifDirectoryReturnName, Blob.Factory blobFactory, BlobUtils blobUtils,
|
IfDirectoryReturnNameStrategy ifDirectoryReturnName, Blob.Factory blobFactory, BlobUtils blobUtils,
|
||||||
|
@ -124,7 +127,7 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore {
|
||||||
super(context, blobUtils, service, defaultLocation, locations);
|
super(context, blobUtils, service, defaultLocation, locations);
|
||||||
this.blobFactory = blobFactory;
|
this.blobFactory = blobFactory;
|
||||||
this.dateService = dateService;
|
this.dateService = dateService;
|
||||||
this.encryptionService = encryptionService;
|
this.crypto = crypto;
|
||||||
this.containerToBlobs = containerToBlobs;
|
this.containerToBlobs = containerToBlobs;
|
||||||
this.containerToLocation = containerToLocation;
|
this.containerToLocation = containerToLocation;
|
||||||
this.httpGetOptionsConverter = httpGetOptionsConverter;
|
this.httpGetOptionsConverter = httpGetOptionsConverter;
|
||||||
|
@ -464,20 +467,23 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore {
|
||||||
payload = (object.getPayload() instanceof DelegatingPayload) ? (DelegatingPayload.class.cast(
|
payload = (object.getPayload() instanceof DelegatingPayload) ? (DelegatingPayload.class.cast(
|
||||||
object.getPayload()).getDelegate() instanceof ByteArrayPayload) ? ByteArrayPayload.class
|
object.getPayload()).getDelegate() instanceof ByteArrayPayload) ? ByteArrayPayload.class
|
||||||
.cast(DelegatingPayload.class.cast(object.getPayload()).getDelegate()) : null : null;
|
.cast(DelegatingPayload.class.cast(object.getPayload()).getDelegate()) : null : null;
|
||||||
if (payload == null || !(payload instanceof ByteArrayPayload)) {
|
try {
|
||||||
InputStream input = object.getPayload().getInput();
|
if (payload == null || !(payload instanceof ByteArrayPayload)) {
|
||||||
try {
|
InputStream input = object.getPayload().getInput();
|
||||||
String oldContentType = object.getPayload().getContentType();
|
try {
|
||||||
payload = encryptionService.generatePayloadWithMD5For(input);
|
String oldContentType = object.getPayload().getContentType();
|
||||||
payload.setContentType(oldContentType);
|
payload = (ByteArrayPayload) Payloads.calculateMD5(Payloads.newPayload(object.getPayload().getInput()));
|
||||||
} finally {
|
payload.setContentType(oldContentType);
|
||||||
Closeables.closeQuietly(input);
|
} finally {
|
||||||
|
Closeables.closeQuietly(input);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (payload.getContentMD5() == null)
|
||||||
|
Payloads.calculateMD5(object, crypto.md5());
|
||||||
}
|
}
|
||||||
} else {
|
} catch (IOException e) {
|
||||||
if (payload.getContentMD5() == null)
|
Throwables.propagate(e);
|
||||||
payload = (ByteArrayPayload) encryptionService.generateMD5BufferingIfNotRepeatable(payload);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Blob blob = blobFactory.create(copy(object.getMetadata()));
|
Blob blob = blobFactory.create(copy(object.getMetadata()));
|
||||||
blob.setPayload(payload);
|
blob.setPayload(payload);
|
||||||
blob.getMetadata().setLastModified(new Date());
|
blob.getMetadata().setLastModified(new Date());
|
||||||
|
@ -485,7 +491,7 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore {
|
||||||
blob.getMetadata().setContentMD5(payload.getContentMD5());
|
blob.getMetadata().setContentMD5(payload.getContentMD5());
|
||||||
blob.getMetadata().setContentType(payload.getContentType());
|
blob.getMetadata().setContentType(payload.getContentType());
|
||||||
|
|
||||||
String eTag = encryptionService.hex(payload.getContentMD5());
|
String eTag = CryptoStreams.hex(payload.getContentMD5());
|
||||||
blob.getMetadata().setETag(eTag);
|
blob.getMetadata().setETag(eTag);
|
||||||
container.put(blob.getMetadata().getName(), blob);
|
container.put(blob.getMetadata().getName(), blob);
|
||||||
|
|
||||||
|
@ -495,7 +501,7 @@ public class TransientAsyncBlobStore extends BaseAsyncBlobStore {
|
||||||
blob.getAllHeaders().put(HttpHeaders.ETAG, eTag);
|
blob.getAllHeaders().put(HttpHeaders.ETAG, eTag);
|
||||||
blob.getAllHeaders().put(HttpHeaders.CONTENT_TYPE, payload.getContentType());
|
blob.getAllHeaders().put(HttpHeaders.CONTENT_TYPE, payload.getContentType());
|
||||||
blob.getAllHeaders().put(HttpHeaders.CONTENT_LENGTH, payload.getContentLength() + "");
|
blob.getAllHeaders().put(HttpHeaders.CONTENT_LENGTH, payload.getContentLength() + "");
|
||||||
blob.getAllHeaders().put("Content-MD5", encryptionService.base64(payload.getContentMD5()));
|
blob.getAllHeaders().put("Content-MD5", CryptoStreams.base64(payload.getContentMD5()));
|
||||||
blob.getAllHeaders().putAll(Multimaps.forMap(blob.getMetadata().getUserMetadata()));
|
blob.getAllHeaders().putAll(Multimaps.forMap(blob.getMetadata().getUserMetadata()));
|
||||||
|
|
||||||
return immediateFuture(eTag);
|
return immediateFuture(eTag);
|
||||||
|
|
|
@ -31,7 +31,7 @@ import org.jclouds.blobstore.strategy.ContainsValueInListStrategy;
|
||||||
import org.jclouds.blobstore.strategy.GetBlobsInListStrategy;
|
import org.jclouds.blobstore.strategy.GetBlobsInListStrategy;
|
||||||
import org.jclouds.blobstore.strategy.PutBlobsStrategy;
|
import org.jclouds.blobstore.strategy.PutBlobsStrategy;
|
||||||
import org.jclouds.blobstore.strategy.internal.ListContainerAndRecurseThroughFolders;
|
import org.jclouds.blobstore.strategy.internal.ListContainerAndRecurseThroughFolders;
|
||||||
import org.jclouds.encryption.EncryptionService;
|
import org.jclouds.crypto.Crypto;
|
||||||
|
|
||||||
import com.google.inject.AbstractModule;
|
import com.google.inject.AbstractModule;
|
||||||
import com.google.inject.Scopes;
|
import com.google.inject.Scopes;
|
||||||
|
@ -84,14 +84,14 @@ public class BlobStoreMapModule extends AbstractModule {
|
||||||
@Inject
|
@Inject
|
||||||
PutBlobsStrategy putBlobsStrategy;
|
PutBlobsStrategy putBlobsStrategy;
|
||||||
@Inject
|
@Inject
|
||||||
EncryptionService encryptionService;
|
Crypto crypto;
|
||||||
@Inject
|
@Inject
|
||||||
ListContainerAndRecurseThroughFolders listStrategy;
|
ListContainerAndRecurseThroughFolders listStrategy;
|
||||||
|
|
||||||
public InputStreamMap create(String containerName, ListContainerOptions options) {
|
public InputStreamMap create(String containerName, ListContainerOptions options) {
|
||||||
return new InputStreamMapImpl(connection, blobFactory, getAllBlobs, listStrategy,
|
return new InputStreamMapImpl(connection, blobFactory, getAllBlobs, listStrategy,
|
||||||
containsValueStrategy, putBlobsStrategy, containerName, options,
|
containsValueStrategy, putBlobsStrategy, containerName, options,
|
||||||
encryptionService);
|
crypto);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
*/
|
*/
|
||||||
package org.jclouds.blobstore.domain;
|
package org.jclouds.blobstore.domain;
|
||||||
|
|
||||||
import org.jclouds.http.PayloadEnclosing;
|
import org.jclouds.io.PayloadEnclosing;
|
||||||
|
|
||||||
import com.google.common.collect.Multimap;
|
import com.google.common.collect.Multimap;
|
||||||
import com.google.inject.internal.Nullable;
|
import com.google.inject.internal.Nullable;
|
||||||
|
|
|
@ -25,9 +25,9 @@ import javax.inject.Inject;
|
||||||
import org.jclouds.blobstore.domain.Blob;
|
import org.jclouds.blobstore.domain.Blob;
|
||||||
import org.jclouds.blobstore.domain.MutableBlobMetadata;
|
import org.jclouds.blobstore.domain.MutableBlobMetadata;
|
||||||
import org.jclouds.blobstore.domain.StorageMetadata;
|
import org.jclouds.blobstore.domain.StorageMetadata;
|
||||||
import org.jclouds.http.PayloadEnclosing;
|
|
||||||
import org.jclouds.http.internal.PayloadEnclosingImpl;
|
import org.jclouds.http.internal.PayloadEnclosingImpl;
|
||||||
import org.jclouds.io.Payload;
|
import org.jclouds.io.Payload;
|
||||||
|
import org.jclouds.io.PayloadEnclosing;
|
||||||
import org.jclouds.io.payloads.DelegatingPayload;
|
import org.jclouds.io.payloads.DelegatingPayload;
|
||||||
|
|
||||||
import com.google.common.collect.LinkedHashMultimap;
|
import com.google.common.collect.LinkedHashMultimap;
|
||||||
|
|
|
@ -18,23 +18,26 @@
|
||||||
*/
|
*/
|
||||||
package org.jclouds.blobstore.functions;
|
package org.jclouds.blobstore.functions;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import org.jclouds.blobstore.domain.Blob;
|
import org.jclouds.blobstore.domain.Blob;
|
||||||
import org.jclouds.encryption.EncryptionService;
|
import org.jclouds.crypto.Crypto;
|
||||||
import org.jclouds.io.Payloads;
|
import org.jclouds.io.Payloads;
|
||||||
|
|
||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
|
import com.google.common.base.Throwables;
|
||||||
|
|
||||||
public class ObjectMD5 implements Function<Object, byte[]> {
|
public class ObjectMD5 implements Function<Object, byte[]> {
|
||||||
|
|
||||||
protected final Blob.Factory blobFactory;
|
protected final Blob.Factory blobFactory;
|
||||||
protected final EncryptionService encryptionService;
|
protected final Crypto crypto;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ObjectMD5(EncryptionService encryptionService, Blob.Factory blobFactory) {
|
ObjectMD5(Crypto crypto, Blob.Factory blobFactory) {
|
||||||
this.blobFactory = blobFactory;
|
this.blobFactory = blobFactory;
|
||||||
this.encryptionService = encryptionService;
|
this.crypto = crypto;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] apply(Object from) {
|
public byte[] apply(Object from) {
|
||||||
|
@ -46,7 +49,11 @@ public class ObjectMD5 implements Function<Object, byte[]> {
|
||||||
object.setPayload(Payloads.newPayload(from));
|
object.setPayload(Payloads.newPayload(from));
|
||||||
}
|
}
|
||||||
if (object.getMetadata().getContentMD5() == null)
|
if (object.getMetadata().getContentMD5() == null)
|
||||||
encryptionService.generateMD5BufferingIfNotRepeatable(object);
|
try {
|
||||||
|
Payloads.calculateMD5(object, crypto.md5());
|
||||||
|
} catch (IOException e) {
|
||||||
|
Throwables.propagate(e);
|
||||||
|
}
|
||||||
return object.getPayload().getContentMD5();
|
return object.getPayload().getContentMD5();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,6 @@ import org.jclouds.blobstore.domain.StorageMetadata;
|
||||||
import org.jclouds.blobstore.options.ListContainerOptions;
|
import org.jclouds.blobstore.options.ListContainerOptions;
|
||||||
import org.jclouds.blobstore.util.BlobUtils;
|
import org.jclouds.blobstore.util.BlobUtils;
|
||||||
import org.jclouds.blobstore.util.internal.BlobUtilsImpl;
|
import org.jclouds.blobstore.util.internal.BlobUtilsImpl;
|
||||||
import static org.jclouds.concurrent.ConcurrentUtils.*;
|
|
||||||
import org.jclouds.domain.Location;
|
import org.jclouds.domain.Location;
|
||||||
import org.jclouds.util.Utils;
|
import org.jclouds.util.Utils;
|
||||||
|
|
||||||
|
@ -114,7 +113,7 @@ public abstract class BaseAsyncBlobStore implements AsyncBlobStore {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public ListenableFuture<Long> countBlobs(final String containerName, final ListContainerOptions options) {
|
public ListenableFuture<Long> countBlobs(final String containerName, final ListContainerOptions options) {
|
||||||
return makeListenable(service.submit(new Callable<Long>() {
|
return org.jclouds.concurrent.Futures.makeListenable(service.submit(new Callable<Long>() {
|
||||||
public Long call() throws Exception {
|
public Long call() throws Exception {
|
||||||
return blobUtils.countBlobs(containerName, options);
|
return blobUtils.countBlobs(containerName, options);
|
||||||
}
|
}
|
||||||
|
@ -142,7 +141,7 @@ public abstract class BaseAsyncBlobStore implements AsyncBlobStore {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public ListenableFuture<Void> clearContainer(final String containerName, final ListContainerOptions options) {
|
public ListenableFuture<Void> clearContainer(final String containerName, final ListContainerOptions options) {
|
||||||
return makeListenable(service.submit(new Callable<Void>() {
|
return org.jclouds.concurrent.Futures.makeListenable(service.submit(new Callable<Void>() {
|
||||||
|
|
||||||
public Void call() throws Exception {
|
public Void call() throws Exception {
|
||||||
blobUtils.clearContainer(containerName, options);
|
blobUtils.clearContainer(containerName, options);
|
||||||
|
@ -160,7 +159,7 @@ public abstract class BaseAsyncBlobStore implements AsyncBlobStore {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public ListenableFuture<Void> deleteDirectory(final String containerName, final String directory) {
|
public ListenableFuture<Void> deleteDirectory(final String containerName, final String directory) {
|
||||||
return makeListenable(service.submit(new Callable<Void>() {
|
return org.jclouds.concurrent.Futures.makeListenable(service.submit(new Callable<Void>() {
|
||||||
|
|
||||||
public Void call() throws Exception {
|
public Void call() throws Exception {
|
||||||
blobUtils.deleteDirectory(containerName, directory);
|
blobUtils.deleteDirectory(containerName, directory);
|
||||||
|
@ -179,7 +178,7 @@ public abstract class BaseAsyncBlobStore implements AsyncBlobStore {
|
||||||
* virtual path
|
* virtual path
|
||||||
*/
|
*/
|
||||||
public ListenableFuture<Boolean> directoryExists(final String containerName, final String directory) {
|
public ListenableFuture<Boolean> directoryExists(final String containerName, final String directory) {
|
||||||
return makeListenable(service.submit(new Callable<Boolean>() {
|
return org.jclouds.concurrent.Futures.makeListenable(service.submit(new Callable<Boolean>() {
|
||||||
|
|
||||||
public Boolean call() throws Exception {
|
public Boolean call() throws Exception {
|
||||||
return blobUtils.directoryExists(containerName, directory);
|
return blobUtils.directoryExists(containerName, directory);
|
||||||
|
@ -200,7 +199,7 @@ public abstract class BaseAsyncBlobStore implements AsyncBlobStore {
|
||||||
public ListenableFuture<Void> createDirectory(final String containerName, final String directory) {
|
public ListenableFuture<Void> createDirectory(final String containerName, final String directory) {
|
||||||
|
|
||||||
return blobUtils.directoryExists(containerName, directory) ? Futures.immediateFuture((Void) null)
|
return blobUtils.directoryExists(containerName, directory) ? Futures.immediateFuture((Void) null)
|
||||||
: makeListenable(service.submit(new Callable<Void>() {
|
: org.jclouds.concurrent.Futures.makeListenable(service.submit(new Callable<Void>() {
|
||||||
public Void call() throws Exception {
|
public Void call() throws Exception {
|
||||||
blobUtils.createDirectory(containerName, directory);
|
blobUtils.createDirectory(containerName, directory);
|
||||||
return null;
|
return null;
|
||||||
|
@ -230,7 +229,7 @@ public abstract class BaseAsyncBlobStore implements AsyncBlobStore {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public ListenableFuture<Void> deleteContainer(final String container) {
|
public ListenableFuture<Void> deleteContainer(final String container) {
|
||||||
return makeListenable(service.submit(new Callable<Void>() {
|
return org.jclouds.concurrent.Futures.makeListenable(service.submit(new Callable<Void>() {
|
||||||
|
|
||||||
public Void call() throws Exception {
|
public Void call() throws Exception {
|
||||||
deleteAndEnsurePathGone(container);
|
deleteAndEnsurePathGone(container);
|
||||||
|
|
|
@ -23,6 +23,7 @@ import static com.google.common.collect.Lists.newArrayList;
|
||||||
import static org.jclouds.io.Payloads.newPayload;
|
import static org.jclouds.io.Payloads.newPayload;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -38,8 +39,9 @@ import org.jclouds.blobstore.strategy.ContainsValueInListStrategy;
|
||||||
import org.jclouds.blobstore.strategy.GetBlobsInListStrategy;
|
import org.jclouds.blobstore.strategy.GetBlobsInListStrategy;
|
||||||
import org.jclouds.blobstore.strategy.PutBlobsStrategy;
|
import org.jclouds.blobstore.strategy.PutBlobsStrategy;
|
||||||
import org.jclouds.blobstore.strategy.internal.ListContainerAndRecurseThroughFolders;
|
import org.jclouds.blobstore.strategy.internal.ListContainerAndRecurseThroughFolders;
|
||||||
import org.jclouds.encryption.EncryptionService;
|
import org.jclouds.crypto.Crypto;
|
||||||
import org.jclouds.io.Payload;
|
import org.jclouds.io.Payload;
|
||||||
|
import org.jclouds.io.Payloads;
|
||||||
import org.jclouds.io.payloads.ByteArrayPayload;
|
import org.jclouds.io.payloads.ByteArrayPayload;
|
||||||
import org.jclouds.io.payloads.FilePayload;
|
import org.jclouds.io.payloads.FilePayload;
|
||||||
import org.jclouds.io.payloads.InputStreamPayload;
|
import org.jclouds.io.payloads.InputStreamPayload;
|
||||||
|
@ -47,6 +49,7 @@ import org.jclouds.io.payloads.StringPayload;
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
|
import com.google.common.base.Throwables;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Map representation of a live connection to a BlobStore. All put operations will result in ETag
|
* Map representation of a live connection to a BlobStore. All put operations will result in ETag
|
||||||
|
@ -59,16 +62,16 @@ import com.google.common.base.Function;
|
||||||
* @see BaseBlobMap
|
* @see BaseBlobMap
|
||||||
*/
|
*/
|
||||||
public class InputStreamMapImpl extends BaseBlobMap<InputStream> implements InputStreamMap {
|
public class InputStreamMapImpl extends BaseBlobMap<InputStream> implements InputStreamMap {
|
||||||
protected final EncryptionService encryptionService;
|
protected final Crypto crypto;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public InputStreamMapImpl(BlobStore connection, Blob.Factory blobFactory,
|
public InputStreamMapImpl(BlobStore connection, Blob.Factory blobFactory,
|
||||||
GetBlobsInListStrategy getAllBlobs, ListContainerAndRecurseThroughFolders listStrategy,
|
GetBlobsInListStrategy getAllBlobs, ListContainerAndRecurseThroughFolders listStrategy,
|
||||||
ContainsValueInListStrategy containsValueStrategy, PutBlobsStrategy putBlobsStrategy,
|
ContainsValueInListStrategy containsValueStrategy, PutBlobsStrategy putBlobsStrategy,
|
||||||
String containerName, ListContainerOptions options, EncryptionService encryptionService) {
|
String containerName, ListContainerOptions options, Crypto crypto) {
|
||||||
super(connection, getAllBlobs, containsValueStrategy, putBlobsStrategy, listStrategy,
|
super(connection, getAllBlobs, containsValueStrategy, putBlobsStrategy, listStrategy,
|
||||||
containerName, options);
|
containerName, options);
|
||||||
this.encryptionService = encryptionService;
|
this.crypto = crypto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -144,7 +147,11 @@ public class InputStreamMapImpl extends BaseBlobMap<InputStream> implements Inpu
|
||||||
Blob newBlobWithMD5(String name, Object value) {
|
Blob newBlobWithMD5(String name, Object value) {
|
||||||
Blob blob = blobstore.newBlob(prefixer.apply(name));
|
Blob blob = blobstore.newBlob(prefixer.apply(name));
|
||||||
blob.setPayload(newPayload(value));
|
blob.setPayload(newPayload(value));
|
||||||
encryptionService.generateMD5BufferingIfNotRepeatable(blob);
|
try {
|
||||||
|
Payloads.calculateMD5(blob, crypto.md5());
|
||||||
|
} catch (IOException e) {
|
||||||
|
Throwables.propagate(e);
|
||||||
|
}
|
||||||
return blob;
|
return blob;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
package org.jclouds.blobstore.strategy.internal;
|
package org.jclouds.blobstore.strategy.internal;
|
||||||
|
|
||||||
import static org.jclouds.blobstore.options.ListContainerOptions.Builder.recursive;
|
import static org.jclouds.blobstore.options.ListContainerOptions.Builder.recursive;
|
||||||
import static org.jclouds.concurrent.ConcurrentUtils.awaitCompletion;
|
import static org.jclouds.concurrent.FutureIterables.awaitCompletion;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
package org.jclouds.blobstore.strategy.internal;
|
package org.jclouds.blobstore.strategy.internal;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkState;
|
import static com.google.common.base.Preconditions.checkState;
|
||||||
import static org.jclouds.concurrent.ConcurrentUtils.transformParallel;
|
import static org.jclouds.concurrent.FutureIterables.transformParallel;
|
||||||
|
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
*/
|
*/
|
||||||
package org.jclouds.blobstore.strategy.internal;
|
package org.jclouds.blobstore.strategy.internal;
|
||||||
|
|
||||||
import static org.jclouds.concurrent.ConcurrentUtils.transformParallel;
|
import static org.jclouds.concurrent.FutureIterables.transformParallel;
|
||||||
|
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
*/
|
*/
|
||||||
package org.jclouds.blobstore.strategy.internal;
|
package org.jclouds.blobstore.strategy.internal;
|
||||||
|
|
||||||
import static org.jclouds.concurrent.ConcurrentUtils.awaitCompletion;
|
import static org.jclouds.concurrent.FutureIterables.awaitCompletion;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
*/
|
*/
|
||||||
package org.jclouds.blobstore.strategy.internal;
|
package org.jclouds.blobstore.strategy.internal;
|
||||||
|
|
||||||
import static org.jclouds.concurrent.ConcurrentUtils.awaitCompletion;
|
import static org.jclouds.concurrent.FutureIterables.awaitCompletion;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
|
|
|
@ -24,8 +24,7 @@ import static org.jclouds.blobstore.options.GetOptions.Builder.ifModifiedSince;
|
||||||
import static org.jclouds.blobstore.options.GetOptions.Builder.ifUnmodifiedSince;
|
import static org.jclouds.blobstore.options.GetOptions.Builder.ifUnmodifiedSince;
|
||||||
import static org.jclouds.blobstore.options.GetOptions.Builder.range;
|
import static org.jclouds.blobstore.options.GetOptions.Builder.range;
|
||||||
import static org.jclouds.blobstore.util.BlobStoreUtils.getContentAsStringOrNullAndClose;
|
import static org.jclouds.blobstore.util.BlobStoreUtils.getContentAsStringOrNullAndClose;
|
||||||
import static org.jclouds.concurrent.ConcurrentUtils.awaitCompletion;
|
import static org.jclouds.concurrent.FutureIterables.awaitCompletion;
|
||||||
import static org.jclouds.concurrent.ConcurrentUtils.compose;
|
|
||||||
import static org.testng.Assert.assertEquals;
|
import static org.testng.Assert.assertEquals;
|
||||||
import static org.testng.Assert.assertNotNull;
|
import static org.testng.Assert.assertNotNull;
|
||||||
import static org.testng.Assert.assertNull;
|
import static org.testng.Assert.assertNull;
|
||||||
|
@ -51,11 +50,13 @@ import org.jclouds.blobstore.domain.BlobMetadata;
|
||||||
import org.jclouds.blobstore.domain.PageSet;
|
import org.jclouds.blobstore.domain.PageSet;
|
||||||
import org.jclouds.blobstore.domain.StorageMetadata;
|
import org.jclouds.blobstore.domain.StorageMetadata;
|
||||||
import org.jclouds.blobstore.domain.StorageType;
|
import org.jclouds.blobstore.domain.StorageType;
|
||||||
import org.jclouds.encryption.EncryptionService;
|
import org.jclouds.concurrent.Futures;
|
||||||
import org.jclouds.encryption.internal.JCEEncryptionService;
|
import org.jclouds.crypto.Crypto;
|
||||||
|
import org.jclouds.crypto.CryptoStreams;
|
||||||
|
import org.jclouds.encryption.internal.JCECrypto;
|
||||||
import org.jclouds.http.BaseJettyTest;
|
import org.jclouds.http.BaseJettyTest;
|
||||||
import org.jclouds.http.HttpResponseException;
|
import org.jclouds.http.HttpResponseException;
|
||||||
import org.jclouds.io.Payload;
|
import org.jclouds.io.InputSuppliers;
|
||||||
import org.jclouds.io.Payloads;
|
import org.jclouds.io.Payloads;
|
||||||
import org.jclouds.logging.Logger;
|
import org.jclouds.logging.Logger;
|
||||||
import org.jclouds.util.Utils;
|
import org.jclouds.util.Utils;
|
||||||
|
@ -76,16 +77,15 @@ import com.google.common.io.InputSupplier;
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
*/
|
*/
|
||||||
public class BaseBlobIntegrationTest extends BaseBlobStoreIntegrationTest {
|
public class BaseBlobIntegrationTest extends BaseBlobStoreIntegrationTest {
|
||||||
private byte[] oneHundredOneConstitutions;
|
private InputSupplier<InputStream> oneHundredOneConstitutions;
|
||||||
private byte[] oneHundredOneConstitutionsMD5;
|
private byte[] oneHundredOneConstitutionsMD5;
|
||||||
|
|
||||||
@BeforeClass(groups = { "integration", "live" })
|
@BeforeClass(groups = { "integration", "live" })
|
||||||
@Override
|
@Override
|
||||||
public void setUpResourcesOnThisThread(ITestContext testContext) throws Exception {
|
public void setUpResourcesOnThisThread(ITestContext testContext) throws Exception {
|
||||||
super.setUpResourcesOnThisThread(testContext);
|
super.setUpResourcesOnThisThread(testContext);
|
||||||
Payload result = context.utils().encryption().generatePayloadWithMD5For(getTestDataSupplier().getInput());
|
oneHundredOneConstitutions = getTestDataSupplier();
|
||||||
oneHundredOneConstitutions = (byte[]) result.getRawContent();
|
oneHundredOneConstitutionsMD5 = CryptoStreams.md5(oneHundredOneConstitutions);
|
||||||
oneHundredOneConstitutionsMD5 = result.getContentMD5();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
|
@ -112,13 +112,16 @@ public class BaseBlobIntegrationTest extends BaseBlobStoreIntegrationTest {
|
||||||
Map<Integer, Future<?>> responses = Maps.newHashMap();
|
Map<Integer, Future<?>> responses = Maps.newHashMap();
|
||||||
for (int i = 0; i < 10; i++) {
|
for (int i = 0; i < 10; i++) {
|
||||||
|
|
||||||
responses.put(i, compose(context.getAsyncBlobStore().getBlob(containerName, key),
|
responses.put(i, Futures.compose(context.getAsyncBlobStore().getBlob(containerName, key),
|
||||||
new Function<Blob, Void>() {
|
new Function<Blob, Void>() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Void apply(Blob from) {
|
public Void apply(Blob from) {
|
||||||
assertEquals(context.utils().encryption().md5(from.getPayload().getInput()),
|
try {
|
||||||
oneHundredOneConstitutionsMD5);
|
assertEquals(CryptoStreams.md5(from.getPayload()), oneHundredOneConstitutionsMD5);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Throwables.propagate(e);
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,7 +141,7 @@ public class BaseBlobIntegrationTest extends BaseBlobStoreIntegrationTest {
|
||||||
Blob sourceObject = context.getBlobStore().newBlob(key);
|
Blob sourceObject = context.getBlobStore().newBlob(key);
|
||||||
sourceObject.getMetadata().setContentType("text/plain");
|
sourceObject.getMetadata().setContentType("text/plain");
|
||||||
sourceObject.getMetadata().setContentMD5(oneHundredOneConstitutionsMD5);
|
sourceObject.getMetadata().setContentMD5(oneHundredOneConstitutionsMD5);
|
||||||
sourceObject.setPayload(oneHundredOneConstitutions);
|
sourceObject.setPayload(oneHundredOneConstitutions.getInput());
|
||||||
context.getBlobStore().putBlob(containerName, sourceObject);
|
context.getBlobStore().putBlob(containerName, sourceObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -409,7 +412,7 @@ public class BaseBlobIntegrationTest extends BaseBlobStoreIntegrationTest {
|
||||||
blob.getMetadata().setContentType(type);
|
blob.getMetadata().setContentType(type);
|
||||||
blob.setPayload(Payloads.newPayload(content));
|
blob.setPayload(Payloads.newPayload(content));
|
||||||
if (content instanceof InputStream) {
|
if (content instanceof InputStream) {
|
||||||
context.utils().encryption().generateMD5BufferingIfNotRepeatable(blob);
|
Payloads.calculateMD5(blob, context.utils().crypto().md5());
|
||||||
}
|
}
|
||||||
String containerName = getContainerName();
|
String containerName = getContainerName();
|
||||||
try {
|
try {
|
||||||
|
@ -424,10 +427,10 @@ public class BaseBlobIntegrationTest extends BaseBlobStoreIntegrationTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected volatile static EncryptionService encryptionService;
|
protected volatile static Crypto crypto;
|
||||||
static {
|
static {
|
||||||
try {
|
try {
|
||||||
encryptionService = new JCEEncryptionService();
|
crypto = new JCECrypto();
|
||||||
} catch (NoSuchAlgorithmException e) {
|
} catch (NoSuchAlgorithmException e) {
|
||||||
Throwables.propagate(e);
|
Throwables.propagate(e);
|
||||||
} catch (CertificateException e) {
|
} catch (CertificateException e) {
|
||||||
|
@ -436,7 +439,7 @@ public class BaseBlobIntegrationTest extends BaseBlobStoreIntegrationTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(groups = { "integration", "live" })
|
@Test(groups = { "integration", "live" })
|
||||||
public void testMetadata() throws InterruptedException {
|
public void testMetadata() throws InterruptedException, IOException {
|
||||||
String key = "hello";
|
String key = "hello";
|
||||||
|
|
||||||
Blob blob = context.getBlobStore().newBlob(key);
|
Blob blob = context.getBlobStore().newBlob(key);
|
||||||
|
@ -447,7 +450,7 @@ public class BaseBlobIntegrationTest extends BaseBlobStoreIntegrationTest {
|
||||||
// normalize the
|
// normalize the
|
||||||
// providers.
|
// providers.
|
||||||
blob.getMetadata().getUserMetadata().put("Adrian", "powderpuff");
|
blob.getMetadata().getUserMetadata().put("Adrian", "powderpuff");
|
||||||
blob.getMetadata().setContentMD5(encryptionService.md5(Utils.toInputStream(TEST_STRING)));
|
Payloads.calculateMD5(blob, context.utils().crypto().md5());
|
||||||
String containerName = getContainerName();
|
String containerName = getContainerName();
|
||||||
try {
|
try {
|
||||||
assertNull(context.getBlobStore().blobMetadata(containerName, "powderpuff"));
|
assertNull(context.getBlobStore().blobMetadata(containerName, "powderpuff"));
|
||||||
|
@ -473,11 +476,11 @@ public class BaseBlobIntegrationTest extends BaseBlobStoreIntegrationTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void validateMetadata(BlobMetadata metadata) {
|
protected void validateMetadata(BlobMetadata metadata) throws IOException {
|
||||||
assert metadata.getContentType().startsWith("text/plain") : metadata.getContentType();
|
assert metadata.getContentType().startsWith("text/plain") : metadata.getContentType();
|
||||||
assertEquals(metadata.getSize(), new Long(TEST_STRING.length()));
|
assertEquals(metadata.getSize(), new Long(TEST_STRING.length()));
|
||||||
assertEquals(metadata.getUserMetadata().get("adrian"), "powderpuff");
|
assertEquals(metadata.getUserMetadata().get("adrian"), "powderpuff");
|
||||||
assertEquals(metadata.getContentMD5(), encryptionService.md5(Utils.toInputStream(TEST_STRING)));
|
assertEquals(metadata.getContentMD5(), CryptoStreams.md5(InputSuppliers.of(TEST_STRING)));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -26,7 +26,7 @@ import java.net.URL;
|
||||||
import java.net.URLConnection;
|
import java.net.URLConnection;
|
||||||
|
|
||||||
import org.jclouds.blobstore.domain.Blob;
|
import org.jclouds.blobstore.domain.Blob;
|
||||||
import org.jclouds.encryption.internal.JCEEncryptionService;
|
import org.jclouds.crypto.CryptoStreams;
|
||||||
import org.testng.annotations.Optional;
|
import org.testng.annotations.Optional;
|
||||||
import org.testng.annotations.Parameters;
|
import org.testng.annotations.Parameters;
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
|
@ -41,25 +41,20 @@ import org.testng.annotations.Test;
|
||||||
@Test(groups = { "live" }, testName = "blobstore.BlobLiveTest")
|
@Test(groups = { "live" }, testName = "blobstore.BlobLiveTest")
|
||||||
public class BaseBlobLiveTest extends BaseBlobStoreIntegrationTest {
|
public class BaseBlobLiveTest extends BaseBlobStoreIntegrationTest {
|
||||||
|
|
||||||
private static final String sysHttpStreamUrl = System
|
private static final String sysHttpStreamUrl = System.getProperty("jclouds.blobstore.httpstream.url");
|
||||||
.getProperty("jclouds.blobstore.httpstream.url");
|
private static final String sysHttpStreamETag = System.getProperty("jclouds.blobstore.httpstream.md5");
|
||||||
private static final String sysHttpStreamETag = System
|
|
||||||
.getProperty("jclouds.blobstore.httpstream.md5");
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Parameters( { "jclouds.blobstore.httpstream.url", "jclouds.blobstore.httpstream.md5" })
|
@Parameters( { "jclouds.blobstore.httpstream.url", "jclouds.blobstore.httpstream.md5" })
|
||||||
public void testCopyUrl(@Optional String httpStreamUrl, @Optional String httpStreamETag)
|
public void testCopyUrl(@Optional String httpStreamUrl, @Optional String httpStreamETag) throws Exception {
|
||||||
throws Exception {
|
httpStreamUrl = checkNotNull(httpStreamUrl != null ? httpStreamUrl : sysHttpStreamUrl, "httpStreamUrl");
|
||||||
httpStreamUrl = checkNotNull(httpStreamUrl != null ? httpStreamUrl : sysHttpStreamUrl,
|
|
||||||
"httpStreamUrl");
|
|
||||||
|
|
||||||
httpStreamETag = checkNotNull(httpStreamETag != null ? httpStreamETag : sysHttpStreamETag,
|
httpStreamETag = checkNotNull(httpStreamETag != null ? httpStreamETag : sysHttpStreamETag, "httpStreamMd5");
|
||||||
"httpStreamMd5");
|
|
||||||
|
|
||||||
String key = "hello";
|
String key = "hello";
|
||||||
|
|
||||||
URL url = new URL(httpStreamUrl);
|
URL url = new URL(httpStreamUrl);
|
||||||
byte[] md5 = new JCEEncryptionService().fromHex(httpStreamETag);
|
byte[] md5 = CryptoStreams.hex(httpStreamETag);
|
||||||
|
|
||||||
URLConnection connection = url.openConnection();
|
URLConnection connection = url.openConnection();
|
||||||
long length = connection.getContentLength();
|
long length = connection.getContentLength();
|
||||||
|
|
|
@ -36,6 +36,7 @@ import org.jclouds.blobstore.BlobMap;
|
||||||
import org.jclouds.blobstore.BlobStoreContext;
|
import org.jclouds.blobstore.BlobStoreContext;
|
||||||
import org.jclouds.blobstore.domain.Blob;
|
import org.jclouds.blobstore.domain.Blob;
|
||||||
import org.jclouds.blobstore.options.ListContainerOptions;
|
import org.jclouds.blobstore.options.ListContainerOptions;
|
||||||
|
import org.jclouds.io.Payloads;
|
||||||
import org.jclouds.util.Utils;
|
import org.jclouds.util.Utils;
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
|
@ -74,7 +75,7 @@ public abstract class BaseBlobMapIntegrationTest extends BaseMapIntegrationTest<
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(groups = { "integration", "live" })
|
@Test(groups = { "integration", "live" })
|
||||||
public void testRemove() throws InterruptedException, ExecutionException, TimeoutException {
|
public void testRemove() throws InterruptedException, ExecutionException, TimeoutException, IOException {
|
||||||
String bucketName = getContainerName();
|
String bucketName = getContainerName();
|
||||||
try {
|
try {
|
||||||
Map<String, Blob> map = createMap(context, bucketName);
|
Map<String, Blob> map = createMap(context, bucketName);
|
||||||
|
@ -118,7 +119,7 @@ public abstract class BaseBlobMapIntegrationTest extends BaseMapIntegrationTest<
|
||||||
.getValue()));
|
.getValue()));
|
||||||
Blob blob = entry.getValue();
|
Blob blob = entry.getValue();
|
||||||
blob.setPayload("");
|
blob.setPayload("");
|
||||||
context.utils().encryption().generateMD5BufferingIfNotRepeatable(blob);
|
Payloads.calculateMD5(blob);
|
||||||
entry.setValue(blob);
|
entry.setValue(blob);
|
||||||
}
|
}
|
||||||
assertConsistencyAware(new Runnable() {
|
assertConsistencyAware(new Runnable() {
|
||||||
|
@ -139,14 +140,14 @@ public abstract class BaseBlobMapIntegrationTest extends BaseMapIntegrationTest<
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(groups = { "integration", "live" })
|
@Test(groups = { "integration", "live" })
|
||||||
public void testContains() throws InterruptedException, ExecutionException, TimeoutException {
|
public void testContains() throws InterruptedException, ExecutionException, TimeoutException, IOException {
|
||||||
String bucketName = getContainerName();
|
String bucketName = getContainerName();
|
||||||
try {
|
try {
|
||||||
Map<String, Blob> map = createMap(context, bucketName);
|
Map<String, Blob> map = createMap(context, bucketName);
|
||||||
putStringWithMD5(map, "one", "apple");
|
putStringWithMD5(map, "one", "apple");
|
||||||
Blob blob = context.getBlobStore().newBlob("one");
|
Blob blob = context.getBlobStore().newBlob("one");
|
||||||
blob.setPayload("apple");
|
blob.setPayload("apple");
|
||||||
context.utils().encryption().generateMD5BufferingIfNotRepeatable(blob);
|
Payloads.calculateMD5(blob);
|
||||||
assertConsistencyAwareContainsValue(map, blob);
|
assertConsistencyAwareContainsValue(map, blob);
|
||||||
} finally {
|
} finally {
|
||||||
returnContainer(bucketName);
|
returnContainer(bucketName);
|
||||||
|
@ -174,11 +175,11 @@ public abstract class BaseBlobMapIntegrationTest extends BaseMapIntegrationTest<
|
||||||
Map<String, Blob> map = createMap(context, bucketName);
|
Map<String, Blob> map = createMap(context, bucketName);
|
||||||
Blob blob = context.getBlobStore().newBlob("one");
|
Blob blob = context.getBlobStore().newBlob("one");
|
||||||
blob.setPayload(Utils.toInputStream("apple"));
|
blob.setPayload(Utils.toInputStream("apple"));
|
||||||
context.utils().encryption().generateMD5BufferingIfNotRepeatable(blob);
|
Payloads.calculateMD5(blob);
|
||||||
Blob old = map.put(blob.getMetadata().getName(), blob);
|
Blob old = map.put(blob.getMetadata().getName(), blob);
|
||||||
getOneReturnsAppleAndOldValueIsNull(map, old);
|
getOneReturnsAppleAndOldValueIsNull(map, old);
|
||||||
blob.setPayload(Utils.toInputStream("bear"));
|
blob.setPayload(Utils.toInputStream("bear"));
|
||||||
context.utils().encryption().generateMD5BufferingIfNotRepeatable(blob);
|
Payloads.calculateMD5(blob);
|
||||||
Blob apple = map.put(blob.getMetadata().getName(), blob);
|
Blob apple = map.put(blob.getMetadata().getName(), blob);
|
||||||
getOneReturnsBearAndOldValueIsApple(map, apple);
|
getOneReturnsBearAndOldValueIsApple(map, apple);
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -239,10 +240,10 @@ public abstract class BaseBlobMapIntegrationTest extends BaseMapIntegrationTest<
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void putStringWithMD5(Map<String, Blob> map, String key, String text) {
|
protected void putStringWithMD5(Map<String, Blob> map, String key, String text) throws IOException {
|
||||||
Blob blob = context.getBlobStore().newBlob(key);
|
Blob blob = context.getBlobStore().newBlob(key);
|
||||||
blob.setPayload(text);
|
blob.setPayload(text);
|
||||||
context.utils().encryption().generateMD5BufferingIfNotRepeatable(blob);
|
Payloads.calculateMD5(blob);
|
||||||
map.put(key, blob);
|
map.put(key, blob);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,9 +23,9 @@ import static com.google.common.collect.Iterables.get;
|
||||||
import static org.jclouds.blobstore.options.ListContainerOptions.Builder.afterMarker;
|
import static org.jclouds.blobstore.options.ListContainerOptions.Builder.afterMarker;
|
||||||
import static org.jclouds.blobstore.options.ListContainerOptions.Builder.inDirectory;
|
import static org.jclouds.blobstore.options.ListContainerOptions.Builder.inDirectory;
|
||||||
import static org.jclouds.blobstore.options.ListContainerOptions.Builder.maxResults;
|
import static org.jclouds.blobstore.options.ListContainerOptions.Builder.maxResults;
|
||||||
import static org.jclouds.util.Utils.toInputStream;
|
|
||||||
import static org.testng.Assert.assertEquals;
|
import static org.testng.Assert.assertEquals;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
|
@ -37,6 +37,9 @@ import org.jclouds.blobstore.domain.Blob;
|
||||||
import org.jclouds.blobstore.domain.BlobMetadata;
|
import org.jclouds.blobstore.domain.BlobMetadata;
|
||||||
import org.jclouds.blobstore.domain.PageSet;
|
import org.jclouds.blobstore.domain.PageSet;
|
||||||
import org.jclouds.blobstore.domain.StorageMetadata;
|
import org.jclouds.blobstore.domain.StorageMetadata;
|
||||||
|
import org.jclouds.crypto.CryptoStreams;
|
||||||
|
import org.jclouds.io.InputSuppliers;
|
||||||
|
import org.jclouds.io.Payloads;
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -68,7 +71,7 @@ public class BaseContainerIntegrationTest extends BaseBlobStoreIntegrationTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(groups = { "integration", "live" })
|
@Test(groups = { "integration", "live" })
|
||||||
public void testWithDetails() throws InterruptedException {
|
public void testWithDetails() throws InterruptedException, IOException {
|
||||||
String key = "hello";
|
String key = "hello";
|
||||||
|
|
||||||
Blob object = context.getBlobStore().newBlob(key);
|
Blob object = context.getBlobStore().newBlob(key);
|
||||||
|
@ -79,7 +82,7 @@ public class BaseContainerIntegrationTest extends BaseBlobStoreIntegrationTest {
|
||||||
// normalize the
|
// normalize the
|
||||||
// providers.
|
// providers.
|
||||||
object.getMetadata().getUserMetadata().put("Adrian", "powderpuff");
|
object.getMetadata().getUserMetadata().put("Adrian", "powderpuff");
|
||||||
object.getMetadata().setContentMD5(context.utils().encryption().md5(toInputStream(TEST_STRING)));
|
Payloads.calculateMD5(object, context.utils().crypto().md5());
|
||||||
String containerName = getContainerName();
|
String containerName = getContainerName();
|
||||||
try {
|
try {
|
||||||
addBlobToContainer(containerName, object);
|
addBlobToContainer(containerName, object);
|
||||||
|
@ -93,7 +96,7 @@ public class BaseContainerIntegrationTest extends BaseBlobStoreIntegrationTest {
|
||||||
assert metadata.getContentType().startsWith("text/plain") : metadata.getContentType();
|
assert metadata.getContentType().startsWith("text/plain") : metadata.getContentType();
|
||||||
assertEquals(metadata.getSize(), new Long(TEST_STRING.length()));
|
assertEquals(metadata.getSize(), new Long(TEST_STRING.length()));
|
||||||
assertEquals(metadata.getUserMetadata().get("adrian"), "powderpuff");
|
assertEquals(metadata.getUserMetadata().get("adrian"), "powderpuff");
|
||||||
assertEquals(metadata.getContentMD5(), context.utils().encryption().md5(toInputStream(TEST_STRING)));
|
assertEquals(metadata.getContentMD5(), CryptoStreams.md5(InputSuppliers.of(TEST_STRING)));
|
||||||
} finally {
|
} finally {
|
||||||
returnContainer(containerName);
|
returnContainer(containerName);
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,7 +104,7 @@ public abstract class BaseMapIntegrationTest<V> extends BaseBlobStoreIntegration
|
||||||
ListContainerOptions options);
|
ListContainerOptions options);
|
||||||
|
|
||||||
@Test(groups = { "integration", "live" })
|
@Test(groups = { "integration", "live" })
|
||||||
public void testClear() throws InterruptedException, ExecutionException, TimeoutException {
|
public void testClear() throws InterruptedException, ExecutionException, TimeoutException, IOException {
|
||||||
String containerNameName = getContainerName();
|
String containerNameName = getContainerName();
|
||||||
try {
|
try {
|
||||||
Map<String, V> map = createMap(context, containerNameName);
|
Map<String, V> map = createMap(context, containerNameName);
|
||||||
|
@ -122,7 +122,7 @@ public abstract class BaseMapIntegrationTest<V> extends BaseBlobStoreIntegration
|
||||||
public abstract void testRemove() throws IOException, InterruptedException, ExecutionException, TimeoutException;
|
public abstract void testRemove() throws IOException, InterruptedException, ExecutionException, TimeoutException;
|
||||||
|
|
||||||
@Test(groups = { "integration", "live" })
|
@Test(groups = { "integration", "live" })
|
||||||
public void testKeySet() throws InterruptedException, ExecutionException, TimeoutException {
|
public void testKeySet() throws InterruptedException, ExecutionException, TimeoutException, IOException {
|
||||||
String containerNameName = getContainerName();
|
String containerNameName = getContainerName();
|
||||||
try {
|
try {
|
||||||
Map<String, V> map = createMap(context, containerNameName);
|
Map<String, V> map = createMap(context, containerNameName);
|
||||||
|
@ -239,7 +239,7 @@ public abstract class BaseMapIntegrationTest<V> extends BaseBlobStoreIntegration
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(groups = { "integration", "live" })
|
@Test(groups = { "integration", "live" })
|
||||||
public void testContainsKey() throws InterruptedException, ExecutionException, TimeoutException {
|
public void testContainsKey() throws InterruptedException, ExecutionException, TimeoutException, IOException {
|
||||||
String containerNameName = getContainerName();
|
String containerNameName = getContainerName();
|
||||||
try {
|
try {
|
||||||
Map<String, V> map = createMap(context, containerNameName);
|
Map<String, V> map = createMap(context, containerNameName);
|
||||||
|
@ -281,7 +281,7 @@ public abstract class BaseMapIntegrationTest<V> extends BaseBlobStoreIntegration
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(groups = { "integration", "live" })
|
@Test(groups = { "integration", "live" })
|
||||||
public void testIsEmpty() throws InterruptedException, ExecutionException, TimeoutException {
|
public void testIsEmpty() throws InterruptedException, ExecutionException, TimeoutException, IOException {
|
||||||
String containerNameName = getContainerName();
|
String containerNameName = getContainerName();
|
||||||
try {
|
try {
|
||||||
Map<String, V> map = createMap(context, containerNameName);
|
Map<String, V> map = createMap(context, containerNameName);
|
||||||
|
@ -310,7 +310,7 @@ public abstract class BaseMapIntegrationTest<V> extends BaseBlobStoreIntegration
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract protected void putStringWithMD5(Map<String, V> map, String key, String value) throws InterruptedException,
|
abstract protected void putStringWithMD5(Map<String, V> map, String key, String value) throws InterruptedException,
|
||||||
ExecutionException, TimeoutException;
|
ExecutionException, TimeoutException, IOException;
|
||||||
|
|
||||||
protected void fourLeftRemovingOne(Map<String, V> map) throws InterruptedException {
|
protected void fourLeftRemovingOne(Map<String, V> map) throws InterruptedException {
|
||||||
map.remove("one");
|
map.remove("one");
|
||||||
|
|
|
@ -29,11 +29,10 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
import javax.ws.rs.core.MediaType;
|
import javax.ws.rs.core.MediaType;
|
||||||
|
|
||||||
import org.jclouds.encryption.EncryptionService;
|
import org.jclouds.crypto.CryptoStreams;
|
||||||
import org.jclouds.http.HttpRequest;
|
import org.jclouds.http.HttpRequest;
|
||||||
import org.jclouds.rest.binders.BindToStringPayload;
|
import org.jclouds.rest.binders.BindToStringPayload;
|
||||||
|
|
||||||
|
@ -46,17 +45,10 @@ import com.google.common.primitives.Bytes;
|
||||||
*/
|
*/
|
||||||
@Singleton
|
@Singleton
|
||||||
public class BindChecksumsToJsonPayload extends BindToStringPayload {
|
public class BindChecksumsToJsonPayload extends BindToStringPayload {
|
||||||
private final EncryptionService encryptionService;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
BindChecksumsToJsonPayload(EncryptionService encryptionService) {
|
|
||||||
this.encryptionService = encryptionService;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public void bindToRequest(HttpRequest request, Object input) {
|
public void bindToRequest(HttpRequest request, Object input) {
|
||||||
checkArgument(checkNotNull(input, "input") instanceof Set,
|
checkArgument(checkNotNull(input, "input") instanceof Set, "this binder is only valid for Set!");
|
||||||
"this binder is only valid for Set!");
|
|
||||||
|
|
||||||
Set<List<Byte>> md5s = (Set<List<Byte>>) input;
|
Set<List<Byte>> md5s = (Set<List<Byte>>) input;
|
||||||
|
|
||||||
|
@ -64,7 +56,7 @@ public class BindChecksumsToJsonPayload extends BindToStringPayload {
|
||||||
builder.append("{\"checksums\":{");
|
builder.append("{\"checksums\":{");
|
||||||
|
|
||||||
for (List<Byte> md5 : md5s)
|
for (List<Byte> md5 : md5s)
|
||||||
builder.append(String.format("\"%s\":null,", encryptionService.hex(Bytes.toArray(md5))));
|
builder.append(String.format("\"%s\":null,", CryptoStreams.hex(Bytes.toArray(md5))));
|
||||||
builder.deleteCharAt(builder.length() - 1);
|
builder.deleteCharAt(builder.length() - 1);
|
||||||
builder.append("}}");
|
builder.append("}}");
|
||||||
super.bindToRequest(request, builder.toString());
|
super.bindToRequest(request, builder.toString());
|
||||||
|
|
|
@ -21,8 +21,9 @@ package org.jclouds.chef.config;
|
||||||
import static org.jclouds.Constants.PROPERTY_CREDENTIAL;
|
import static org.jclouds.Constants.PROPERTY_CREDENTIAL;
|
||||||
import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
|
import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.IOException;
|
||||||
import java.security.PrivateKey;
|
import java.security.PrivateKey;
|
||||||
|
import java.security.spec.InvalidKeySpecException;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
@ -31,15 +32,17 @@ import javax.inject.Singleton;
|
||||||
|
|
||||||
import org.jclouds.chef.handlers.ChefClientErrorRetryHandler;
|
import org.jclouds.chef.handlers.ChefClientErrorRetryHandler;
|
||||||
import org.jclouds.chef.handlers.ChefErrorHandler;
|
import org.jclouds.chef.handlers.ChefErrorHandler;
|
||||||
|
import org.jclouds.crypto.Crypto;
|
||||||
|
import org.jclouds.crypto.Pems;
|
||||||
import org.jclouds.date.DateService;
|
import org.jclouds.date.DateService;
|
||||||
import org.jclouds.date.TimeStamp;
|
import org.jclouds.date.TimeStamp;
|
||||||
import org.jclouds.encryption.EncryptionService;
|
|
||||||
import org.jclouds.http.HttpErrorHandler;
|
import org.jclouds.http.HttpErrorHandler;
|
||||||
import org.jclouds.http.HttpRetryHandler;
|
import org.jclouds.http.HttpRetryHandler;
|
||||||
import org.jclouds.http.RequiresHttp;
|
import org.jclouds.http.RequiresHttp;
|
||||||
import org.jclouds.http.annotation.ClientError;
|
import org.jclouds.http.annotation.ClientError;
|
||||||
import org.jclouds.http.annotation.Redirection;
|
import org.jclouds.http.annotation.Redirection;
|
||||||
import org.jclouds.http.annotation.ServerError;
|
import org.jclouds.http.annotation.ServerError;
|
||||||
|
import org.jclouds.io.InputSuppliers;
|
||||||
import org.jclouds.rest.ConfiguresRestClient;
|
import org.jclouds.rest.ConfiguresRestClient;
|
||||||
import org.jclouds.rest.config.RestClientModule;
|
import org.jclouds.rest.config.RestClientModule;
|
||||||
|
|
||||||
|
@ -61,7 +64,7 @@ public class BaseChefRestClientModule<S, A> extends RestClientModule<S, A> {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected BaseChefRestClientModule(Class<S> syncClientType, Class<A> asyncClientType,
|
protected BaseChefRestClientModule(Class<S> syncClientType, Class<A> asyncClientType,
|
||||||
Map<Class<?>, Class<?>> delegates) {
|
Map<Class<?>, Class<?>> delegates) {
|
||||||
super(syncClientType, asyncClientType, delegates);
|
super(syncClientType, asyncClientType, delegates);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,9 +89,9 @@ public class BaseChefRestClientModule<S, A> extends RestClientModule<S, A> {
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
public PrivateKey provideKey(EncryptionService encryptionService, @Named(PROPERTY_CREDENTIAL) String pem)
|
public PrivateKey provideKey(Crypto crypto, @Named(PROPERTY_CREDENTIAL) String pem) throws InvalidKeySpecException,
|
||||||
throws UnsupportedEncodingException {
|
IOException {
|
||||||
return encryptionService.privateKeyFromPEM(pem.getBytes("UTF-8"));
|
return crypto.rsaKeyFactory().generatePrivate(Pems.privateKeySpec(InputSuppliers.of(pem)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -18,11 +18,14 @@
|
||||||
*/
|
*/
|
||||||
package org.jclouds.chef.config;
|
package org.jclouds.chef.config;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
import java.security.PrivateKey;
|
import java.security.PrivateKey;
|
||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
|
import java.security.cert.CertificateException;
|
||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
|
import java.security.spec.InvalidKeySpecException;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
@ -31,7 +34,9 @@ import javax.inject.Singleton;
|
||||||
|
|
||||||
import org.jclouds.Constants;
|
import org.jclouds.Constants;
|
||||||
import org.jclouds.chef.domain.DataBagItem;
|
import org.jclouds.chef.domain.DataBagItem;
|
||||||
import org.jclouds.encryption.EncryptionService;
|
import org.jclouds.crypto.Crypto;
|
||||||
|
import org.jclouds.crypto.Pems;
|
||||||
|
import org.jclouds.io.InputSuppliers;
|
||||||
import org.jclouds.json.config.GsonModule.DateAdapter;
|
import org.jclouds.json.config.GsonModule.DateAdapter;
|
||||||
import org.jclouds.json.config.GsonModule.Iso8601DateAdapter;
|
import org.jclouds.json.config.GsonModule.Iso8601DateAdapter;
|
||||||
|
|
||||||
|
@ -61,11 +66,11 @@ public class ChefParserModule extends AbstractModule {
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
public static class PrivateKeyAdapterImpl implements PrivateKeyAdapter {
|
public static class PrivateKeyAdapterImpl implements PrivateKeyAdapter {
|
||||||
private final EncryptionService encryptionService;
|
private final Crypto crypto;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
PrivateKeyAdapterImpl(EncryptionService encryptionService) {
|
PrivateKeyAdapterImpl(Crypto crypto) {
|
||||||
this.encryptionService = encryptionService;
|
this.crypto = crypto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -73,10 +78,16 @@ public class ChefParserModule extends AbstractModule {
|
||||||
throws JsonParseException {
|
throws JsonParseException {
|
||||||
String keyText = json.getAsString().replaceAll("\\n", "\n");
|
String keyText = json.getAsString().replaceAll("\\n", "\n");
|
||||||
try {
|
try {
|
||||||
return encryptionService.privateKeyFromPEM(keyText.getBytes("UTF-8"));
|
return crypto.rsaKeyFactory().generatePrivate(Pems.privateKeySpec(InputSuppliers.of(keyText)));
|
||||||
} catch (UnsupportedEncodingException e) {
|
} catch (UnsupportedEncodingException e) {
|
||||||
Throwables.propagate(e);
|
Throwables.propagate(e);
|
||||||
return null;
|
return null;
|
||||||
|
} catch (InvalidKeySpecException e) {
|
||||||
|
Throwables.propagate(e);
|
||||||
|
return null;
|
||||||
|
} catch (IOException e) {
|
||||||
|
Throwables.propagate(e);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,11 +99,11 @@ public class ChefParserModule extends AbstractModule {
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
public static class PublicKeyAdapterImpl implements PublicKeyAdapter {
|
public static class PublicKeyAdapterImpl implements PublicKeyAdapter {
|
||||||
private final EncryptionService encryptionService;
|
private final Crypto crypto;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
PublicKeyAdapterImpl(EncryptionService encryptionService) {
|
PublicKeyAdapterImpl(Crypto crypto) {
|
||||||
this.encryptionService = encryptionService;
|
this.crypto = crypto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -100,10 +111,16 @@ public class ChefParserModule extends AbstractModule {
|
||||||
throws JsonParseException {
|
throws JsonParseException {
|
||||||
String keyText = json.getAsString().replaceAll("\\n", "\n");
|
String keyText = json.getAsString().replaceAll("\\n", "\n");
|
||||||
try {
|
try {
|
||||||
return encryptionService.publicKeyFromPEM(keyText.getBytes("UTF-8"));
|
return crypto.rsaKeyFactory().generatePublic(Pems.publicKeySpec(InputSuppliers.of(keyText)));
|
||||||
} catch (UnsupportedEncodingException e) {
|
} catch (UnsupportedEncodingException e) {
|
||||||
Throwables.propagate(e);
|
Throwables.propagate(e);
|
||||||
return null;
|
return null;
|
||||||
|
} catch (InvalidKeySpecException e) {
|
||||||
|
Throwables.propagate(e);
|
||||||
|
return null;
|
||||||
|
} catch (IOException e) {
|
||||||
|
Throwables.propagate(e);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -115,11 +132,11 @@ public class ChefParserModule extends AbstractModule {
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
public static class X509CertificateAdapterImpl implements X509CertificateAdapter {
|
public static class X509CertificateAdapterImpl implements X509CertificateAdapter {
|
||||||
private final EncryptionService encryptionService;
|
private final Crypto crypto;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
X509CertificateAdapterImpl(EncryptionService encryptionService) {
|
X509CertificateAdapterImpl(Crypto crypto) {
|
||||||
this.encryptionService = encryptionService;
|
this.crypto = crypto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -127,10 +144,16 @@ public class ChefParserModule extends AbstractModule {
|
||||||
throws JsonParseException {
|
throws JsonParseException {
|
||||||
String keyText = json.getAsString().replaceAll("\\n", "\n");
|
String keyText = json.getAsString().replaceAll("\\n", "\n");
|
||||||
try {
|
try {
|
||||||
return encryptionService.x509CertificateFromPEM(keyText.getBytes("UTF-8"));
|
return Pems.x509Certificate(InputSuppliers.of(keyText), crypto.certFactory());
|
||||||
} catch (UnsupportedEncodingException e) {
|
} catch (UnsupportedEncodingException e) {
|
||||||
Throwables.propagate(e);
|
Throwables.propagate(e);
|
||||||
return null;
|
return null;
|
||||||
|
} catch (IOException e) {
|
||||||
|
Throwables.propagate(e);
|
||||||
|
return null;
|
||||||
|
} catch (CertificateException e) {
|
||||||
|
Throwables.propagate(e);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,17 +37,20 @@ import javax.inject.Provider;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
import org.jclouds.Constants;
|
import org.jclouds.Constants;
|
||||||
|
import org.jclouds.crypto.Crypto;
|
||||||
|
import org.jclouds.crypto.CryptoStreams;
|
||||||
import org.jclouds.date.TimeStamp;
|
import org.jclouds.date.TimeStamp;
|
||||||
import org.jclouds.encryption.EncryptionService;
|
|
||||||
import org.jclouds.http.HttpException;
|
import org.jclouds.http.HttpException;
|
||||||
import org.jclouds.http.HttpRequest;
|
import org.jclouds.http.HttpRequest;
|
||||||
import org.jclouds.http.HttpRequestFilter;
|
import org.jclouds.http.HttpRequestFilter;
|
||||||
import org.jclouds.http.HttpUtils;
|
import org.jclouds.http.HttpUtils;
|
||||||
import org.jclouds.http.internal.SignatureWire;
|
import org.jclouds.http.internal.SignatureWire;
|
||||||
|
import org.jclouds.io.InputSuppliers;
|
||||||
import org.jclouds.io.Payload;
|
import org.jclouds.io.Payload;
|
||||||
import org.jclouds.io.Payloads;
|
import org.jclouds.io.Payloads;
|
||||||
import org.jclouds.io.payloads.MultipartForm;
|
import org.jclouds.io.payloads.MultipartForm;
|
||||||
import org.jclouds.io.payloads.Part;
|
import org.jclouds.io.payloads.Part;
|
||||||
|
import org.jclouds.io.payloads.RSAEncryptingPayload;
|
||||||
import org.jclouds.logging.Logger;
|
import org.jclouds.logging.Logger;
|
||||||
import org.jclouds.util.Utils;
|
import org.jclouds.util.Utils;
|
||||||
|
|
||||||
|
@ -56,6 +59,7 @@ import com.google.common.base.Predicate;
|
||||||
import com.google.common.base.Splitter;
|
import com.google.common.base.Splitter;
|
||||||
import com.google.common.base.Throwables;
|
import com.google.common.base.Throwables;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
|
import com.google.common.io.ByteStreams;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ported from mixlib-authentication in order to sign Chef requests.
|
* Ported from mixlib-authentication in order to sign Chef requests.
|
||||||
|
@ -72,7 +76,7 @@ public class SignedHeaderAuth implements HttpRequestFilter {
|
||||||
private final String userId;
|
private final String userId;
|
||||||
private final PrivateKey privateKey;
|
private final PrivateKey privateKey;
|
||||||
private final Provider<String> timeStampProvider;
|
private final Provider<String> timeStampProvider;
|
||||||
private final EncryptionService encryptionService;
|
private final Crypto crypto;
|
||||||
private final String emptyStringHash;
|
private final String emptyStringHash;
|
||||||
private final HttpUtils utils;
|
private final HttpUtils utils;
|
||||||
|
|
||||||
|
@ -82,12 +86,12 @@ public class SignedHeaderAuth implements HttpRequestFilter {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public SignedHeaderAuth(SignatureWire signatureWire, @Named(PROPERTY_IDENTITY) String userId, PrivateKey privateKey,
|
public SignedHeaderAuth(SignatureWire signatureWire, @Named(PROPERTY_IDENTITY) String userId, PrivateKey privateKey,
|
||||||
@TimeStamp Provider<String> timeStampProvider, EncryptionService encryptionService, HttpUtils utils) {
|
@TimeStamp Provider<String> timeStampProvider, Crypto crypto, HttpUtils utils) {
|
||||||
this.signatureWire = signatureWire;
|
this.signatureWire = signatureWire;
|
||||||
this.userId = userId;
|
this.userId = userId;
|
||||||
this.privateKey = privateKey;
|
this.privateKey = privateKey;
|
||||||
this.timeStampProvider = timeStampProvider;
|
this.timeStampProvider = timeStampProvider;
|
||||||
this.encryptionService = encryptionService;
|
this.crypto = crypto;
|
||||||
this.emptyStringHash = hashBody(Payloads.newStringPayload(""));
|
this.emptyStringHash = hashBody(Payloads.newStringPayload(""));
|
||||||
this.utils = utils;
|
this.utils = utils;
|
||||||
}
|
}
|
||||||
|
@ -129,7 +133,7 @@ public class SignedHeaderAuth implements HttpRequestFilter {
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
String hashPath(String path) {
|
String hashPath(String path) {
|
||||||
try {
|
try {
|
||||||
return encryptionService.base64(encryptionService.sha1(Utils.toInputStream(canonicalPath(path))));
|
return CryptoStreams.base64(CryptoStreams.digest(InputSuppliers.of(canonicalPath(path)), crypto.sha1()));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Throwables.propagateIfPossible(e);
|
Throwables.propagateIfPossible(e);
|
||||||
throw new HttpException("error creating sigature for path: " + path, e);
|
throw new HttpException("error creating sigature for path: " + path, e);
|
||||||
|
@ -154,7 +158,7 @@ public class SignedHeaderAuth implements HttpRequestFilter {
|
||||||
checkArgument(payload != null, "payload was null");
|
checkArgument(payload != null, "payload was null");
|
||||||
checkArgument(payload.isRepeatable(), "payload must be repeatable: " + payload);
|
checkArgument(payload.isRepeatable(), "payload must be repeatable: " + payload);
|
||||||
try {
|
try {
|
||||||
return encryptionService.base64(encryptionService.sha1(payload.getInput()));
|
return CryptoStreams.base64(CryptoStreams.digest(payload, crypto.sha1()));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Throwables.propagateIfPossible(e);
|
Throwables.propagateIfPossible(e);
|
||||||
throw new HttpException("error creating sigature for payload: " + payload, e);
|
throw new HttpException("error creating sigature for payload: " + payload, e);
|
||||||
|
@ -182,8 +186,9 @@ public class SignedHeaderAuth implements HttpRequestFilter {
|
||||||
|
|
||||||
public String sign(String toSign) {
|
public String sign(String toSign) {
|
||||||
try {
|
try {
|
||||||
byte[] encrypted = encryptionService.rsaEncrypt(Payloads.newStringPayload(toSign), privateKey);
|
byte[] encrypted = ByteStreams.toByteArray(new RSAEncryptingPayload(Payloads.newStringPayload(toSign),
|
||||||
return encryptionService.base64(encrypted);
|
privateKey));
|
||||||
|
return CryptoStreams.base64(encrypted);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new HttpException("error signing request", e);
|
throw new HttpException("error signing request", e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
package org.jclouds.chef.strategy.internal;
|
package org.jclouds.chef.strategy.internal;
|
||||||
|
|
||||||
import static com.google.common.collect.Maps.newHashMap;
|
import static com.google.common.collect.Maps.newHashMap;
|
||||||
import static org.jclouds.concurrent.ConcurrentUtils.awaitCompletion;
|
import static org.jclouds.concurrent.FutureIterables.awaitCompletion;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
package org.jclouds.chef.strategy.internal;
|
package org.jclouds.chef.strategy.internal;
|
||||||
|
|
||||||
import static com.google.common.collect.Iterables.filter;
|
import static com.google.common.collect.Iterables.filter;
|
||||||
import static org.jclouds.concurrent.ConcurrentUtils.transformParallel;
|
import static org.jclouds.concurrent.FutureIterables.transformParallel;
|
||||||
|
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
*/
|
*/
|
||||||
package org.jclouds.ohai.functions;
|
package org.jclouds.ohai.functions;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
|
||||||
import static com.google.common.collect.Iterables.transform;
|
import static com.google.common.collect.Iterables.transform;
|
||||||
import static com.google.common.collect.Lists.partition;
|
import static com.google.common.collect.Lists.partition;
|
||||||
import static com.google.common.primitives.Bytes.asList;
|
import static com.google.common.primitives.Bytes.asList;
|
||||||
|
@ -26,10 +25,9 @@ import static com.google.common.primitives.Bytes.toArray;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
import org.jclouds.encryption.EncryptionService;
|
import org.jclouds.crypto.CryptoStreams;
|
||||||
|
|
||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
import com.google.common.base.Joiner;
|
import com.google.common.base.Joiner;
|
||||||
|
@ -42,12 +40,6 @@ import com.google.common.base.Joiner;
|
||||||
*/
|
*/
|
||||||
@Singleton
|
@Singleton
|
||||||
public class ByteArrayToMacAddress implements Function<byte[], String> {
|
public class ByteArrayToMacAddress implements Function<byte[], String> {
|
||||||
private final EncryptionService encryptionService;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
ByteArrayToMacAddress(EncryptionService encryptionService) {
|
|
||||||
this.encryptionService = checkNotNull(encryptionService, "encryptionService");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String apply(byte[] from) {
|
public String apply(byte[] from) {
|
||||||
|
@ -55,7 +47,7 @@ public class ByteArrayToMacAddress implements Function<byte[], String> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String apply(List<Byte> from) {
|
public String apply(List<Byte> from) {
|
||||||
return encryptionService.hex(toArray(from));
|
return CryptoStreams.hex(toArray(from));
|
||||||
}
|
}
|
||||||
|
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -37,8 +37,8 @@ import org.jclouds.chef.domain.Role;
|
||||||
import org.jclouds.chef.filters.SignedHeaderAuth;
|
import org.jclouds.chef.filters.SignedHeaderAuth;
|
||||||
import org.jclouds.chef.filters.SignedHeaderAuthTest;
|
import org.jclouds.chef.filters.SignedHeaderAuthTest;
|
||||||
import org.jclouds.chef.functions.ParseKeySetFromJson;
|
import org.jclouds.chef.functions.ParseKeySetFromJson;
|
||||||
|
import org.jclouds.crypto.CryptoStreams;
|
||||||
import org.jclouds.date.TimeStamp;
|
import org.jclouds.date.TimeStamp;
|
||||||
import org.jclouds.encryption.EncryptionService;
|
|
||||||
import org.jclouds.http.HttpRequest;
|
import org.jclouds.http.HttpRequest;
|
||||||
import org.jclouds.http.RequiresHttp;
|
import org.jclouds.http.RequiresHttp;
|
||||||
import org.jclouds.http.functions.ParseJson;
|
import org.jclouds.http.functions.ParseJson;
|
||||||
|
@ -88,12 +88,11 @@ public class ChefAsyncClientTest extends RestClientTest<ChefAsyncClient> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testGetUploadSandboxForChecksums() throws SecurityException, NoSuchMethodException, IOException {
|
public void testGetUploadSandboxForChecksums() throws SecurityException, NoSuchMethodException, IOException {
|
||||||
EncryptionService encryptionService = injector.getInstance(EncryptionService.class);
|
|
||||||
Method method = ChefAsyncClient.class.getMethod("getUploadSandboxForChecksums", Set.class);
|
Method method = ChefAsyncClient.class.getMethod("getUploadSandboxForChecksums", Set.class);
|
||||||
GeneratedHttpRequest<ChefAsyncClient> httpRequest = processor.createRequest(method, ImmutableSet.of(Bytes
|
GeneratedHttpRequest<ChefAsyncClient> httpRequest = processor.createRequest(method, ImmutableSet.of(Bytes
|
||||||
.asList(encryptionService.fromHex("0189e76ccc476701d6b374e5a1a27347")), Bytes.asList(encryptionService
|
.asList(CryptoStreams.hex("0189e76ccc476701d6b374e5a1a27347")), Bytes.asList(CryptoStreams
|
||||||
.fromHex("0c5ecd7788cf4f6c7de2a57193897a6c")), Bytes.asList(encryptionService
|
.hex("0c5ecd7788cf4f6c7de2a57193897a6c")), Bytes.asList(CryptoStreams
|
||||||
.fromHex("1dda05ed139664f1f89b9dec482b77c0"))));
|
.hex("1dda05ed139664f1f89b9dec482b77c0"))));
|
||||||
assertRequestLineEquals(httpRequest, "POST http://localhost:4000/sandboxes HTTP/1.1");
|
assertRequestLineEquals(httpRequest, "POST http://localhost:4000/sandboxes HTTP/1.1");
|
||||||
assertNonPayloadHeadersEqual(httpRequest, "Accept: application/json\nX-Chef-Version: 0.9.8\n");
|
assertNonPayloadHeadersEqual(httpRequest, "Accept: application/json\nX-Chef-Version: 0.9.8\n");
|
||||||
assertPayloadEquals(
|
assertPayloadEquals(
|
||||||
|
|
|
@ -41,6 +41,9 @@ import org.jclouds.chef.domain.Node;
|
||||||
import org.jclouds.chef.domain.Resource;
|
import org.jclouds.chef.domain.Resource;
|
||||||
import org.jclouds.chef.domain.Role;
|
import org.jclouds.chef.domain.Role;
|
||||||
import org.jclouds.chef.domain.UploadSandbox;
|
import org.jclouds.chef.domain.UploadSandbox;
|
||||||
|
import org.jclouds.crypto.CryptoStreams;
|
||||||
|
import org.jclouds.crypto.Pems;
|
||||||
|
import org.jclouds.io.InputSuppliers;
|
||||||
import org.jclouds.io.Payloads;
|
import org.jclouds.io.Payloads;
|
||||||
import org.jclouds.io.payloads.FilePayload;
|
import org.jclouds.io.payloads.FilePayload;
|
||||||
import org.jclouds.logging.log4j.config.Log4JLoggingModule;
|
import org.jclouds.logging.log4j.config.Log4JLoggingModule;
|
||||||
|
@ -108,7 +111,7 @@ public class ChefClientLiveTest {
|
||||||
content.setContentType("application/x-binary");
|
content.setContentType("application/x-binary");
|
||||||
|
|
||||||
// get an md5 so that you can see if the server already has it or not
|
// get an md5 so that you can see if the server already has it or not
|
||||||
adminConnection.utils().encryption().generateMD5BufferingIfNotRepeatable(content);
|
Payloads.calculateMD5(content);
|
||||||
|
|
||||||
// Note that java collections cannot effectively do equals or hashcodes on
|
// Note that java collections cannot effectively do equals or hashcodes on
|
||||||
// byte arrays,
|
// byte arrays,
|
||||||
|
@ -142,8 +145,7 @@ public class ChefClientLiveTest {
|
||||||
|
|
||||||
@Test(dependsOnMethods = "testCreateClient")
|
@Test(dependsOnMethods = "testCreateClient")
|
||||||
public void testGenerateKeyForClient() throws Exception {
|
public void testGenerateKeyForClient() throws Exception {
|
||||||
clientKey = validatorConnection.utils().encryption().toPem(
|
clientKey = Pems.pem(validatorConnection.getApi().generateKeyForClient(PREFIX).getPrivateKey());
|
||||||
validatorConnection.getApi().generateKeyForClient(PREFIX).getPrivateKey());
|
|
||||||
|
|
||||||
assertNotNull(clientKey);
|
assertNotNull(clientKey);
|
||||||
clientConnection.close();
|
clientConnection.close();
|
||||||
|
@ -163,7 +165,7 @@ public class ChefClientLiveTest {
|
||||||
cookbookO.getTemplates()).build()) {
|
cookbookO.getTemplates()).build()) {
|
||||||
try {
|
try {
|
||||||
InputStream stream = adminConnection.utils().http().get(resource.getUrl());
|
InputStream stream = adminConnection.utils().http().get(resource.getUrl());
|
||||||
byte[] md5 = adminConnection.utils().encryption().md5(stream);
|
byte[] md5 = CryptoStreams.md5(InputSuppliers.of(stream));
|
||||||
assertEquals(md5, resource.getChecksum());
|
assertEquals(md5, resource.getChecksum());
|
||||||
} catch (NullPointerException e) {
|
} catch (NullPointerException e) {
|
||||||
assert false : "resource not found: " + resource;
|
assert false : "resource not found: " + resource;
|
||||||
|
@ -205,8 +207,7 @@ public class ChefClientLiveTest {
|
||||||
public void testCreateClient() throws Exception {
|
public void testCreateClient() throws Exception {
|
||||||
validatorConnection.getApi().deleteClient(PREFIX);
|
validatorConnection.getApi().deleteClient(PREFIX);
|
||||||
|
|
||||||
clientKey = validatorConnection.utils().encryption().toPem(
|
clientKey = Pems.pem(validatorConnection.getApi().createClient(PREFIX).getPrivateKey());
|
||||||
validatorConnection.getApi().createClient(PREFIX).getPrivateKey());
|
|
||||||
|
|
||||||
System.out.println(clientKey);
|
System.out.println(clientKey);
|
||||||
assertNotNull(clientKey);
|
assertNotNull(clientKey);
|
||||||
|
|
|
@ -31,7 +31,7 @@ import java.net.URI;
|
||||||
import javax.ws.rs.HttpMethod;
|
import javax.ws.rs.HttpMethod;
|
||||||
|
|
||||||
import org.jclouds.chef.config.ChefParserModule;
|
import org.jclouds.chef.config.ChefParserModule;
|
||||||
import org.jclouds.encryption.EncryptionService;
|
import org.jclouds.crypto.CryptoStreams;
|
||||||
import org.jclouds.http.HttpRequest;
|
import org.jclouds.http.HttpRequest;
|
||||||
import org.jclouds.json.config.GsonModule;
|
import org.jclouds.json.config.GsonModule;
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
|
@ -58,8 +58,7 @@ public class BindHexEncodedMD5sToJsonPayloadTest {
|
||||||
@Test(enabled = false)
|
@Test(enabled = false)
|
||||||
public void testCorrect() {
|
public void testCorrect() {
|
||||||
HttpRequest request = new HttpRequest(HttpMethod.POST, URI.create("http://localhost"));
|
HttpRequest request = new HttpRequest(HttpMethod.POST, URI.create("http://localhost"));
|
||||||
binder.bindToRequest(request, ImmutableSet.of(injector.getInstance(EncryptionService.class).fromHex("abddef"),
|
binder.bindToRequest(request, ImmutableSet.of(CryptoStreams.hex("abddef"), CryptoStreams.hex("1234")));
|
||||||
injector.getInstance(EncryptionService.class).fromHex("1234")));
|
|
||||||
assertEquals(request.getPayload().getRawContent(), "{\"checksums\":{\"abddef\":null,\"1234\":null}}");
|
assertEquals(request.getPayload().getRawContent(), "{\"checksums\":{\"abddef\":null,\"1234\":null}}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@ import javax.inject.Provider;
|
||||||
import javax.ws.rs.HttpMethod;
|
import javax.ws.rs.HttpMethod;
|
||||||
import javax.ws.rs.core.HttpHeaders;
|
import javax.ws.rs.core.HttpHeaders;
|
||||||
|
|
||||||
import org.jclouds.encryption.EncryptionService;
|
import org.jclouds.crypto.Crypto;
|
||||||
import org.jclouds.http.HttpRequest;
|
import org.jclouds.http.HttpRequest;
|
||||||
import org.jclouds.http.HttpUtils;
|
import org.jclouds.http.HttpUtils;
|
||||||
import org.jclouds.http.internal.SignatureWire;
|
import org.jclouds.http.internal.SignatureWire;
|
||||||
|
@ -173,7 +173,7 @@ public class SignedHeaderAuthTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
private SignedHeaderAuth signing_obj;
|
private SignedHeaderAuth signing_obj;
|
||||||
private EncryptionService encryptionService;
|
private Crypto crypto;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* before class, as we need to ensure that the filter is threadsafe.
|
* before class, as we need to ensure that the filter is threadsafe.
|
||||||
|
@ -187,7 +187,7 @@ public class SignedHeaderAuthTest {
|
||||||
Injector injector = new RestContextFactory().createContextBuilder("chef", USER_ID, PRIVATE_KEY,
|
Injector injector = new RestContextFactory().createContextBuilder("chef", USER_ID, PRIVATE_KEY,
|
||||||
ImmutableSet.<Module> of(new MockModule(), new NullLoggingModule()), new Properties()).buildInjector();
|
ImmutableSet.<Module> of(new MockModule(), new NullLoggingModule()), new Properties()).buildInjector();
|
||||||
|
|
||||||
encryptionService = injector.getInstance(EncryptionService.class);
|
crypto = injector.getInstance(Crypto.class);
|
||||||
HttpUtils utils = injector.getInstance(HttpUtils.class);
|
HttpUtils utils = injector.getInstance(HttpUtils.class);
|
||||||
|
|
||||||
PrivateKey privateKey = injector.getInstance(PrivateKey.class);
|
PrivateKey privateKey = injector.getInstance(PrivateKey.class);
|
||||||
|
@ -199,7 +199,7 @@ public class SignedHeaderAuthTest {
|
||||||
return TIMESTAMP_ISO8601;
|
return TIMESTAMP_ISO8601;
|
||||||
}
|
}
|
||||||
|
|
||||||
}, encryptionService, utils);
|
}, crypto, utils);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -5,18 +5,24 @@ import static org.testng.Assert.assertEquals;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.security.PrivateKey;
|
import java.security.PrivateKey;
|
||||||
|
import java.security.cert.CertificateException;
|
||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
|
import java.security.spec.InvalidKeySpecException;
|
||||||
|
|
||||||
import org.jclouds.chef.config.ChefParserModule;
|
import org.jclouds.chef.config.ChefParserModule;
|
||||||
import org.jclouds.chef.domain.Client;
|
import org.jclouds.chef.domain.Client;
|
||||||
import org.jclouds.encryption.EncryptionService;
|
import org.jclouds.crypto.Crypto;
|
||||||
|
import org.jclouds.crypto.Pems;
|
||||||
import org.jclouds.http.HttpResponse;
|
import org.jclouds.http.HttpResponse;
|
||||||
import org.jclouds.http.functions.ParseJson;
|
import org.jclouds.http.functions.ParseJson;
|
||||||
import org.jclouds.io.Payloads;
|
import org.jclouds.io.Payloads;
|
||||||
|
import org.jclouds.io.payloads.RSADecryptingPayload;
|
||||||
|
import org.jclouds.io.payloads.RSAEncryptingPayload;
|
||||||
import org.jclouds.json.config.GsonModule;
|
import org.jclouds.json.config.GsonModule;
|
||||||
import org.testng.annotations.BeforeTest;
|
import org.testng.annotations.BeforeTest;
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
|
import com.google.common.io.ByteStreams;
|
||||||
import com.google.inject.Guice;
|
import com.google.inject.Guice;
|
||||||
import com.google.inject.Injector;
|
import com.google.inject.Injector;
|
||||||
import com.google.inject.Key;
|
import com.google.inject.Key;
|
||||||
|
@ -33,30 +39,30 @@ public class ParseClientFromJsonTest {
|
||||||
private static final String PRIVATE_KEY = "-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEAyb2ZJJqGm0KKR+8nfQJNsSd+F9tXNMV7CfOcW6jsqs8EZgiV\nR09hD1IYOj4YqM0qJONlgyg4xRWewdSG7QTPj1lJpVAida9sXy2+kzyagZA1Am0O\nZcbqb5hoeIDgcX+eDa79s0u0DomjcfO9EKhvHLBz+zM+3QqPRkPV8nYTbfs+HjVz\nzOU6D1B0XR3+IPZZl2AnWs2d0qhnStHcDUvnRVQ0P482YwN9VgceOZtpPz0DCKEJ\n5Tx5STub8k0/zt/VAMHQafLSuQMLd2s4ZLuOZptN//uAsTmxireqd37z+8ZTdBbJ\n8LEpJ+iCXuSfm5aUh7iw6oxvToY2AL53+jK2UQIDAQABAoIBAQDA88B3i/xWn0vX\nBVxFamCYoecuNjGwXXkSyZew616A+EOCu47bh4aTurdFbYL0YFaAtaWvzlaN2eHg\nDb+HDuTefE29+WkcGk6SshPmiz5T0XOCAICWw6wSVDkHmGwS4jZvbAFm7W8nwGk9\nYhxgxFiRngswJZFopOLoF5WXs2td8guIYNslMpo7tu50iFnBHwKO2ZsPAk8t9nnS\nxlDavKruymEmqHCr3+dtio5eaenJcp3fjoXBQOKUk3ipII29XRB8NqeCVV/7Kxwq\nckqOBEbRwBclckyIbD+RiAgKvOelORjEiE9R42vuqvxRA6k9kd9o7utlX0AUtpEn\n3gZc6LepAoGBAP9ael5Y75+sK2JJUNOOhO8ae45cdsilp2yI0X+UBaSuQs2+dyPp\nkpEHAxd4pmmSvn/8c9TlEZhr+qYbABXVPlDncxpIuw2Ajbk7s/S4XaSKsRqpXL57\nzj/QOqLkRk8+OVV9q6lMeQNqLtEj1u6JPviX70Ro+FQtRttNOYbfdP/fAoGBAMpA\nXjR5woV5sUb+REg9vEuYo8RSyOarxqKFCIXVUNsLOx+22+AK4+CQpbueWN7jotrl\nYD6uT6svWi3AAC7kiY0UI/fjVPRCUi8tVoQUE0TaU5VLITaYOB+W/bBaDE4M9560\n1NuDWO90baA5dfU44iuzva02rGJXK9+nS3o8nk/PAoGBALOL6djnDe4mwAaG6Jco\ncd4xr8jkyPzCRZuyBCSBbwphIUXLc7hDprPky064ncJD1UDmwIdkXd/fpMkg2QmA\n/CUk6LEFjMisqHojOaCL9gQZJPhLN5QUN2x1PJWGjs1vQh8Tkx0iUUCOa8bQPXNR\n+34OTsW6TUna4CSZAycLfhffAoGBAIggVsefBCvuQkF0NeUhmDCRZfhnd8y55RHR\n1HCvqKIlpv+rhcX/zmyBLuteopYyRJRsOiE2FW00i8+rIPRu4Z3Q5nybx7w3PzV9\noHN5R5baE9OyI4KpZWztpYYitZF67NcnAvVULHHOvVJQGnKYfLHJYmrJF7GA1ojM\nAuMdFbjFAoGAPxUhxwFy8gaqBahKUEZn4F81HFP5ihGhkT4QL6AFPO2e+JhIGjuR\n27+85hcFqQ+HHVtFsm81b/a+R7P4UuCRgc8eCjxQMoJ1Xl4n7VbjPbHMnIN0Ryvd\nO4ZpWDWYnCO021JTOUUOJ4J/y0416Bvkw0z59y7sNX7wDBBHHbK/XCc=\n-----END RSA PRIVATE KEY-----\n";
|
private static final String PRIVATE_KEY = "-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEAyb2ZJJqGm0KKR+8nfQJNsSd+F9tXNMV7CfOcW6jsqs8EZgiV\nR09hD1IYOj4YqM0qJONlgyg4xRWewdSG7QTPj1lJpVAida9sXy2+kzyagZA1Am0O\nZcbqb5hoeIDgcX+eDa79s0u0DomjcfO9EKhvHLBz+zM+3QqPRkPV8nYTbfs+HjVz\nzOU6D1B0XR3+IPZZl2AnWs2d0qhnStHcDUvnRVQ0P482YwN9VgceOZtpPz0DCKEJ\n5Tx5STub8k0/zt/VAMHQafLSuQMLd2s4ZLuOZptN//uAsTmxireqd37z+8ZTdBbJ\n8LEpJ+iCXuSfm5aUh7iw6oxvToY2AL53+jK2UQIDAQABAoIBAQDA88B3i/xWn0vX\nBVxFamCYoecuNjGwXXkSyZew616A+EOCu47bh4aTurdFbYL0YFaAtaWvzlaN2eHg\nDb+HDuTefE29+WkcGk6SshPmiz5T0XOCAICWw6wSVDkHmGwS4jZvbAFm7W8nwGk9\nYhxgxFiRngswJZFopOLoF5WXs2td8guIYNslMpo7tu50iFnBHwKO2ZsPAk8t9nnS\nxlDavKruymEmqHCr3+dtio5eaenJcp3fjoXBQOKUk3ipII29XRB8NqeCVV/7Kxwq\nckqOBEbRwBclckyIbD+RiAgKvOelORjEiE9R42vuqvxRA6k9kd9o7utlX0AUtpEn\n3gZc6LepAoGBAP9ael5Y75+sK2JJUNOOhO8ae45cdsilp2yI0X+UBaSuQs2+dyPp\nkpEHAxd4pmmSvn/8c9TlEZhr+qYbABXVPlDncxpIuw2Ajbk7s/S4XaSKsRqpXL57\nzj/QOqLkRk8+OVV9q6lMeQNqLtEj1u6JPviX70Ro+FQtRttNOYbfdP/fAoGBAMpA\nXjR5woV5sUb+REg9vEuYo8RSyOarxqKFCIXVUNsLOx+22+AK4+CQpbueWN7jotrl\nYD6uT6svWi3AAC7kiY0UI/fjVPRCUi8tVoQUE0TaU5VLITaYOB+W/bBaDE4M9560\n1NuDWO90baA5dfU44iuzva02rGJXK9+nS3o8nk/PAoGBALOL6djnDe4mwAaG6Jco\ncd4xr8jkyPzCRZuyBCSBbwphIUXLc7hDprPky064ncJD1UDmwIdkXd/fpMkg2QmA\n/CUk6LEFjMisqHojOaCL9gQZJPhLN5QUN2x1PJWGjs1vQh8Tkx0iUUCOa8bQPXNR\n+34OTsW6TUna4CSZAycLfhffAoGBAIggVsefBCvuQkF0NeUhmDCRZfhnd8y55RHR\n1HCvqKIlpv+rhcX/zmyBLuteopYyRJRsOiE2FW00i8+rIPRu4Z3Q5nybx7w3PzV9\noHN5R5baE9OyI4KpZWztpYYitZF67NcnAvVULHHOvVJQGnKYfLHJYmrJF7GA1ojM\nAuMdFbjFAoGAPxUhxwFy8gaqBahKUEZn4F81HFP5ihGhkT4QL6AFPO2e+JhIGjuR\n27+85hcFqQ+HHVtFsm81b/a+R7P4UuCRgc8eCjxQMoJ1Xl4n7VbjPbHMnIN0Ryvd\nO4ZpWDWYnCO021JTOUUOJ4J/y0416Bvkw0z59y7sNX7wDBBHHbK/XCc=\n-----END RSA PRIVATE KEY-----\n";
|
||||||
private static final String CERTIFICATE = "-----BEGIN CERTIFICATE-----\nMIIClzCCAgCgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBnjELMAkGA1UEBhMCVVMx\nEzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcMB1NlYXR0bGUxFjAUBgNVBAoM\nDU9wc2NvZGUsIEluYy4xHDAaBgNVBAsME0NlcnRpZmljYXRlIFNlcnZpY2UxMjAw\nBgNVBAMMKW9wc2NvZGUuY29tL2VtYWlsQWRkcmVzcz1hdXRoQG9wc2NvZGUuY29t\nMB4XDTEwMDczMDIwNDEzMFoXDTIwMDcyNzIwNDEzMFowADCCASIwDQYJKoZIhvcN\nAQEBBQADggEPADCCAQoCggEBAMm9mSSahptCikfvJ30CTbEnfhfbVzTFewnznFuo\n7KrPBGYIlUdPYQ9SGDo+GKjNKiTjZYMoOMUVnsHUhu0Ez49ZSaVQInWvbF8tvpM8\nmoGQNQJtDmXG6m+YaHiA4HF/ng2u/bNLtA6Jo3HzvRCobxywc/szPt0Kj0ZD1fJ2\nE237Ph41c8zlOg9QdF0d/iD2WZdgJ1rNndKoZ0rR3A1L50VUND+PNmMDfVYHHjmb\naT89AwihCeU8eUk7m/JNP87f1QDB0Gny0rkDC3drOGS7jmabTf/7gLE5sYq3qnd+\n8/vGU3QWyfCxKSfogl7kn5uWlIe4sOqMb06GNgC+d/oytlECAwEAATANBgkqhkiG\n9w0BAQUFAAOBgQBftzSZxstWw60GqRTDNN/F2GnrdtnKBoXzHww3r6jtGEylYq20\n5KfKpEx+sPX0gyZuYJiXC2CkEjImAluWKcdN9ZF6VD541sheAjbiaU7q7ZsztTxF\nWUH2tCvHeDXYKPKek3QzL7bYpUhLnCN/XxEv6ibeMDwtI7f5qpk2Aspzcw==\n-----END CERTIFICATE-----\n";
|
private static final String CERTIFICATE = "-----BEGIN CERTIFICATE-----\nMIIClzCCAgCgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBnjELMAkGA1UEBhMCVVMx\nEzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcMB1NlYXR0bGUxFjAUBgNVBAoM\nDU9wc2NvZGUsIEluYy4xHDAaBgNVBAsME0NlcnRpZmljYXRlIFNlcnZpY2UxMjAw\nBgNVBAMMKW9wc2NvZGUuY29tL2VtYWlsQWRkcmVzcz1hdXRoQG9wc2NvZGUuY29t\nMB4XDTEwMDczMDIwNDEzMFoXDTIwMDcyNzIwNDEzMFowADCCASIwDQYJKoZIhvcN\nAQEBBQADggEPADCCAQoCggEBAMm9mSSahptCikfvJ30CTbEnfhfbVzTFewnznFuo\n7KrPBGYIlUdPYQ9SGDo+GKjNKiTjZYMoOMUVnsHUhu0Ez49ZSaVQInWvbF8tvpM8\nmoGQNQJtDmXG6m+YaHiA4HF/ng2u/bNLtA6Jo3HzvRCobxywc/szPt0Kj0ZD1fJ2\nE237Ph41c8zlOg9QdF0d/iD2WZdgJ1rNndKoZ0rR3A1L50VUND+PNmMDfVYHHjmb\naT89AwihCeU8eUk7m/JNP87f1QDB0Gny0rkDC3drOGS7jmabTf/7gLE5sYq3qnd+\n8/vGU3QWyfCxKSfogl7kn5uWlIe4sOqMb06GNgC+d/oytlECAwEAATANBgkqhkiG\n9w0BAQUFAAOBgQBftzSZxstWw60GqRTDNN/F2GnrdtnKBoXzHww3r6jtGEylYq20\n5KfKpEx+sPX0gyZuYJiXC2CkEjImAluWKcdN9ZF6VD541sheAjbiaU7q7ZsztTxF\nWUH2tCvHeDXYKPKek3QzL7bYpUhLnCN/XxEv6ibeMDwtI7f5qpk2Aspzcw==\n-----END CERTIFICATE-----\n";
|
||||||
private ParseJson<Client> handler;
|
private ParseJson<Client> handler;
|
||||||
private EncryptionService encryptionService;
|
private Crypto crypto;
|
||||||
private PrivateKey privateKey;
|
private PrivateKey privateKey;
|
||||||
private X509Certificate certificate;
|
private X509Certificate certificate;
|
||||||
|
|
||||||
@BeforeTest
|
@BeforeTest
|
||||||
protected void setUpInjector() throws IOException {
|
protected void setUpInjector() throws IOException, CertificateException, InvalidKeySpecException {
|
||||||
Injector injector = Guice.createInjector(new ChefParserModule(), new GsonModule());
|
Injector injector = Guice.createInjector(new ChefParserModule(), new GsonModule());
|
||||||
handler = injector.getInstance(Key.get(new TypeLiteral<ParseJson<Client>>() {
|
handler = injector.getInstance(Key.get(new TypeLiteral<ParseJson<Client>>() {
|
||||||
}));
|
}));
|
||||||
encryptionService = injector.getInstance(EncryptionService.class);
|
crypto = injector.getInstance(Crypto.class);
|
||||||
certificate = encryptionService.x509CertificateFromPEM(CERTIFICATE.getBytes("UTF-8"));
|
certificate = Pems.x509Certificate(Payloads.newStringPayload(CERTIFICATE), null);
|
||||||
privateKey = encryptionService.privateKeyFromPEM(PRIVATE_KEY.getBytes("UTF-8"));
|
privateKey = crypto.rsaKeyFactory().generatePrivate(Pems.privateKeySpec(Payloads.newStringPayload(PRIVATE_KEY)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void test() {
|
public void test() throws IOException {
|
||||||
|
|
||||||
Client user = new Client(certificate, "jclouds", "adriancole-jcloudstest", "adriancole-jcloudstest", false,
|
Client user = new Client(certificate, "jclouds", "adriancole-jcloudstest", "adriancole-jcloudstest", false,
|
||||||
privateKey);
|
privateKey);
|
||||||
|
|
||||||
byte[] encrypted = encryptionService.rsaEncrypt(Payloads.newPayload("fooya"), user.getCertificate()
|
byte[] encrypted = ByteStreams.toByteArray(new RSAEncryptingPayload(Payloads.newPayload("fooya"), user
|
||||||
.getPublicKey());
|
.getCertificate().getPublicKey()));
|
||||||
|
|
||||||
assertEquals(encryptionService.rsaDecrypt(Payloads.newPayload(encrypted), user.getPrivateKey()), "fooya"
|
assertEquals(ByteStreams.toByteArray(new RSADecryptingPayload(Payloads.newPayload(encrypted), user
|
||||||
.getBytes());
|
.getPrivateKey())), "fooya".getBytes());
|
||||||
|
|
||||||
assertEquals(handler.apply(new HttpResponse(200, "ok", newInputStreamPayload(ParseClientFromJsonTest.class
|
assertEquals(handler.apply(new HttpResponse(200, "ok", newInputStreamPayload(ParseClientFromJsonTest.class
|
||||||
.getResourceAsStream("/client.json")))), user);
|
.getResourceAsStream("/client.json")))), user);
|
||||||
|
|
|
@ -11,7 +11,7 @@ import org.jclouds.chef.domain.Attribute;
|
||||||
import org.jclouds.chef.domain.CookbookVersion;
|
import org.jclouds.chef.domain.CookbookVersion;
|
||||||
import org.jclouds.chef.domain.Metadata;
|
import org.jclouds.chef.domain.Metadata;
|
||||||
import org.jclouds.chef.domain.Resource;
|
import org.jclouds.chef.domain.Resource;
|
||||||
import org.jclouds.encryption.EncryptionService;
|
import org.jclouds.crypto.CryptoStreams;
|
||||||
import org.jclouds.http.HttpResponse;
|
import org.jclouds.http.HttpResponse;
|
||||||
import org.jclouds.http.functions.ParseJson;
|
import org.jclouds.http.functions.ParseJson;
|
||||||
import org.jclouds.io.Payloads;
|
import org.jclouds.io.Payloads;
|
||||||
|
@ -51,68 +51,69 @@ public class ParseCookbookVersionFromJsonTest {
|
||||||
@Test(enabled = false)
|
@Test(enabled = false)
|
||||||
public void testBrew() throws IOException {
|
public void testBrew() throws IOException {
|
||||||
CookbookVersion cookbook = handler.apply(new HttpResponse(200, "ok", Payloads
|
CookbookVersion cookbook = handler.apply(new HttpResponse(200, "ok", Payloads
|
||||||
.newPayload(ParseCookbookVersionFromJsonTest.class.getResourceAsStream("/brew-cookbook.json"))));
|
.newPayload(ParseCookbookVersionFromJsonTest.class.getResourceAsStream("/brew-cookbook.json"))));
|
||||||
|
|
||||||
assertEquals(cookbook, handler.apply(new HttpResponse(200, "ok", Payloads.newPayload(Utils.toInputStream(json
|
assertEquals(cookbook, handler.apply(new HttpResponse(200, "ok", Payloads.newPayload(Utils.toInputStream(json
|
||||||
.toJson(cookbook))))));
|
.toJson(cookbook))))));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(enabled = false)
|
@Test(enabled = false)
|
||||||
public void testTomcat() {
|
public void testTomcat() {
|
||||||
CookbookVersion cookbook = handler.apply(new HttpResponse(200, "ok", Payloads
|
CookbookVersion cookbook = handler.apply(new HttpResponse(200, "ok", Payloads
|
||||||
.newPayload(ParseCookbookVersionFromJsonTest.class.getResourceAsStream("/tomcat-cookbook.json"))));
|
.newPayload(ParseCookbookVersionFromJsonTest.class.getResourceAsStream("/tomcat-cookbook.json"))));
|
||||||
|
|
||||||
assertEquals(cookbook, handler.apply(new HttpResponse(200, "ok", Payloads.newPayload(Utils.toInputStream(json
|
assertEquals(cookbook, handler.apply(new HttpResponse(200, "ok", Payloads.newPayload(Utils.toInputStream(json
|
||||||
.toJson(cookbook))))));
|
.toJson(cookbook))))));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(enabled = false)
|
@Test(enabled = false)
|
||||||
public void testMysql() throws IOException {
|
public void testMysql() throws IOException {
|
||||||
CookbookVersion cookbook = handler.apply(new HttpResponse(200, "ok", Payloads
|
CookbookVersion cookbook = handler.apply(new HttpResponse(200, "ok", Payloads
|
||||||
.newPayload(ParseCookbookVersionFromJsonTest.class.getResourceAsStream("/mysql-cookbook.json"))));
|
.newPayload(ParseCookbookVersionFromJsonTest.class.getResourceAsStream("/mysql-cookbook.json"))));
|
||||||
|
|
||||||
assertEquals(cookbook, handler.apply(new HttpResponse(200, "ok", Payloads.newPayload(Utils.toInputStream(json
|
assertEquals(cookbook, handler.apply(new HttpResponse(200, "ok", Payloads.newPayload(Utils.toInputStream(json
|
||||||
.toJson(cookbook))))));
|
.toJson(cookbook))))));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(enabled = false)
|
@Test(enabled = false)
|
||||||
public void testApache() {
|
public void testApache() {
|
||||||
EncryptionService encryptionService = injector.getInstance(EncryptionService.class);
|
|
||||||
|
|
||||||
assertEquals(
|
assertEquals(
|
||||||
handler.apply(new HttpResponse(200, "ok", Payloads.newPayload(ParseCookbookVersionFromJsonTest.class
|
handler.apply(new HttpResponse(200, "ok", Payloads.newPayload(ParseCookbookVersionFromJsonTest.class
|
||||||
.getResourceAsStream("/apache-chef-demo-cookbook.json")))),
|
.getResourceAsStream("/apache-chef-demo-cookbook.json")))),
|
||||||
new CookbookVersion(
|
new CookbookVersion(
|
||||||
"apache-chef-demo-0.0.0",
|
"apache-chef-demo-0.0.0",
|
||||||
ImmutableSet.<Resource> of(),
|
ImmutableSet.<Resource> of(),
|
||||||
ImmutableSet.<Resource> of(),
|
ImmutableSet.<Resource> of(),
|
||||||
ImmutableSet.<Resource> of(),
|
ImmutableSet.<Resource> of(),
|
||||||
new Metadata("Apache v2.0", "Your Name", ImmutableMap.<String, String> of(), ImmutableMap
|
new Metadata("Apache v2.0", "Your Name", ImmutableMap.<String, String> of(), ImmutableMap
|
||||||
.<String, Set<String>> of(), "youremail@example.com", ImmutableMap.<String, Set<String>> of(),
|
.<String, Set<String>> of(), "youremail@example.com", ImmutableMap
|
||||||
"A fabulous new cookbook", ImmutableMap.<String, Set<String>> of(), ImmutableMap
|
.<String, Set<String>> of(), "A fabulous new cookbook", ImmutableMap
|
||||||
.<String, Set<String>> of(), "0.0.0", ImmutableMap.<String, String> of(), ImmutableMap
|
.<String, Set<String>> of(), ImmutableMap.<String, Set<String>> of(), "0.0.0",
|
||||||
.<String, Set<String>> of(), "apache-chef-demo", ImmutableMap.<String, String> of(), "",
|
ImmutableMap.<String, String> of(), ImmutableMap.<String, Set<String>> of(),
|
||||||
ImmutableMap.<String, Attribute> of(), ImmutableMap.<String, String> of()),
|
"apache-chef-demo", ImmutableMap.<String, String> of(), "", ImmutableMap
|
||||||
ImmutableSet.<Resource> of(),
|
.<String, Attribute> of(), ImmutableMap.<String, String> of()),
|
||||||
"apache-chef-demo",
|
ImmutableSet.<Resource> of(),
|
||||||
ImmutableSet.<Resource> of(),
|
"apache-chef-demo",
|
||||||
ImmutableSet.<Resource> of(),
|
ImmutableSet.<Resource> of(),
|
||||||
ImmutableSet.<Resource> of(),
|
ImmutableSet.<Resource> of(),
|
||||||
"0.0.0",
|
ImmutableSet.<Resource> of(),
|
||||||
ImmutableSet.<Resource> of(),
|
"0.0.0",
|
||||||
ImmutableSet
|
ImmutableSet.<Resource> of(),
|
||||||
.<Resource> of(
|
ImmutableSet
|
||||||
new Resource(
|
.<Resource> of(
|
||||||
"README",
|
new Resource(
|
||||||
URI
|
"README",
|
||||||
.create("https://s3.amazonaws.com/opscode-platform-production-data/organization-486ca3ac66264fea926aa0b4ff74341c/checksum-11637f98942eafbf49c71b7f2f048b78?AWSAccessKeyId=AKIAJOZTD2N26S7W6APA&Expires=1277766181&Signature=zgpNl6wSxjTNovqZu2nJq0JztU8%3D"),
|
URI
|
||||||
encryptionService.fromHex("11637f98942eafbf49c71b7f2f048b78"), "README", "default"),
|
.create("https://s3.amazonaws.com/opscode-platform-production-data/organization-486ca3ac66264fea926aa0b4ff74341c/checksum-11637f98942eafbf49c71b7f2f048b78?AWSAccessKeyId=AKIAJOZTD2N26S7W6APA&Expires=1277766181&Signature=zgpNl6wSxjTNovqZu2nJq0JztU8%3D"),
|
||||||
new Resource(
|
CryptoStreams.hex("11637f98942eafbf49c71b7f2f048b78"), "README",
|
||||||
"Rakefile",
|
"default"),
|
||||||
URI
|
new Resource(
|
||||||
.create("https://s3.amazonaws.com/opscode-platform-production-data/organization-486ca3ac66264fea926aa0b4ff74341c/checksum-ebcf925a1651b4e04b9cd8aac2bc54eb?AWSAccessKeyId=AKIAJOZTD2N26S7W6APA&Expires=1277766181&Signature=EFzzDSKKytTl7b%2FxrCeNLh05zj4%3D"),
|
"Rakefile",
|
||||||
encryptionService.fromHex("ebcf925a1651b4e04b9cd8aac2bc54eb"), "Rakefile",
|
URI
|
||||||
"default"))));
|
.create("https://s3.amazonaws.com/opscode-platform-production-data/organization-486ca3ac66264fea926aa0b4ff74341c/checksum-ebcf925a1651b4e04b9cd8aac2bc54eb?AWSAccessKeyId=AKIAJOZTD2N26S7W6APA&Expires=1277766181&Signature=EFzzDSKKytTl7b%2FxrCeNLh05zj4%3D"),
|
||||||
|
CryptoStreams.hex("ebcf925a1651b4e04b9cd8aac2bc54eb"), "Rakefile",
|
||||||
|
"default"))));
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ import java.util.List;
|
||||||
import org.jclouds.chef.config.ChefParserModule;
|
import org.jclouds.chef.config.ChefParserModule;
|
||||||
import org.jclouds.chef.domain.ChecksumStatus;
|
import org.jclouds.chef.domain.ChecksumStatus;
|
||||||
import org.jclouds.chef.domain.UploadSandbox;
|
import org.jclouds.chef.domain.UploadSandbox;
|
||||||
import org.jclouds.encryption.EncryptionService;
|
import org.jclouds.crypto.CryptoStreams;
|
||||||
import org.jclouds.http.HttpResponse;
|
import org.jclouds.http.HttpResponse;
|
||||||
import org.jclouds.http.functions.ParseJson;
|
import org.jclouds.http.functions.ParseJson;
|
||||||
import org.jclouds.io.Payloads;
|
import org.jclouds.io.Payloads;
|
||||||
|
@ -43,22 +43,21 @@ public class ParseUploadSandboxFromJsonTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void test() {
|
public void test() {
|
||||||
EncryptionService encryptionService = injector.getInstance(EncryptionService.class);
|
|
||||||
assertEquals(
|
assertEquals(
|
||||||
handler.apply(new HttpResponse(200, "ok", Payloads.newPayload(ParseUploadSandboxFromJsonTest.class
|
handler.apply(new HttpResponse(200, "ok", Payloads.newPayload(ParseUploadSandboxFromJsonTest.class
|
||||||
.getResourceAsStream("/upload-site.json")))),
|
.getResourceAsStream("/upload-site.json")))),
|
||||||
new UploadSandbox(
|
new UploadSandbox(
|
||||||
URI
|
URI
|
||||||
.create("https://api.opscode.com/organizations/jclouds/sandboxes/d454f71e2a5f400c808d0c5d04c2c88c"),
|
.create("https://api.opscode.com/organizations/jclouds/sandboxes/d454f71e2a5f400c808d0c5d04c2c88c"),
|
||||||
ImmutableMap
|
ImmutableMap
|
||||||
.<List<Byte>, ChecksumStatus> of(
|
.<List<Byte>, ChecksumStatus> of(
|
||||||
Bytes.asList(encryptionService.fromHex("0c5ecd7788cf4f6c7de2a57193897a6c")),
|
Bytes.asList(CryptoStreams.hex("0c5ecd7788cf4f6c7de2a57193897a6c")),
|
||||||
new ChecksumStatus(
|
new ChecksumStatus(
|
||||||
URI
|
URI
|
||||||
.create("https://s3.amazonaws.com/opscode-platform-production-data/organization-486ca3ac66264fea926aa0b4ff74341c/sandbox-d454f71e2a5f400c808d0c5d04c2c88c/checksum-0c5ecd7788cf4f6c7de2a57193897a6c?AWSAccessKeyId=AKIAJOZTD2N26S7W6APA&Expires=1277344702&Signature=FtKyqvYEjhhEKmRY%2B0M8aGPMM7g%3D"),
|
.create("https://s3.amazonaws.com/opscode-platform-production-data/organization-486ca3ac66264fea926aa0b4ff74341c/sandbox-d454f71e2a5f400c808d0c5d04c2c88c/checksum-0c5ecd7788cf4f6c7de2a57193897a6c?AWSAccessKeyId=AKIAJOZTD2N26S7W6APA&Expires=1277344702&Signature=FtKyqvYEjhhEKmRY%2B0M8aGPMM7g%3D"),
|
||||||
true), Bytes.asList(encryptionService.fromHex("0189e76ccc476701d6b374e5a1a27347")),
|
true), Bytes.asList(CryptoStreams
|
||||||
new ChecksumStatus(), Bytes.asList(encryptionService
|
.hex("0189e76ccc476701d6b374e5a1a27347")), new ChecksumStatus(),
|
||||||
.fromHex("1dda05ed139664f1f89b9dec482b77c0")), new ChecksumStatus()),
|
Bytes.asList(CryptoStreams.hex("1dda05ed139664f1f89b9dec482b77c0")),
|
||||||
"d454f71e2a5f400c808d0c5d04c2c88c"));
|
new ChecksumStatus()), "d454f71e2a5f400c808d0c5d04c2c88c"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ import static org.testng.Assert.assertEquals;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import org.jclouds.encryption.EncryptionService;
|
import org.jclouds.crypto.CryptoStreams;
|
||||||
import org.testng.annotations.BeforeTest;
|
import org.testng.annotations.BeforeTest;
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
|
@ -43,16 +43,14 @@ import com.google.inject.Injector;
|
||||||
public class ByteArrayToMacAddressTest {
|
public class ByteArrayToMacAddressTest {
|
||||||
|
|
||||||
private ByteArrayToMacAddress converter;
|
private ByteArrayToMacAddress converter;
|
||||||
private EncryptionService encryptionService;
|
|
||||||
|
|
||||||
@BeforeTest
|
@BeforeTest
|
||||||
protected void setUpInjector() throws IOException {
|
protected void setUpInjector() throws IOException {
|
||||||
Injector injector = Guice.createInjector();
|
Injector injector = Guice.createInjector();
|
||||||
converter = injector.getInstance(ByteArrayToMacAddress.class);
|
converter = injector.getInstance(ByteArrayToMacAddress.class);
|
||||||
encryptionService = injector.getInstance(EncryptionService.class);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void test() {
|
public void test() {
|
||||||
assertEquals(converter.apply(encryptionService.fromHex("0026bb09e6c4")), "00:26:bb:09:e6:c4");
|
assertEquals(converter.apply(CryptoStreams.hex("0026bb09e6c4")), "00:26:bb:09:e6:c4");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,10 +43,13 @@ import org.jclouds.chef.ChefService;
|
||||||
import org.jclouds.chef.domain.Client;
|
import org.jclouds.chef.domain.Client;
|
||||||
import org.jclouds.chef.reference.ChefConstants;
|
import org.jclouds.chef.reference.ChefConstants;
|
||||||
import org.jclouds.chef.servlet.functions.InitParamsToProperties;
|
import org.jclouds.chef.servlet.functions.InitParamsToProperties;
|
||||||
|
import org.jclouds.crypto.Pems;
|
||||||
import org.jclouds.logging.Logger;
|
import org.jclouds.logging.Logger;
|
||||||
import org.jclouds.logging.jdk.JDKLogger;
|
import org.jclouds.logging.jdk.JDKLogger;
|
||||||
import org.jclouds.rest.RestContextFactory;
|
import org.jclouds.rest.RestContextFactory;
|
||||||
|
|
||||||
|
import com.google.common.base.Throwables;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers a new node in Chef and binds its name to {@link ChefConstants.CHEF_NODE}, its role to
|
* Registers a new node in Chef and binds its name to {@link ChefConstants.CHEF_NODE}, its role to
|
||||||
* {@link ChefConstants.CHEF_ROLE} and the {@link ChefService} for the client to
|
* {@link ChefConstants.CHEF_ROLE} and the {@link ChefService} for the client to
|
||||||
|
@ -129,14 +132,14 @@ public class ChefRegistrationListener implements ServletContextListener {
|
||||||
clientProperties.putAll(overrides);
|
clientProperties.putAll(overrides);
|
||||||
removeCredentials(clientProperties);
|
removeCredentials(clientProperties);
|
||||||
clientProperties.setProperty("chef.identity", id);
|
clientProperties.setProperty("chef.identity", id);
|
||||||
clientProperties.setProperty("chef.credential", validatorClient.getContext().utils().encryption().toPem(
|
clientProperties.setProperty("chef.credential", Pems.pem(client.getPrivateKey()));
|
||||||
client.getPrivateKey()));
|
|
||||||
clientService = createService(clientProperties);
|
clientService = createService(clientProperties);
|
||||||
clientService.createNodeAndPopulateAutomaticAttributes(id, singleton("role[" + role + "]"));
|
clientService.createNodeAndPopulateAutomaticAttributes(id, singleton("role[" + role + "]"));
|
||||||
return clientService;
|
return clientService;
|
||||||
} catch (RuntimeException e) {
|
} catch (Exception e) {
|
||||||
logger.error(e, "error creating node %s", id);
|
logger.error(e, "error creating node %s", id);
|
||||||
throw e;
|
Throwables.propagate(e);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,8 +26,8 @@ package org.jclouds.compute.internal;
|
||||||
import static com.google.common.base.Preconditions.checkArgument;
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
import static org.jclouds.compute.util.ComputeServiceUtils.installNewCredentials;
|
import static org.jclouds.compute.util.ComputeServiceUtils.installNewCredentials;
|
||||||
import static org.jclouds.concurrent.ConcurrentUtils.awaitCompletion;
|
import static org.jclouds.concurrent.FutureIterables.awaitCompletion;
|
||||||
import static org.jclouds.concurrent.ConcurrentUtils.transformParallel;
|
import static org.jclouds.concurrent.FutureIterables.transformParallel;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
|
@ -25,8 +25,8 @@ import javax.inject.Singleton;
|
||||||
|
|
||||||
import org.jclouds.Constants;
|
import org.jclouds.Constants;
|
||||||
import org.jclouds.compute.Utils;
|
import org.jclouds.compute.Utils;
|
||||||
|
import org.jclouds.crypto.Crypto;
|
||||||
import org.jclouds.date.DateService;
|
import org.jclouds.date.DateService;
|
||||||
import org.jclouds.encryption.EncryptionService;
|
|
||||||
import org.jclouds.json.Json;
|
import org.jclouds.json.Json;
|
||||||
import org.jclouds.logging.Logger.LoggerFactory;
|
import org.jclouds.logging.Logger.LoggerFactory;
|
||||||
import org.jclouds.rest.HttpAsyncClient;
|
import org.jclouds.rest.HttpAsyncClient;
|
||||||
|
@ -46,7 +46,7 @@ public class UtilsImpl extends org.jclouds.rest.internal.UtilsImpl implements Ut
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
UtilsImpl(Json json, HttpClient simpleClient, HttpAsyncClient simpleAsyncClient,
|
UtilsImpl(Json json, HttpClient simpleClient, HttpAsyncClient simpleAsyncClient,
|
||||||
EncryptionService encryption, DateService date,
|
Crypto encryption, DateService date,
|
||||||
@Named(Constants.PROPERTY_USER_THREADS) ExecutorService userThreads,
|
@Named(Constants.PROPERTY_USER_THREADS) ExecutorService userThreads,
|
||||||
@Named(Constants.PROPERTY_IO_WORKER_THREADS) ExecutorService ioThreads,
|
@Named(Constants.PROPERTY_IO_WORKER_THREADS) ExecutorService ioThreads,
|
||||||
LoggerFactory loggerFactory) {
|
LoggerFactory loggerFactory) {
|
||||||
|
|
|
@ -22,7 +22,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
import static com.google.common.base.Preconditions.checkState;
|
import static com.google.common.base.Preconditions.checkState;
|
||||||
import static org.jclouds.compute.util.ComputeServiceUtils.installNewCredentials;
|
import static org.jclouds.compute.util.ComputeServiceUtils.installNewCredentials;
|
||||||
import static org.jclouds.compute.util.ComputeServiceUtils.isKeyAuth;
|
import static org.jclouds.compute.util.ComputeServiceUtils.isKeyAuth;
|
||||||
import static org.jclouds.concurrent.ConcurrentUtils.awaitCompletion;
|
import static org.jclouds.concurrent.FutureIterables.awaitCompletion;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
|
@ -34,7 +34,7 @@
|
||||||
:enterprise 'org.jclouds.enterprise.config.EnterpriseConfigurationModule
|
:enterprise 'org.jclouds.enterprise.config.EnterpriseConfigurationModule
|
||||||
:apachehc 'org.jclouds.http.apachehc.config.ApacheHCHttpCommandExecutorServiceModule
|
:apachehc 'org.jclouds.http.apachehc.config.ApacheHCHttpCommandExecutorServiceModule
|
||||||
:ning 'org.jclouds.http.ning.config.NingHttpCommandExecutorServiceModule
|
:ning 'org.jclouds.http.ning.config.NingHttpCommandExecutorServiceModule
|
||||||
:bouncycastle 'org.jclouds.encryption.bouncycastle.config.BouncyCastleEncryptionServiceModule
|
:bouncycastle 'org.jclouds.encryption.bouncycastle.config.BouncyCastleCryptoModule
|
||||||
:joda 'org.jclouds.date.joda.config.JodaDateServiceModule
|
:joda 'org.jclouds.date.joda.config.JodaDateServiceModule
|
||||||
:gae 'org.jclouds.gae.config.GoogleAppEngineConfigurationModule})
|
:gae 'org.jclouds.gae.config.GoogleAppEngineConfigurationModule})
|
||||||
|
|
||||||
|
|
|
@ -1,561 +0,0 @@
|
||||||
/**
|
|
||||||
*
|
|
||||||
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
|
|
||||||
*
|
|
||||||
* ====================================================================
|
|
||||||
* Licensed 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.concurrent;
|
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
|
||||||
import static com.google.common.collect.Maps.newHashMap;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.AbstractExecutorService;
|
|
||||||
import java.util.concurrent.CancellationException;
|
|
||||||
import java.util.concurrent.CountDownLatch;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.concurrent.Future;
|
|
||||||
import java.util.concurrent.RejectedExecutionException;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.concurrent.TimeoutException;
|
|
||||||
import java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy;
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
import java.util.concurrent.locks.Condition;
|
|
||||||
import java.util.concurrent.locks.Lock;
|
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import javax.annotation.Resource;
|
|
||||||
import javax.inject.Named;
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
|
|
||||||
import org.jclouds.Constants;
|
|
||||||
import org.jclouds.http.handlers.BackoffLimitedRetryHandler;
|
|
||||||
import org.jclouds.logging.Logger;
|
|
||||||
|
|
||||||
import com.google.common.base.Function;
|
|
||||||
import com.google.common.base.Throwables;
|
|
||||||
import com.google.common.collect.ForwardingObject;
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
|
||||||
import com.google.common.collect.Iterables;
|
|
||||||
import com.google.common.collect.Maps;
|
|
||||||
import com.google.common.util.concurrent.ExecutionList;
|
|
||||||
import com.google.common.util.concurrent.ForwardingFuture;
|
|
||||||
import com.google.common.util.concurrent.Futures;
|
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
|
||||||
import com.google.common.util.concurrent.MoreExecutors;
|
|
||||||
import com.google.inject.Inject;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adapt things from Guava.
|
|
||||||
*
|
|
||||||
* @author Adrian Cole
|
|
||||||
*/
|
|
||||||
@Singleton
|
|
||||||
public class ConcurrentUtils {
|
|
||||||
@Resource
|
|
||||||
private static Logger logger = Logger.CONSOLE;
|
|
||||||
|
|
||||||
@Inject(optional = true)
|
|
||||||
@Named(Constants.PROPERTY_MAX_RETRIES)
|
|
||||||
private static int maxRetries = 5;
|
|
||||||
|
|
||||||
@Inject(optional = true)
|
|
||||||
private static BackoffLimitedRetryHandler retryHandler = BackoffLimitedRetryHandler.INSTANCE;
|
|
||||||
|
|
||||||
public static <F, T> Iterable<T> transformParallel(final Iterable<F> fromIterable,
|
|
||||||
final Function<? super F, Future<T>> function) {
|
|
||||||
return transformParallel(fromIterable, function, sameThreadExecutor(), null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <F, T> Iterable<T> transformParallel(final Iterable<F> fromIterable,
|
|
||||||
final Function<? super F, Future<T>> function, ExecutorService exec, @Nullable Long maxTime) {
|
|
||||||
return transformParallel(fromIterable, function, exec, maxTime, logger, "transforming");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <F, T> Iterable<T> transformParallel(final Iterable<F> fromIterable,
|
|
||||||
final Function<? super F, Future<T>> function, ExecutorService exec, @Nullable Long maxTime, Logger logger,
|
|
||||||
String logPrefix) {
|
|
||||||
return transformParallel(fromIterable, function, exec, maxTime, logger, logPrefix, retryHandler, maxRetries);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <F, T> Iterable<T> transformParallel(Iterable<F> fromIterable,
|
|
||||||
Function<? super F, Future<T>> function, ExecutorService exec, @Nullable Long maxTime, Logger logger,
|
|
||||||
String logPrefix, BackoffLimitedRetryHandler retryHandler, int maxRetries) {
|
|
||||||
Map<F, Exception> exceptions = newHashMap();
|
|
||||||
Map<F, Future<T>> responses = newHashMap();
|
|
||||||
for (int i = 0; i < maxRetries; i++) {
|
|
||||||
|
|
||||||
for (F from : fromIterable) {
|
|
||||||
responses.put(from, function.apply(from));
|
|
||||||
}
|
|
||||||
exceptions = awaitCompletion(responses, exec, maxTime, logger, logPrefix);
|
|
||||||
if (exceptions.size() > 0) {
|
|
||||||
fromIterable = exceptions.keySet();
|
|
||||||
retryHandler.imposeBackoffExponentialDelay(i + 1, String.format("error %s: %s: %s", logPrefix,
|
|
||||||
fromIterable, exceptions));
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (exceptions.size() > 0)
|
|
||||||
throw new RuntimeException(String.format("error %s: %s: %s", logPrefix, fromIterable, exceptions));
|
|
||||||
|
|
||||||
return unwrap(responses.values());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T> Map<T, Exception> awaitCompletion(Map<T, ? extends Future<?>> responses, ExecutorService exec,
|
|
||||||
@Nullable Long maxTime, final Logger logger, final String logPrefix) {
|
|
||||||
if (responses.size() == 0)
|
|
||||||
return ImmutableMap.of();
|
|
||||||
final int total = responses.size();
|
|
||||||
final CountDownLatch doneSignal = new CountDownLatch(total);
|
|
||||||
final AtomicInteger complete = new AtomicInteger(0);
|
|
||||||
final AtomicInteger errors = new AtomicInteger(0);
|
|
||||||
final long start = System.currentTimeMillis();
|
|
||||||
final Map<T, Exception> errorMap = Maps.newHashMap();
|
|
||||||
for (final java.util.Map.Entry<T, ? extends Future<?>> future : responses.entrySet()) {
|
|
||||||
makeListenable(future.getValue(), exec).addListener(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
future.getValue().get();
|
|
||||||
complete.incrementAndGet();
|
|
||||||
} catch (Exception e) {
|
|
||||||
errors.incrementAndGet();
|
|
||||||
logException(logger, logPrefix, total, complete.get(), errors.get(), start, e);
|
|
||||||
errorMap.put(future.getKey(), e);
|
|
||||||
}
|
|
||||||
doneSignal.countDown();
|
|
||||||
}
|
|
||||||
}, exec);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
if (maxTime != null)
|
|
||||||
doneSignal.await(maxTime, TimeUnit.MILLISECONDS);
|
|
||||||
else
|
|
||||||
doneSignal.await();
|
|
||||||
if (errors.get() > 0) {
|
|
||||||
String message = message(logPrefix, total, complete.get(), errors.get(), start);
|
|
||||||
RuntimeException exception = new RuntimeException(message);
|
|
||||||
logger.error(exception, message);
|
|
||||||
}
|
|
||||||
if (logger.isTraceEnabled()) {
|
|
||||||
String message = message(logPrefix, total, complete.get(), errors.get(), start);
|
|
||||||
logger.trace(message);
|
|
||||||
}
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
String message = message(logPrefix, total, complete.get(), errors.get(), start);
|
|
||||||
TimeoutException exception = new TimeoutException(message);
|
|
||||||
logger.error(exception, message);
|
|
||||||
Throwables.propagate(exception);
|
|
||||||
}
|
|
||||||
return errorMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T> Iterable<T> unwrap(Iterable<Future<T>> values) {
|
|
||||||
return Iterables.transform(values, new Function<Future<T>, T>() {
|
|
||||||
@Override
|
|
||||||
public T apply(Future<T> from) {
|
|
||||||
try {
|
|
||||||
return from.get();
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
Throwables.propagate(e);
|
|
||||||
} catch (ExecutionException e) {
|
|
||||||
Throwables.propagate(e);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void logException(Logger logger, String logPrefix, int total, int complete, int errors, long start,
|
|
||||||
Exception e) {
|
|
||||||
String message = message(logPrefix, total, complete, errors, start);
|
|
||||||
logger.error(e, message);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String message(String prefix, int size, int complete, int errors, long start) {
|
|
||||||
return String.format("%s, completed: %d/%d, errors: %d, rate: %dms/op", prefix, complete, size, errors,
|
|
||||||
(long) ((System.currentTimeMillis() - start) / ((double) size)));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static boolean timeOut(long start, Long maxTime) {
|
|
||||||
return maxTime != null ? System.currentTimeMillis() < start + maxTime : false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Just like {@code Futures#makeListenable} except that we pass in an executorService.
|
|
||||||
* <p/>
|
|
||||||
* Temporary hack until http://code.google.com/p/guava-libraries/issues/detail?id=317 is fixed.
|
|
||||||
*/
|
|
||||||
public static <T> ListenableFuture<T> makeListenable(Future<T> future, ExecutorService executorService) {
|
|
||||||
if (future instanceof ListenableFuture<?>) {
|
|
||||||
return (ListenableFuture<T>) future;
|
|
||||||
}
|
|
||||||
return ListenableFutureAdapter.create(future, executorService);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Just like {@code Futures#compose} except that we check the type of the executorService before
|
|
||||||
* creating the Future. If we are single threaded, invoke the function lazy as opposed to
|
|
||||||
* chaining, so that we don't invoke get() early.
|
|
||||||
*/
|
|
||||||
public static <I, O> ListenableFuture<O> compose(Future<I> future, final Function<? super I, ? extends O> function,
|
|
||||||
ExecutorService executorService) {
|
|
||||||
if (future instanceof ListenableFutureAdapter<?>) {
|
|
||||||
ListenableFutureAdapter<I> lf = (ListenableFutureAdapter<I>) future;
|
|
||||||
if (lf.futureListener.executor.getClass().isAnnotationPresent(SingleThreaded.class))
|
|
||||||
return LazyListenableFutureFunctionAdapter.create(((ListenableFutureAdapter<I>) future).futureListener,
|
|
||||||
function);
|
|
||||||
else
|
|
||||||
return Futures.compose(lf, function, executorService);
|
|
||||||
} else if (executorService.getClass().isAnnotationPresent(SingleThreaded.class)) {
|
|
||||||
return LazyListenableFutureFunctionAdapter.create(future, function, executorService);
|
|
||||||
} else {
|
|
||||||
return Futures.compose(makeListenable(future, executorService), function, executorService);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class FutureListener<T> {
|
|
||||||
private final Future<T> future;
|
|
||||||
private final ExecutorService executor;
|
|
||||||
private final ExecutionList executionList = new ExecutionList();
|
|
||||||
private final AtomicBoolean hasListeners = new AtomicBoolean(false);
|
|
||||||
|
|
||||||
static <T> FutureListener<T> create(Future<T> future, ExecutorService executor) {
|
|
||||||
return new FutureListener<T>(future, executor);
|
|
||||||
}
|
|
||||||
|
|
||||||
private FutureListener(Future<T> future, ExecutorService executor) {
|
|
||||||
this.future = checkNotNull(future, "future");
|
|
||||||
this.executor = checkNotNull(executor, "executor");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addListener(Runnable listener, Executor exec) {
|
|
||||||
|
|
||||||
// When a listener is first added, we run a task that will wait for
|
|
||||||
// the future to finish, and when it is done will run the listeners.
|
|
||||||
if (!hasListeners.get() && hasListeners.compareAndSet(false, true)) {
|
|
||||||
executor.execute(new Runnable() {
|
|
||||||
/* @Override */
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
future.get();
|
|
||||||
} catch (CancellationException e) {
|
|
||||||
// The task was cancelled, so it is done, run the listeners.
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
// This thread was interrupted. This should never happen, so we
|
|
||||||
// throw an IllegalStateException.
|
|
||||||
throw new IllegalStateException("Adapter thread interrupted!", e);
|
|
||||||
} catch (ExecutionException e) {
|
|
||||||
// The task caused an exception, so it is done, run the listeners.
|
|
||||||
}
|
|
||||||
executionList.run();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
executionList.add(listener, exec);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<T> getFuture() {
|
|
||||||
return future;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExecutorService getExecutor() {
|
|
||||||
return executor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class ListenableFutureAdapter<T> extends ForwardingFuture<T> implements ListenableFuture<T> {
|
|
||||||
private final FutureListener<T> futureListener;
|
|
||||||
|
|
||||||
static <T> ListenableFutureAdapter<T> create(Future<T> future, ExecutorService executor) {
|
|
||||||
return new ListenableFutureAdapter<T>(future, executor);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ListenableFutureAdapter(Future<T> future, ExecutorService executor) {
|
|
||||||
this.futureListener = FutureListener.create(future, executor);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Future<T> delegate() {
|
|
||||||
return futureListener.getFuture();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addListener(Runnable listener, Executor exec) {
|
|
||||||
futureListener.addListener(listener, exec);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class LazyListenableFutureFunctionAdapter<I, O> extends ForwardingObject implements
|
|
||||||
ListenableFuture<O> {
|
|
||||||
private final FutureListener<I> futureListener;
|
|
||||||
private final Function<? super I, ? extends O> function;
|
|
||||||
|
|
||||||
static <I, O> LazyListenableFutureFunctionAdapter<I, O> create(Future<I> future,
|
|
||||||
Function<? super I, ? extends O> function, ExecutorService executor) {
|
|
||||||
return new LazyListenableFutureFunctionAdapter<I, O>(future, function, executor);
|
|
||||||
}
|
|
||||||
|
|
||||||
static <I, O> LazyListenableFutureFunctionAdapter<I, O> create(FutureListener<I> futureListener,
|
|
||||||
Function<? super I, ? extends O> function) {
|
|
||||||
return new LazyListenableFutureFunctionAdapter<I, O>(futureListener, function);
|
|
||||||
}
|
|
||||||
|
|
||||||
private LazyListenableFutureFunctionAdapter(Future<I> future, Function<? super I, ? extends O> function,
|
|
||||||
ExecutorService executor) {
|
|
||||||
this(FutureListener.create(future, executor), function);
|
|
||||||
}
|
|
||||||
|
|
||||||
private LazyListenableFutureFunctionAdapter(FutureListener<I> futureListener,
|
|
||||||
Function<? super I, ? extends O> function) {
|
|
||||||
this.futureListener = checkNotNull(futureListener, "futureListener");
|
|
||||||
this.function = checkNotNull(function, "function");
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Concurrency detail:
|
|
||||||
*
|
|
||||||
* <p>To preserve the idempotency of calls to this.get(*) calls to the function are only
|
|
||||||
* applied once. A lock is required to prevent multiple applications of the function. The
|
|
||||||
* calls to future.get(*) are performed outside the lock, as is required to prevent calls to
|
|
||||||
* get(long, TimeUnit) to persist beyond their timeout.
|
|
||||||
*
|
|
||||||
* <p>Calls to future.get(*) on every call to this.get(*) also provide the cancellation
|
|
||||||
* behavior for this.
|
|
||||||
*
|
|
||||||
* <p>(Consider: in thread A, call get(), in thread B call get(long, TimeUnit). Thread B may
|
|
||||||
* have to wait for Thread A to finish, which would be unacceptable.)
|
|
||||||
*
|
|
||||||
* <p>Note that each call to Future<O>.get(*) results in a call to Future<I>.get(*), but the
|
|
||||||
* function is only applied once, so Future<I>.get(*) is assumed to be idempotent.
|
|
||||||
*/
|
|
||||||
|
|
||||||
private final Object lock = new Object();
|
|
||||||
private boolean set = false;
|
|
||||||
private O value = null;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public O get() throws InterruptedException, ExecutionException {
|
|
||||||
return apply(futureListener.getFuture().get());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public O get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
|
|
||||||
return apply(futureListener.getFuture().get(timeout, unit));
|
|
||||||
}
|
|
||||||
|
|
||||||
private O apply(I raw) {
|
|
||||||
synchronized (lock) {
|
|
||||||
if (!set) {
|
|
||||||
value = function.apply(raw);
|
|
||||||
set = true;
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean cancel(boolean mayInterruptIfRunning) {
|
|
||||||
return futureListener.getFuture().cancel(mayInterruptIfRunning);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isCancelled() {
|
|
||||||
return futureListener.getFuture().isCancelled();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isDone() {
|
|
||||||
return futureListener.getFuture().isDone();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addListener(Runnable listener, Executor exec) {
|
|
||||||
futureListener.addListener(listener, exec);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Object delegate() {
|
|
||||||
return futureListener.getFuture();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Taken from {@link MoreExecutors#sameThreadExecutor} as it was hidden and therefore incapable
|
|
||||||
* of instanceof checks.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* Creates an executor service that runs each task in the thread that invokes {@code
|
|
||||||
* execute/submit}, as in {@link CallerRunsPolicy} This applies both to individually submitted
|
|
||||||
* tasks and to collections of tasks submitted via {@code invokeAll} or {@code invokeAny}. In the
|
|
||||||
* latter case, tasks will run serially on the calling thread. Tasks are run to completion before
|
|
||||||
* a {@code Future} is returned to the caller (unless the executor has been shutdown).
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* Although all tasks are immediately executed in the thread that submitted the task, this
|
|
||||||
* {@code ExecutorService} imposes a small locking overhead on each task submission in order to
|
|
||||||
* implement shutdown and termination behavior.
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* The implementation deviates from the {@code ExecutorService} specification with regards to the
|
|
||||||
* {@code shutdownNow} method. First, "best-effort" with regards to canceling running tasks is
|
|
||||||
* implemented as "no-effort". No interrupts or other attempts are made to stop threads executing
|
|
||||||
* tasks. Second, the returned list will always be empty, as any submitted task is considered to
|
|
||||||
* have started execution. This applies also to tasks given to {@code invokeAll} or {@code
|
|
||||||
* invokeAny} which are pending serial execution, even the subset of the tasks that have not yet
|
|
||||||
* started execution. It is unclear from the {@code ExecutorService} specification if these
|
|
||||||
* should be included, and it's much easier to implement the interpretation that they not be.
|
|
||||||
* Finally, a call to {@code shutdown} or {@code shutdownNow} may result in concurrent calls to
|
|
||||||
* {@code invokeAll/invokeAny} throwing RejectedExecutionException, although a subset of the
|
|
||||||
* tasks may already have been executed.
|
|
||||||
*/
|
|
||||||
public static ExecutorService sameThreadExecutor() {
|
|
||||||
return new SameThreadExecutorService();
|
|
||||||
}
|
|
||||||
|
|
||||||
// See sameThreadExecutor javadoc for behavioral notes.
|
|
||||||
@SingleThreaded
|
|
||||||
public static class SameThreadExecutorService extends AbstractExecutorService {
|
|
||||||
/**
|
|
||||||
* Lock used whenever accessing the state variables (runningTasks, shutdown,
|
|
||||||
* terminationCondition) of the executor
|
|
||||||
*/
|
|
||||||
private final Lock lock = new ReentrantLock();
|
|
||||||
|
|
||||||
/** Signaled after the executor is shutdown and running tasks are done */
|
|
||||||
private final Condition termination = lock.newCondition();
|
|
||||||
|
|
||||||
private SameThreadExecutorService() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Conceptually, these two variables describe the executor being in one of three states: -
|
|
||||||
* Active: shutdown == false - Shutdown: runningTasks > 0 and shutdown == true - Terminated:
|
|
||||||
* runningTasks == 0 and shutdown == true
|
|
||||||
*/
|
|
||||||
private int runningTasks = 0;
|
|
||||||
private boolean shutdown = false;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void execute(Runnable command) {
|
|
||||||
startTask();
|
|
||||||
try {
|
|
||||||
command.run();
|
|
||||||
} finally {
|
|
||||||
endTask();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isShutdown() {
|
|
||||||
lock.lock();
|
|
||||||
try {
|
|
||||||
return shutdown;
|
|
||||||
} finally {
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void shutdown() {
|
|
||||||
lock.lock();
|
|
||||||
try {
|
|
||||||
shutdown = true;
|
|
||||||
} finally {
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// See sameThreadExecutor javadoc for unusual behavior of this method.
|
|
||||||
@Override
|
|
||||||
public List<Runnable> shutdownNow() {
|
|
||||||
shutdown();
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isTerminated() {
|
|
||||||
lock.lock();
|
|
||||||
try {
|
|
||||||
return shutdown && runningTasks == 0;
|
|
||||||
} finally {
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
|
|
||||||
long nanos = unit.toNanos(timeout);
|
|
||||||
lock.lock();
|
|
||||||
try {
|
|
||||||
for (;;) {
|
|
||||||
if (isTerminated()) {
|
|
||||||
return true;
|
|
||||||
} else if (nanos <= 0) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
nanos = termination.awaitNanos(nanos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the executor has been shut down and increments the running task count.
|
|
||||||
*
|
|
||||||
* @throws RejectedExecutionException
|
|
||||||
* if the executor has been previously shutdown
|
|
||||||
*/
|
|
||||||
private void startTask() {
|
|
||||||
lock.lock();
|
|
||||||
try {
|
|
||||||
if (isShutdown()) {
|
|
||||||
throw new RejectedExecutionException("Executor already shutdown");
|
|
||||||
}
|
|
||||||
runningTasks++;
|
|
||||||
} finally {
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decrements the running task count.
|
|
||||||
*/
|
|
||||||
private void endTask() {
|
|
||||||
lock.lock();
|
|
||||||
try {
|
|
||||||
runningTasks--;
|
|
||||||
if (isTerminated()) {
|
|
||||||
termination.signalAll();
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,185 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||||
|
*
|
||||||
|
* ====================================================================
|
||||||
|
* Licensed 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.concurrent;
|
||||||
|
|
||||||
|
import static com.google.common.collect.Maps.newHashMap;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import javax.inject.Named;
|
||||||
|
|
||||||
|
import org.jclouds.Constants;
|
||||||
|
import org.jclouds.http.handlers.BackoffLimitedRetryHandler;
|
||||||
|
import org.jclouds.logging.Logger;
|
||||||
|
|
||||||
|
import com.google.common.annotations.Beta;
|
||||||
|
import com.google.common.base.Function;
|
||||||
|
import com.google.common.base.Throwables;
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
import com.google.inject.Inject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* functions related to or replacing those in {@link com.google.common.collect.Iterables} dealing with Futures
|
||||||
|
*
|
||||||
|
* @author Adrian Cole
|
||||||
|
*/
|
||||||
|
@Beta
|
||||||
|
public class FutureIterables {
|
||||||
|
@Resource
|
||||||
|
private static Logger logger = Logger.CONSOLE;
|
||||||
|
|
||||||
|
@Inject(optional = true)
|
||||||
|
@Named(Constants.PROPERTY_MAX_RETRIES)
|
||||||
|
private static int maxRetries = 5;
|
||||||
|
|
||||||
|
@Inject(optional = true)
|
||||||
|
private static BackoffLimitedRetryHandler retryHandler = BackoffLimitedRetryHandler.INSTANCE;
|
||||||
|
|
||||||
|
public static <F, T> Iterable<T> transformParallel(final Iterable<F> fromIterable,
|
||||||
|
final Function<? super F, Future<T>> function) {
|
||||||
|
return transformParallel(fromIterable, function, org.jclouds.concurrent.MoreExecutors.sameThreadExecutor(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <F, T> Iterable<T> transformParallel(final Iterable<F> fromIterable,
|
||||||
|
final Function<? super F, Future<T>> function, ExecutorService exec, @Nullable Long maxTime) {
|
||||||
|
return transformParallel(fromIterable, function, exec, maxTime, logger, "transforming");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <F, T> Iterable<T> transformParallel(final Iterable<F> fromIterable,
|
||||||
|
final Function<? super F, Future<T>> function, ExecutorService exec, @Nullable Long maxTime, Logger logger,
|
||||||
|
String logPrefix) {
|
||||||
|
return transformParallel(fromIterable, function, exec, maxTime, logger, logPrefix, retryHandler, maxRetries);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <F, T> Iterable<T> transformParallel(Iterable<F> fromIterable,
|
||||||
|
Function<? super F, Future<T>> function, ExecutorService exec, @Nullable Long maxTime, Logger logger,
|
||||||
|
String logPrefix, BackoffLimitedRetryHandler retryHandler, int maxRetries) {
|
||||||
|
Map<F, Exception> exceptions = newHashMap();
|
||||||
|
Map<F, Future<T>> responses = newHashMap();
|
||||||
|
for (int i = 0; i < maxRetries; i++) {
|
||||||
|
|
||||||
|
for (F from : fromIterable) {
|
||||||
|
responses.put(from, function.apply(from));
|
||||||
|
}
|
||||||
|
exceptions = awaitCompletion(responses, exec, maxTime, logger, logPrefix);
|
||||||
|
if (exceptions.size() > 0) {
|
||||||
|
fromIterable = exceptions.keySet();
|
||||||
|
retryHandler.imposeBackoffExponentialDelay(i + 1, String.format("error %s: %s: %s", logPrefix,
|
||||||
|
fromIterable, exceptions));
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (exceptions.size() > 0)
|
||||||
|
throw new RuntimeException(String.format("error %s: %s: %s", logPrefix, fromIterable, exceptions));
|
||||||
|
|
||||||
|
return unwrap(responses.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Map<T, Exception> awaitCompletion(Map<T, ? extends Future<?>> responses, ExecutorService exec,
|
||||||
|
@Nullable Long maxTime, final Logger logger, final String logPrefix) {
|
||||||
|
if (responses.size() == 0)
|
||||||
|
return ImmutableMap.of();
|
||||||
|
final int total = responses.size();
|
||||||
|
final CountDownLatch doneSignal = new CountDownLatch(total);
|
||||||
|
final AtomicInteger complete = new AtomicInteger(0);
|
||||||
|
final AtomicInteger errors = new AtomicInteger(0);
|
||||||
|
final long start = System.currentTimeMillis();
|
||||||
|
final Map<T, Exception> errorMap = Maps.newHashMap();
|
||||||
|
for (final java.util.Map.Entry<T, ? extends Future<?>> future : responses.entrySet()) {
|
||||||
|
Futures.makeListenable(future.getValue(), exec).addListener(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
future.getValue().get();
|
||||||
|
complete.incrementAndGet();
|
||||||
|
} catch (Exception e) {
|
||||||
|
errors.incrementAndGet();
|
||||||
|
logException(logger, logPrefix, total, complete.get(), errors.get(), start, e);
|
||||||
|
errorMap.put(future.getKey(), e);
|
||||||
|
}
|
||||||
|
doneSignal.countDown();
|
||||||
|
}
|
||||||
|
}, exec);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (maxTime != null)
|
||||||
|
doneSignal.await(maxTime, TimeUnit.MILLISECONDS);
|
||||||
|
else
|
||||||
|
doneSignal.await();
|
||||||
|
if (errors.get() > 0) {
|
||||||
|
String message = message(logPrefix, total, complete.get(), errors.get(), start);
|
||||||
|
RuntimeException exception = new RuntimeException(message);
|
||||||
|
logger.error(exception, message);
|
||||||
|
}
|
||||||
|
if (logger.isTraceEnabled()) {
|
||||||
|
String message = message(logPrefix, total, complete.get(), errors.get(), start);
|
||||||
|
logger.trace(message);
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
String message = message(logPrefix, total, complete.get(), errors.get(), start);
|
||||||
|
TimeoutException exception = new TimeoutException(message);
|
||||||
|
logger.error(exception, message);
|
||||||
|
Throwables.propagate(exception);
|
||||||
|
}
|
||||||
|
return errorMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Iterable<T> unwrap(Iterable<Future<T>> values) {
|
||||||
|
return Iterables.transform(values, new Function<Future<T>, T>() {
|
||||||
|
@Override
|
||||||
|
public T apply(Future<T> from) {
|
||||||
|
try {
|
||||||
|
return from.get();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Throwables.propagate(e);
|
||||||
|
} catch (ExecutionException e) {
|
||||||
|
Throwables.propagate(e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void logException(Logger logger, String logPrefix, int total, int complete, int errors, long start,
|
||||||
|
Exception e) {
|
||||||
|
String message = message(logPrefix, total, complete, errors, start);
|
||||||
|
logger.error(e, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String message(String prefix, int size, int complete, int errors, long start) {
|
||||||
|
return String.format("%s, completed: %d/%d, errors: %d, rate: %dms/op", prefix, complete, size, errors,
|
||||||
|
(long) ((System.currentTimeMillis() - start) / ((double) size)));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static boolean timeOut(long start, Long maxTime) {
|
||||||
|
return maxTime != null ? System.currentTimeMillis() < start + maxTime : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,248 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||||
|
*
|
||||||
|
* ====================================================================
|
||||||
|
* Licensed 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.concurrent;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
import java.util.concurrent.CancellationException;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
import com.google.common.annotations.Beta;
|
||||||
|
import com.google.common.base.Function;
|
||||||
|
import com.google.common.collect.ForwardingObject;
|
||||||
|
import com.google.common.util.concurrent.ExecutionList;
|
||||||
|
import com.google.common.util.concurrent.ForwardingFuture;
|
||||||
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* functions related to or replacing those in {@link com.google.common.util.concurrent.Futures}
|
||||||
|
*
|
||||||
|
* @author Adrian Cole
|
||||||
|
*/
|
||||||
|
@Beta
|
||||||
|
public class Futures {
|
||||||
|
|
||||||
|
public static class FutureListener<T> {
|
||||||
|
private final Future<T> future;
|
||||||
|
final ExecutorService executor;
|
||||||
|
private final ExecutionList executionList = new ExecutionList();
|
||||||
|
private final AtomicBoolean hasListeners = new AtomicBoolean(false);
|
||||||
|
|
||||||
|
static <T> FutureListener<T> create(Future<T> future, ExecutorService executor) {
|
||||||
|
return new FutureListener<T>(future, executor);
|
||||||
|
}
|
||||||
|
|
||||||
|
private FutureListener(Future<T> future, ExecutorService executor) {
|
||||||
|
this.future = checkNotNull(future, "future");
|
||||||
|
this.executor = checkNotNull(executor, "executor");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addListener(Runnable listener, Executor exec) {
|
||||||
|
|
||||||
|
// When a listener is first added, we run a task that will wait for
|
||||||
|
// the future to finish, and when it is done will run the listeners.
|
||||||
|
if (!hasListeners.get() && hasListeners.compareAndSet(false, true)) {
|
||||||
|
executor.execute(new Runnable() {
|
||||||
|
/* @Override */
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
future.get();
|
||||||
|
} catch (CancellationException e) {
|
||||||
|
// The task was cancelled, so it is done, run the listeners.
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
// This thread was interrupted. This should never happen, so we
|
||||||
|
// throw an IllegalStateException.
|
||||||
|
throw new IllegalStateException("Adapter thread interrupted!", e);
|
||||||
|
} catch (ExecutionException e) {
|
||||||
|
// The task caused an exception, so it is done, run the listeners.
|
||||||
|
}
|
||||||
|
executionList.run();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
executionList.add(listener, exec);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<T> getFuture() {
|
||||||
|
return future;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExecutorService getExecutor() {
|
||||||
|
return executor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ListenableFutureAdapter<T> extends ForwardingFuture<T> implements ListenableFuture<T> {
|
||||||
|
final FutureListener<T> futureListener;
|
||||||
|
|
||||||
|
static <T> ListenableFutureAdapter<T> create(Future<T> future, ExecutorService executor) {
|
||||||
|
return new ListenableFutureAdapter<T>(future, executor);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ListenableFutureAdapter(Future<T> future, ExecutorService executor) {
|
||||||
|
this.futureListener = FutureListener.create(future, executor);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Future<T> delegate() {
|
||||||
|
return futureListener.getFuture();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addListener(Runnable listener, Executor exec) {
|
||||||
|
futureListener.addListener(listener, exec);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class LazyListenableFutureFunctionAdapter<I, O> extends ForwardingObject implements
|
||||||
|
ListenableFuture<O> {
|
||||||
|
private final FutureListener<I> futureListener;
|
||||||
|
private final Function<? super I, ? extends O> function;
|
||||||
|
|
||||||
|
static <I, O> LazyListenableFutureFunctionAdapter<I, O> create(Future<I> future,
|
||||||
|
Function<? super I, ? extends O> function, ExecutorService executor) {
|
||||||
|
return new LazyListenableFutureFunctionAdapter<I, O>(future, function, executor);
|
||||||
|
}
|
||||||
|
|
||||||
|
static <I, O> LazyListenableFutureFunctionAdapter<I, O> create(FutureListener<I> futureListener,
|
||||||
|
Function<? super I, ? extends O> function) {
|
||||||
|
return new LazyListenableFutureFunctionAdapter<I, O>(futureListener, function);
|
||||||
|
}
|
||||||
|
|
||||||
|
private LazyListenableFutureFunctionAdapter(Future<I> future, Function<? super I, ? extends O> function,
|
||||||
|
ExecutorService executor) {
|
||||||
|
this(FutureListener.create(future, executor), function);
|
||||||
|
}
|
||||||
|
|
||||||
|
private LazyListenableFutureFunctionAdapter(FutureListener<I> futureListener,
|
||||||
|
Function<? super I, ? extends O> function) {
|
||||||
|
this.futureListener = checkNotNull(futureListener, "futureListener");
|
||||||
|
this.function = checkNotNull(function, "function");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Concurrency detail:
|
||||||
|
*
|
||||||
|
* <p>To preserve the idempotency of calls to this.get(*) calls to the function are only
|
||||||
|
* applied once. A lock is required to prevent multiple applications of the function. The
|
||||||
|
* calls to future.get(*) are performed outside the lock, as is required to prevent calls to
|
||||||
|
* get(long, TimeUnit) to persist beyond their timeout.
|
||||||
|
*
|
||||||
|
* <p>Calls to future.get(*) on every call to this.get(*) also provide the cancellation
|
||||||
|
* behavior for this.
|
||||||
|
*
|
||||||
|
* <p>(Consider: in thread A, call get(), in thread B call get(long, TimeUnit). Thread B may
|
||||||
|
* have to wait for Thread A to finish, which would be unacceptable.)
|
||||||
|
*
|
||||||
|
* <p>Note that each call to Future<O>.get(*) results in a call to Future<I>.get(*), but the
|
||||||
|
* function is only applied once, so Future<I>.get(*) is assumed to be idempotent.
|
||||||
|
*/
|
||||||
|
|
||||||
|
private final Object lock = new Object();
|
||||||
|
private boolean set = false;
|
||||||
|
private O value = null;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public O get() throws InterruptedException, ExecutionException {
|
||||||
|
return apply(futureListener.getFuture().get());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public O get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
|
||||||
|
return apply(futureListener.getFuture().get(timeout, unit));
|
||||||
|
}
|
||||||
|
|
||||||
|
private O apply(I raw) {
|
||||||
|
synchronized (lock) {
|
||||||
|
if (!set) {
|
||||||
|
value = function.apply(raw);
|
||||||
|
set = true;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean cancel(boolean mayInterruptIfRunning) {
|
||||||
|
return futureListener.getFuture().cancel(mayInterruptIfRunning);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCancelled() {
|
||||||
|
return futureListener.getFuture().isCancelled();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDone() {
|
||||||
|
return futureListener.getFuture().isDone();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addListener(Runnable listener, Executor exec) {
|
||||||
|
futureListener.addListener(listener, exec);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Object delegate() {
|
||||||
|
return futureListener.getFuture();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Just like {@code Futures#compose} except that we check the type of the executorService before
|
||||||
|
* creating the Future. If we are single threaded, invoke the function lazy as opposed to
|
||||||
|
* chaining, so that we don't invoke get() early.
|
||||||
|
*/
|
||||||
|
public static <I, O> ListenableFuture<O> compose(Future<I> future, final Function<? super I, ? extends O> function,
|
||||||
|
ExecutorService executorService) {
|
||||||
|
if (future instanceof Futures.ListenableFutureAdapter<?>) {
|
||||||
|
Futures.ListenableFutureAdapter<I> lf = (ListenableFutureAdapter<I>) future;
|
||||||
|
if (lf.futureListener.executor.getClass().isAnnotationPresent(SingleThreaded.class))
|
||||||
|
return Futures.LazyListenableFutureFunctionAdapter.create(
|
||||||
|
((org.jclouds.concurrent.Futures.ListenableFutureAdapter<I>) future).futureListener, function);
|
||||||
|
else
|
||||||
|
return com.google.common.util.concurrent.Futures.compose(lf, function, executorService);
|
||||||
|
} else if (executorService.getClass().isAnnotationPresent(SingleThreaded.class)) {
|
||||||
|
return Futures.LazyListenableFutureFunctionAdapter.create(future, function, executorService);
|
||||||
|
} else {
|
||||||
|
return com.google.common.util.concurrent.Futures.compose(Futures.makeListenable(future, executorService), function, executorService);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Just like {@code Futures#makeListenable} except that we pass in an executorService.
|
||||||
|
* <p/>
|
||||||
|
* Temporary hack until http://code.google.com/p/guava-libraries/issues/detail?id=317 is fixed.
|
||||||
|
*/
|
||||||
|
public static <T> ListenableFuture<T> makeListenable(Future<T> future, ExecutorService executorService) {
|
||||||
|
if (future instanceof ListenableFuture<?>) {
|
||||||
|
return (ListenableFuture<T>) future;
|
||||||
|
}
|
||||||
|
return ListenableFutureAdapter.create(future, executorService);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,199 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||||
|
*
|
||||||
|
* ====================================================================
|
||||||
|
* Licensed 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.concurrent;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.AbstractExecutorService;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.RejectedExecutionException;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy;
|
||||||
|
import java.util.concurrent.locks.Condition;
|
||||||
|
import java.util.concurrent.locks.Lock;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
|
import com.google.common.annotations.Beta;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* functions related to or replacing those in
|
||||||
|
* {@link com.google.common.util.concurrent.MoreExecutors}
|
||||||
|
*
|
||||||
|
* @author Adrian Cole
|
||||||
|
*/
|
||||||
|
@Beta
|
||||||
|
public class MoreExecutors {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Taken from @link com.google.common.util.concurrent.MoreExecutors} as it was hidden and
|
||||||
|
* therefore incapable of instanceof checks.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Creates an executor service that runs each task in the thread that invokes {@code
|
||||||
|
* execute/submit}, as in {@link CallerRunsPolicy} This applies both to individually submitted
|
||||||
|
* tasks and to collections of tasks submitted via {@code invokeAll} or {@code invokeAny}. In the
|
||||||
|
* latter case, tasks will run serially on the calling thread. Tasks are run to completion before
|
||||||
|
* a {@code Future} is returned to the caller (unless the executor has been shutdown).
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Although all tasks are immediately executed in the thread that submitted the task, this
|
||||||
|
* {@code ExecutorService} imposes a small locking overhead on each task submission in order to
|
||||||
|
* implement shutdown and termination behavior.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* The implementation deviates from the {@code ExecutorService} specification with regards to the
|
||||||
|
* {@code shutdownNow} method. First, "best-effort" with regards to canceling running tasks is
|
||||||
|
* implemented as "no-effort". No interrupts or other attempts are made to stop threads executing
|
||||||
|
* tasks. Second, the returned list will always be empty, as any submitted task is considered to
|
||||||
|
* have started execution. This applies also to tasks given to {@code invokeAll} or {@code
|
||||||
|
* invokeAny} which are pending serial execution, even the subset of the tasks that have not yet
|
||||||
|
* started execution. It is unclear from the {@code ExecutorService} specification if these
|
||||||
|
* should be included, and it's much easier to implement the interpretation that they not be.
|
||||||
|
* Finally, a call to {@code shutdown} or {@code shutdownNow} may result in concurrent calls to
|
||||||
|
* {@code invokeAll/invokeAny} throwing RejectedExecutionException, although a subset of the
|
||||||
|
* tasks may already have been executed.
|
||||||
|
*/
|
||||||
|
public static ExecutorService sameThreadExecutor() {
|
||||||
|
return new SameThreadExecutorService();
|
||||||
|
}
|
||||||
|
|
||||||
|
// See sameThreadExecutor javadoc for behavioral notes.
|
||||||
|
@SingleThreaded
|
||||||
|
public static class SameThreadExecutorService extends AbstractExecutorService {
|
||||||
|
/**
|
||||||
|
* Lock used whenever accessing the state variables (runningTasks, shutdown,
|
||||||
|
* terminationCondition) of the executor
|
||||||
|
*/
|
||||||
|
private final Lock lock = new ReentrantLock();
|
||||||
|
|
||||||
|
/** Signaled after the executor is shutdown and running tasks are done */
|
||||||
|
private final Condition termination = lock.newCondition();
|
||||||
|
|
||||||
|
private SameThreadExecutorService() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Conceptually, these two variables describe the executor being in one of three states: -
|
||||||
|
* Active: shutdown == false - Shutdown: runningTasks > 0 and shutdown == true - Terminated:
|
||||||
|
* runningTasks == 0 and shutdown == true
|
||||||
|
*/
|
||||||
|
private int runningTasks = 0;
|
||||||
|
private boolean shutdown = false;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(Runnable command) {
|
||||||
|
startTask();
|
||||||
|
try {
|
||||||
|
command.run();
|
||||||
|
} finally {
|
||||||
|
endTask();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isShutdown() {
|
||||||
|
lock.lock();
|
||||||
|
try {
|
||||||
|
return shutdown;
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void shutdown() {
|
||||||
|
lock.lock();
|
||||||
|
try {
|
||||||
|
shutdown = true;
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// See sameThreadExecutor javadoc for unusual behavior of this method.
|
||||||
|
@Override
|
||||||
|
public List<Runnable> shutdownNow() {
|
||||||
|
shutdown();
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isTerminated() {
|
||||||
|
lock.lock();
|
||||||
|
try {
|
||||||
|
return shutdown && runningTasks == 0;
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
|
||||||
|
long nanos = unit.toNanos(timeout);
|
||||||
|
lock.lock();
|
||||||
|
try {
|
||||||
|
for (;;) {
|
||||||
|
if (isTerminated()) {
|
||||||
|
return true;
|
||||||
|
} else if (nanos <= 0) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
nanos = termination.awaitNanos(nanos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the executor has been shut down and increments the running task count.
|
||||||
|
*
|
||||||
|
* @throws RejectedExecutionException
|
||||||
|
* if the executor has been previously shutdown
|
||||||
|
*/
|
||||||
|
private void startTask() {
|
||||||
|
lock.lock();
|
||||||
|
try {
|
||||||
|
if (isShutdown()) {
|
||||||
|
throw new RejectedExecutionException("Executor already shutdown");
|
||||||
|
}
|
||||||
|
runningTasks++;
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrements the running task count.
|
||||||
|
*/
|
||||||
|
private void endTask() {
|
||||||
|
lock.lock();
|
||||||
|
try {
|
||||||
|
runningTasks--;
|
||||||
|
if (isTerminated()) {
|
||||||
|
termination.signalAll();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -32,7 +32,7 @@ import javax.inject.Named;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
import org.jclouds.Constants;
|
import org.jclouds.Constants;
|
||||||
import static org.jclouds.concurrent.ConcurrentUtils.*;
|
import org.jclouds.concurrent.MoreExecutors;
|
||||||
import org.jclouds.concurrent.SingleThreaded;
|
import org.jclouds.concurrent.SingleThreaded;
|
||||||
import org.jclouds.lifecycle.Closer;
|
import org.jclouds.lifecycle.Closer;
|
||||||
import org.jclouds.logging.Logger;
|
import org.jclouds.logging.Logger;
|
||||||
|
@ -87,8 +87,8 @@ public class ExecutorServiceModule extends AbstractModule {
|
||||||
&& executor.getClass().getSimpleName().indexOf("SameThread") != -1) {
|
&& executor.getClass().getSimpleName().indexOf("SameThread") != -1) {
|
||||||
Logger.CONSOLE.warn(
|
Logger.CONSOLE.warn(
|
||||||
"please switch from %s to %s or annotate your same threaded executor with @SingleThreaded", executor
|
"please switch from %s to %s or annotate your same threaded executor with @SingleThreaded", executor
|
||||||
.getClass().getName(), SameThreadExecutorService.class.getName());
|
.getClass().getName(), MoreExecutors.SameThreadExecutorService.class.getName());
|
||||||
return sameThreadExecutor();
|
return MoreExecutors.sameThreadExecutor();
|
||||||
}
|
}
|
||||||
return executor;
|
return executor;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||||
|
*
|
||||||
|
* ====================================================================
|
||||||
|
* Licensed 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.crypto;
|
||||||
|
|
||||||
|
import java.security.InvalidKeyException;
|
||||||
|
import java.security.KeyFactory;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.cert.CertificateFactory;
|
||||||
|
|
||||||
|
import javax.crypto.Mac;
|
||||||
|
|
||||||
|
import org.jclouds.encryption.internal.JCECrypto;
|
||||||
|
|
||||||
|
import com.google.inject.ImplementedBy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows you to access cryptographic objects and factories without adding a provider to the JCE
|
||||||
|
* runtime.
|
||||||
|
*
|
||||||
|
* @author Adrian Cole
|
||||||
|
*/
|
||||||
|
@ImplementedBy(JCECrypto.class)
|
||||||
|
public interface Crypto {
|
||||||
|
|
||||||
|
KeyFactory rsaKeyFactory();
|
||||||
|
|
||||||
|
CertificateFactory certFactory();
|
||||||
|
|
||||||
|
Mac hmac(String algorithm, byte[] key) throws NoSuchAlgorithmException, InvalidKeyException;
|
||||||
|
|
||||||
|
Mac hmacSHA256(byte[] key) throws InvalidKeyException;
|
||||||
|
|
||||||
|
Mac hmacSHA1(byte[] key) throws InvalidKeyException;
|
||||||
|
|
||||||
|
MessageDigest digest(String algorithm) throws NoSuchAlgorithmException;
|
||||||
|
|
||||||
|
MessageDigest md5();
|
||||||
|
|
||||||
|
MessageDigest sha1();
|
||||||
|
|
||||||
|
MessageDigest sha256();
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,318 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||||
|
*
|
||||||
|
* ====================================================================
|
||||||
|
* Licensed 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.crypto;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import javax.crypto.Mac;
|
||||||
|
|
||||||
|
import org.jclouds.encryption.internal.Base64;
|
||||||
|
import org.jclouds.io.InputSuppliers;
|
||||||
|
|
||||||
|
import com.google.common.annotations.Beta;
|
||||||
|
import com.google.common.base.Charsets;
|
||||||
|
import com.google.common.base.Throwables;
|
||||||
|
import com.google.common.io.ByteProcessor;
|
||||||
|
import com.google.common.io.ByteStreams;
|
||||||
|
import com.google.common.io.InputSupplier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* functions related to but not in {@link com.google.common.io.ByteStreams}
|
||||||
|
*
|
||||||
|
* @author Adrian Cole
|
||||||
|
*/
|
||||||
|
@Beta
|
||||||
|
public class CryptoStreams {
|
||||||
|
|
||||||
|
public static String hex(byte[] in) {
|
||||||
|
byte[] hex = new byte[2 * in.length];
|
||||||
|
int index = 0;
|
||||||
|
|
||||||
|
for (byte b : in) {
|
||||||
|
int v = b & 0xFF;
|
||||||
|
hex[index++] = HEX_CHAR_TABLE[v >>> 4];
|
||||||
|
hex[index++] = HEX_CHAR_TABLE[v & 0xF];
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return new String(hex, "ASCII");
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] hex(String s) {
|
||||||
|
int len = s.length();
|
||||||
|
byte[] data = new byte[len / 2];
|
||||||
|
for (int i = 0; i < len; i += 2) {
|
||||||
|
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i + 1), 16));
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String base64(byte[] in) {
|
||||||
|
return Base64.encodeBytes(in, Base64.DONT_BREAK_LINES);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] base64(String in) {
|
||||||
|
return Base64.decode(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see #md5
|
||||||
|
* @see #hex
|
||||||
|
*/
|
||||||
|
public static String md5Hex(InputSupplier<? extends InputStream> supplier) throws IOException {
|
||||||
|
return hex(md5(supplier));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see #md5
|
||||||
|
* @see #base64
|
||||||
|
*/
|
||||||
|
public static String md5Base64(InputSupplier<? extends InputStream> supplier) throws IOException {
|
||||||
|
return base64(md5(supplier));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes and returns the MAC value for a supplied input stream. The mac object is reset when
|
||||||
|
* this method returns successfully.
|
||||||
|
*
|
||||||
|
* @param supplier
|
||||||
|
* the input stream factory
|
||||||
|
* @param mac
|
||||||
|
* the mac object
|
||||||
|
* @return the result of {@link Mac#doFinal()} after updating the mac object with all of the
|
||||||
|
* bytes in the stream and encoding in Base64
|
||||||
|
* @throws IOException
|
||||||
|
* if an I/O error occurs
|
||||||
|
*/
|
||||||
|
public static String macBase64(InputSupplier<? extends InputStream> supplier, final Mac mac) throws IOException {
|
||||||
|
return base64(mac(supplier, mac));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes and returns the Digest value for a supplied input stream. The digest object is reset
|
||||||
|
* when this method returns successfully.
|
||||||
|
*
|
||||||
|
* @param supplier
|
||||||
|
* the input stream factory
|
||||||
|
* @param md
|
||||||
|
* the digest object
|
||||||
|
* @return the result of {@link MessageDigest#digest()} after updating the digest object with all
|
||||||
|
* of the bytes in the stream
|
||||||
|
* @throws IOException
|
||||||
|
* if an I/O error occurs
|
||||||
|
*/
|
||||||
|
public static byte[] digest(InputSupplier<? extends InputStream> supplier, final MessageDigest md)
|
||||||
|
throws IOException {
|
||||||
|
return com.google.common.io.ByteStreams.readBytes(supplier, new ByteProcessor<byte[]>() {
|
||||||
|
public boolean processBytes(byte[] buf, int off, int len) {
|
||||||
|
md.update(buf, off, len);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getResult() {
|
||||||
|
return md.digest();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes and returns the MD5 value for a supplied input stream. A digest object is created and
|
||||||
|
* disposed of at runtime, consider using {@link #digest} to be more efficient.
|
||||||
|
*
|
||||||
|
* @param supplier
|
||||||
|
* the input stream factory
|
||||||
|
*
|
||||||
|
* @return the result of {@link MessageDigest#digest()} after updating the md5 object with all of
|
||||||
|
* the bytes in the stream
|
||||||
|
* @throws IOException
|
||||||
|
* if an I/O error occurs
|
||||||
|
*/
|
||||||
|
public static byte[] md5(InputSupplier<? extends InputStream> supplier) throws IOException {
|
||||||
|
try {
|
||||||
|
return digest(supplier, MessageDigest.getInstance("MD5"));
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
Throwables.propagate(e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] md5(byte[] in) throws IOException {
|
||||||
|
return md5(ByteStreams.newInputStreamSupplier(in));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes and returns the MAC value for a supplied input stream. The mac object is reset when
|
||||||
|
* this method returns successfully.
|
||||||
|
*
|
||||||
|
* @param supplier
|
||||||
|
* the input stream factory
|
||||||
|
* @param mac
|
||||||
|
* the mac object
|
||||||
|
* @return the result of {@link Mac#doFinal()} after updating the mac object with all of the
|
||||||
|
* bytes in the stream
|
||||||
|
* @throws IOException
|
||||||
|
* if an I/O error occurs
|
||||||
|
*/
|
||||||
|
public static byte[] mac(InputSupplier<? extends InputStream> supplier, final Mac mac) throws IOException {
|
||||||
|
return com.google.common.io.ByteStreams.readBytes(checkNotNull(supplier, "supplier"),
|
||||||
|
new ByteProcessor<byte[]>() {
|
||||||
|
public boolean processBytes(byte[] buf, int off, int len) {
|
||||||
|
mac.update(buf, off, len);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getResult() {
|
||||||
|
return mac.doFinal();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes and returns the base64 value for a supplied input stream.
|
||||||
|
*
|
||||||
|
* @param supplier
|
||||||
|
* the input stream factory
|
||||||
|
*
|
||||||
|
* @return the result of base 64 encoding all of the bytes in the stream
|
||||||
|
* @throws IOException
|
||||||
|
* if an I/O error occurs
|
||||||
|
*/
|
||||||
|
public static String base64Encode(InputSupplier<? extends InputStream> supplier) throws IOException {
|
||||||
|
final ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
return com.google.common.io.ByteStreams.readBytes(InputSuppliers.base64Encoder(supplier),
|
||||||
|
new ByteProcessor<String>() {
|
||||||
|
public boolean processBytes(byte[] buf, int off, int len) {
|
||||||
|
out.write(buf, off, len);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getResult() {
|
||||||
|
return new String(out.toByteArray(), Charsets.UTF_8);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes and returns the unencoded value for an input stream which is encoded in Base64.
|
||||||
|
*
|
||||||
|
* @param supplier
|
||||||
|
* the input stream factory
|
||||||
|
*
|
||||||
|
* @return the result of base 64 decoding all of the bytes in the stream
|
||||||
|
* @throws IOException
|
||||||
|
* if an I/O error occurs
|
||||||
|
*/
|
||||||
|
public static byte[] base64Decode(InputSupplier<? extends InputStream> supplier) throws IOException {
|
||||||
|
final ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
return com.google.common.io.ByteStreams.readBytes(InputSuppliers.base64Decoder(supplier),
|
||||||
|
new ByteProcessor<byte[]>() {
|
||||||
|
public boolean processBytes(byte[] buf, int off, int len) {
|
||||||
|
out.write(buf, off, len);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getResult() {
|
||||||
|
return out.toByteArray();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
final static byte[] HEX_CHAR_TABLE = { (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5',
|
||||||
|
(byte) '6', (byte) '7', (byte) '8', (byte) '9', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e',
|
||||||
|
(byte) 'f' };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes and returns the hex value for a supplied input stream.
|
||||||
|
*
|
||||||
|
* @param supplier
|
||||||
|
* the input stream factory
|
||||||
|
*
|
||||||
|
* @return the result of hex encoding all of the bytes in the stream
|
||||||
|
* @throws IOException
|
||||||
|
* if an I/O error occurs
|
||||||
|
*/
|
||||||
|
public static String hexEncode(InputSupplier<? extends InputStream> supplier) throws IOException {
|
||||||
|
final StringBuilder out = new StringBuilder();
|
||||||
|
return com.google.common.io.ByteStreams.readBytes(supplier, new ByteProcessor<String>() {
|
||||||
|
public boolean processBytes(byte[] buf, int off, int len) {
|
||||||
|
char[] hex = new char[2 * len];
|
||||||
|
int index = 0;
|
||||||
|
|
||||||
|
for (int i = off; i < off + len; i++) {
|
||||||
|
byte b = buf[i];
|
||||||
|
int v = b & 0xFF;
|
||||||
|
hex[index++] = (char) HEX_CHAR_TABLE[v >>> 4];
|
||||||
|
hex[index++] = (char) HEX_CHAR_TABLE[v & 0xF];
|
||||||
|
}
|
||||||
|
out.append(hex);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getResult() {
|
||||||
|
return out.toString();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes and returns the unencoded value for an input stream which is encoded in hex.
|
||||||
|
*
|
||||||
|
* @param supplier
|
||||||
|
* the input stream factory
|
||||||
|
*
|
||||||
|
* @return the result of hex decoding all of the bytes in the stream
|
||||||
|
* @throws IOException
|
||||||
|
* if an I/O error occurs
|
||||||
|
*/
|
||||||
|
public static byte[] hexDecode(InputSupplier<? extends InputStream> supplier) throws IOException {
|
||||||
|
final ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
return com.google.common.io.ByteStreams.readBytes(supplier, new ByteProcessor<byte[]>() {
|
||||||
|
int currentPos = 0;
|
||||||
|
|
||||||
|
public boolean processBytes(byte[] buf, int off, int len) {
|
||||||
|
try {
|
||||||
|
if (currentPos == 0 && new String(Arrays.copyOfRange(buf, off, 2), "ASCII").equals("0x")) {
|
||||||
|
off += 2;
|
||||||
|
}
|
||||||
|
byte[] decoded = hex(new String(Arrays.copyOfRange(buf, off, len), "ASCII"));
|
||||||
|
out.write(decoded, 0, decoded.length);
|
||||||
|
currentPos += len;
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
throw new IllegalStateException("ASCII must be supported");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getResult() {
|
||||||
|
return out.toByteArray();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,252 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||||
|
*
|
||||||
|
* ====================================================================
|
||||||
|
* Licensed 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.crypto;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.security.PrivateKey;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
import java.security.cert.CertificateEncodingException;
|
||||||
|
import java.security.cert.CertificateException;
|
||||||
|
import java.security.cert.CertificateFactory;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
import java.security.spec.KeySpec;
|
||||||
|
import java.security.spec.PKCS8EncodedKeySpec;
|
||||||
|
import java.security.spec.RSAPrivateKeySpec;
|
||||||
|
import java.security.spec.X509EncodedKeySpec;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import net.oauth.signature.pem.PEMReader;
|
||||||
|
import net.oauth.signature.pem.PKCS1EncodedKeySpec;
|
||||||
|
|
||||||
|
import org.jclouds.crypto.Pems.PemProcessor.ResultParser;
|
||||||
|
|
||||||
|
import com.google.common.annotations.Beta;
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.google.common.io.ByteProcessor;
|
||||||
|
import com.google.common.io.ByteStreams;
|
||||||
|
import com.google.common.io.InputSupplier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads and writes PEM encoded Strings and Streams
|
||||||
|
*
|
||||||
|
* @author Adrian Cole
|
||||||
|
*/
|
||||||
|
@Beta
|
||||||
|
public class Pems {
|
||||||
|
|
||||||
|
public static class PemProcessor<T> implements com.google.common.io.ByteProcessor<T> {
|
||||||
|
public interface ResultParser<T> {
|
||||||
|
T parseResult(byte[] bytes) throws IOException;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
private final Map<String, ResultParser<T>> parsers;
|
||||||
|
|
||||||
|
public PemProcessor(Map<String, ResultParser<T>> parsers) {
|
||||||
|
this.parsers = checkNotNull(parsers, "parsers");
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean processBytes(byte[] buf, int off, int len) {
|
||||||
|
out.write(buf, off, len);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T getResult() {
|
||||||
|
try {
|
||||||
|
PEMReader reader = new PEMReader(out.toByteArray());
|
||||||
|
byte[] bytes = reader.getDerBytes();
|
||||||
|
if (parsers.containsKey(reader.getBeginMarker())) {
|
||||||
|
return parsers.get(reader.getBeginMarker()).parseResult(bytes);
|
||||||
|
} else {
|
||||||
|
throw new IOException(String.format("Invalid PEM file: no parsers for marker %s in %s", reader
|
||||||
|
.getBeginMarker(), parsers.keySet()));
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the object of generic type {@code T} that is pem encoded in the supplier.
|
||||||
|
*
|
||||||
|
* @param supplier
|
||||||
|
* the input stream factory
|
||||||
|
* @param marker
|
||||||
|
* header that begins the PEM block
|
||||||
|
* @param processor
|
||||||
|
* how to parser the object from a byte array
|
||||||
|
* @return the object of generic type {@code T} which was PEM encoded in the stream
|
||||||
|
* @throws IOException
|
||||||
|
* if an I/O error occurs
|
||||||
|
*/
|
||||||
|
public static <T> T fromPem(InputSupplier<? extends InputStream> supplier, PemProcessor<T> processor)
|
||||||
|
throws IOException {
|
||||||
|
try {
|
||||||
|
return com.google.common.io.ByteStreams.readBytes(supplier, processor);
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
if (e.getCause() != null && e.getCause() instanceof IOException) {
|
||||||
|
throw (IOException) e.getCause();
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the {@link RSAPrivateKeySpec} that is pem encoded in the supplier.
|
||||||
|
*
|
||||||
|
* @param supplier
|
||||||
|
* the input stream factory
|
||||||
|
*
|
||||||
|
* @return the {@link RSAPrivateKeySpec} which was PEM encoded in the stream
|
||||||
|
* @throws IOException
|
||||||
|
* if an I/O error occurs
|
||||||
|
*/
|
||||||
|
public static KeySpec privateKeySpec(InputSupplier<? extends InputStream> supplier) throws IOException {
|
||||||
|
return fromPem(supplier, new PemProcessor<KeySpec>(ImmutableMap.<String, ResultParser<KeySpec>> of(
|
||||||
|
PEMReader.PRIVATE_PKCS1_MARKER, new ResultParser<KeySpec>() {
|
||||||
|
|
||||||
|
public KeySpec parseResult(byte[] bytes) throws IOException {
|
||||||
|
return (new PKCS1EncodedKeySpec(bytes)).getKeySpec();
|
||||||
|
}
|
||||||
|
|
||||||
|
}, PEMReader.PRIVATE_PKCS8_MARKER, new ResultParser<KeySpec>() {
|
||||||
|
|
||||||
|
public KeySpec parseResult(byte[] bytes) throws IOException {
|
||||||
|
return new PKCS8EncodedKeySpec(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
})));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the {@link X509EncodedKeySpec} that is pem encoded in the supplier.
|
||||||
|
*
|
||||||
|
* @param supplier
|
||||||
|
* the input stream factory
|
||||||
|
*
|
||||||
|
* @return the {@link X509EncodedKeySpec} which was PEM encoded in the stream
|
||||||
|
* @throws IOException
|
||||||
|
* if an I/O error occurs
|
||||||
|
*/
|
||||||
|
public static X509EncodedKeySpec publicKeySpec(InputSupplier<? extends InputStream> supplier) throws IOException {
|
||||||
|
return fromPem(supplier, new PemProcessor<X509EncodedKeySpec>(ImmutableMap
|
||||||
|
.<String, ResultParser<X509EncodedKeySpec>> of(PEMReader.PUBLIC_X509_MARKER,
|
||||||
|
new ResultParser<X509EncodedKeySpec>() {
|
||||||
|
|
||||||
|
public X509EncodedKeySpec parseResult(byte[] bytes) throws IOException {
|
||||||
|
return new X509EncodedKeySpec(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
})));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the {@link X509EncodedKeySpec} that is pem encoded in the supplier.
|
||||||
|
*
|
||||||
|
* @param supplier
|
||||||
|
* the input stream factory
|
||||||
|
* @param certFactory
|
||||||
|
* or null to use default
|
||||||
|
*
|
||||||
|
* @return the {@link X509EncodedKeySpec} which was PEM encoded in the stream
|
||||||
|
* @throws IOException
|
||||||
|
* if an I/O error occurs
|
||||||
|
* @throws CertificateException
|
||||||
|
*/
|
||||||
|
public static X509Certificate x509Certificate(InputSupplier<? extends InputStream> supplier,
|
||||||
|
@Nullable CertificateFactory certFactory) throws IOException, CertificateException {
|
||||||
|
final CertificateFactory finalCertFactory = certFactory != null ? certFactory : CertificateFactory
|
||||||
|
.getInstance("X.509");
|
||||||
|
try {
|
||||||
|
return fromPem(supplier, new PemProcessor<X509Certificate>(ImmutableMap
|
||||||
|
.<String, ResultParser<X509Certificate>> of(PEMReader.CERTIFICATE_X509_MARKER,
|
||||||
|
new ResultParser<X509Certificate>() {
|
||||||
|
|
||||||
|
public X509Certificate parseResult(byte[] bytes) throws IOException {
|
||||||
|
try {
|
||||||
|
return (X509Certificate) finalCertFactory
|
||||||
|
.generateCertificate(new ByteArrayInputStream(bytes));
|
||||||
|
} catch (CertificateException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
})));
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
if (e.getCause() != null && e.getCause() instanceof CertificateException) {
|
||||||
|
throw (CertificateException) e.getCause();
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* encodes the {@link X509Certificate} to PEM format.
|
||||||
|
*
|
||||||
|
* @param cert
|
||||||
|
* what to encode
|
||||||
|
* @return the PEM encoded certificate
|
||||||
|
* @throws IOException
|
||||||
|
* @throws CertificateEncodingException
|
||||||
|
*/
|
||||||
|
public static String pem(X509Certificate cert) throws IOException, CertificateEncodingException {
|
||||||
|
return new StringBuilder("-----BEGIN CERTIFICATE-----\n").append(
|
||||||
|
CryptoStreams.base64Encode(ByteStreams.newInputStreamSupplier(cert.getEncoded()))).append(
|
||||||
|
"\n-----END CERTIFICATE-----\n").toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* encodes the {@link PublicKey} to PEM format.
|
||||||
|
*
|
||||||
|
* @param cert
|
||||||
|
* what to encode
|
||||||
|
* @return the PEM encoded public key
|
||||||
|
* @throws IOException
|
||||||
|
* @throws CertificateEncodingException
|
||||||
|
*/
|
||||||
|
public static String pem(PublicKey key) throws IOException {
|
||||||
|
return new StringBuilder("-----BEGIN PUBLIC KEY-----\n").append(
|
||||||
|
CryptoStreams.base64Encode(ByteStreams.newInputStreamSupplier(key.getEncoded()))).append(
|
||||||
|
"\n-----END PUBLIC KEY-----\n").toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* encodes the {@link PrivateKey} to PEM format. Note
|
||||||
|
*
|
||||||
|
* @param cert
|
||||||
|
* what to encode
|
||||||
|
* @return the PEM encoded private key
|
||||||
|
* @throws IOException
|
||||||
|
* @throws CertificateEncodingException
|
||||||
|
*/
|
||||||
|
public static String pem(PrivateKey key) throws IOException {
|
||||||
|
return new StringBuilder("-----BEGIN PRIVATE KEY-----\n").append(
|
||||||
|
CryptoStreams.base64Encode(ByteStreams.newInputStreamSupplier(key.getEncoded()))).append(
|
||||||
|
"\n-----END PRIVATE KEY-----\n").toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,102 +0,0 @@
|
||||||
/**
|
|
||||||
*
|
|
||||||
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
|
|
||||||
*
|
|
||||||
* ====================================================================
|
|
||||||
* Licensed 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.encryption;
|
|
||||||
|
|
||||||
import java.io.FilterOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.security.Key;
|
|
||||||
import java.security.PrivateKey;
|
|
||||||
import java.security.PublicKey;
|
|
||||||
import java.security.cert.X509Certificate;
|
|
||||||
|
|
||||||
import org.jclouds.encryption.internal.JCEEncryptionService;
|
|
||||||
import org.jclouds.http.PayloadEnclosing;
|
|
||||||
import org.jclouds.io.Payload;
|
|
||||||
import org.jclouds.io.payloads.ByteArrayPayload;
|
|
||||||
|
|
||||||
import com.google.inject.ImplementedBy;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author Adrian Cole
|
|
||||||
*/
|
|
||||||
@ImplementedBy(JCEEncryptionService.class)
|
|
||||||
public interface EncryptionService {
|
|
||||||
String base64(byte[] toEncode);
|
|
||||||
|
|
||||||
byte[] fromBase64(String encoded);
|
|
||||||
|
|
||||||
String hex(byte[] toEncode);
|
|
||||||
|
|
||||||
byte[] fromHex(String encoded);
|
|
||||||
|
|
||||||
byte[] rsaEncrypt(Payload payload, Key key);
|
|
||||||
|
|
||||||
byte[] hmacSha256(String toEncode, byte[] key);
|
|
||||||
|
|
||||||
byte[] hmacSha1(String toEncode, byte[] key);
|
|
||||||
|
|
||||||
byte[] sha1(InputStream toEncode);
|
|
||||||
|
|
||||||
byte[] sha256(InputStream toEncode);
|
|
||||||
|
|
||||||
byte[] md5(InputStream toEncode);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* generate an MD5 Hash for the current data.
|
|
||||||
* <p/>
|
|
||||||
* <h2>Note</h2>
|
|
||||||
* <p/>
|
|
||||||
* If this is an InputStream, it will be converted to a byte array first.
|
|
||||||
*
|
|
||||||
* @throws IOException
|
|
||||||
*/
|
|
||||||
<T extends PayloadEnclosing> T generateMD5BufferingIfNotRepeatable(T payloadEnclosing);
|
|
||||||
|
|
||||||
Payload generateMD5BufferingIfNotRepeatable(Payload in);
|
|
||||||
|
|
||||||
ByteArrayPayload generatePayloadWithMD5For(InputStream toEncode);
|
|
||||||
|
|
||||||
MD5OutputStream md5OutputStream(OutputStream out);
|
|
||||||
|
|
||||||
PrivateKey privateKeyFromPEM(byte[] pem);
|
|
||||||
|
|
||||||
public static abstract class MD5OutputStream extends FilterOutputStream {
|
|
||||||
public MD5OutputStream(OutputStream out) {
|
|
||||||
super(out);
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract byte[] getMD5();
|
|
||||||
}
|
|
||||||
|
|
||||||
PublicKey publicKeyFromPEM(byte[] pem);
|
|
||||||
|
|
||||||
X509Certificate x509CertificateFromPEM(byte[] pem);
|
|
||||||
|
|
||||||
byte[] rsaDecrypt(Payload payload, Key key);
|
|
||||||
|
|
||||||
String toPem(X509Certificate cert);
|
|
||||||
|
|
||||||
String toPem(PublicKey key);
|
|
||||||
|
|
||||||
String toPem(PrivateKey key);
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,299 +0,0 @@
|
||||||
/**
|
|
||||||
*
|
|
||||||
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
|
|
||||||
*
|
|
||||||
* ====================================================================
|
|
||||||
* Licensed 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.encryption.internal;
|
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
|
||||||
import static com.google.common.base.Preconditions.checkState;
|
|
||||||
import static com.google.common.base.Throwables.propagate;
|
|
||||||
import static com.google.common.io.Closeables.closeQuietly;
|
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.UnsupportedEncodingException;
|
|
||||||
import java.security.InvalidKeyException;
|
|
||||||
import java.security.Key;
|
|
||||||
import java.security.KeyFactory;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.security.PrivateKey;
|
|
||||||
import java.security.PublicKey;
|
|
||||||
import java.security.cert.CertificateEncodingException;
|
|
||||||
import java.security.cert.CertificateFactory;
|
|
||||||
import java.security.cert.X509Certificate;
|
|
||||||
import java.security.spec.KeySpec;
|
|
||||||
import java.security.spec.PKCS8EncodedKeySpec;
|
|
||||||
import java.security.spec.X509EncodedKeySpec;
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
|
||||||
import javax.crypto.BadPaddingException;
|
|
||||||
import javax.crypto.Cipher;
|
|
||||||
import javax.crypto.IllegalBlockSizeException;
|
|
||||||
import javax.crypto.NoSuchPaddingException;
|
|
||||||
import javax.crypto.ShortBufferException;
|
|
||||||
|
|
||||||
import net.oauth.signature.pem.PEMReader;
|
|
||||||
import net.oauth.signature.pem.PKCS1EncodedKeySpec;
|
|
||||||
|
|
||||||
import org.jclouds.encryption.EncryptionService;
|
|
||||||
import org.jclouds.http.PayloadEnclosing;
|
|
||||||
import org.jclouds.io.Payload;
|
|
||||||
import org.jclouds.logging.Logger;
|
|
||||||
|
|
||||||
import com.google.common.base.Throwables;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author Adrian Cole
|
|
||||||
*/
|
|
||||||
public abstract class BaseEncryptionService implements EncryptionService {
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
protected Logger logger = Logger.NULL;
|
|
||||||
|
|
||||||
protected static final int BUF_SIZE = 0x2000; // 8
|
|
||||||
|
|
||||||
final byte[] HEX_CHAR_TABLE = { (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6',
|
|
||||||
(byte) '7', (byte) '8', (byte) '9', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f' };
|
|
||||||
|
|
||||||
private final KeyFactory rsaKeyFactory;
|
|
||||||
private final CertificateFactory certFactory;
|
|
||||||
|
|
||||||
public BaseEncryptionService(KeyFactory rsaKeyFactory, CertificateFactory certFactory) {
|
|
||||||
this.rsaKeyFactory = rsaKeyFactory;
|
|
||||||
this.certFactory = certFactory;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String hex(byte[] raw) {
|
|
||||||
byte[] hex = new byte[2 * raw.length];
|
|
||||||
int index = 0;
|
|
||||||
|
|
||||||
for (byte b : raw) {
|
|
||||||
int v = b & 0xFF;
|
|
||||||
hex[index++] = HEX_CHAR_TABLE[v >>> 4];
|
|
||||||
hex[index++] = HEX_CHAR_TABLE[v & 0xF];
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return new String(hex, "ASCII");
|
|
||||||
} catch (UnsupportedEncodingException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] fromHex(String hex) {
|
|
||||||
if (hex.startsWith("0x"))
|
|
||||||
hex = hex.substring(2);
|
|
||||||
byte[] bytes = new byte[hex.length() / 2];
|
|
||||||
for (int i = 0; i < bytes.length; i++) {
|
|
||||||
bytes[i] = (byte) Integer.parseInt(hex.substring(2 * i, 2 * i + 2), 16);
|
|
||||||
}
|
|
||||||
return bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Payload generateMD5BufferingIfNotRepeatable(Payload payload) {
|
|
||||||
checkNotNull(payload, "payload");
|
|
||||||
if (!payload.isRepeatable()) {
|
|
||||||
String oldContentType = payload.getContentType();
|
|
||||||
payload = generatePayloadWithMD5For(payload.getInput());
|
|
||||||
payload.setContentType(oldContentType);
|
|
||||||
} else {
|
|
||||||
payload.setContentMD5(md5(payload.getInput()));
|
|
||||||
}
|
|
||||||
return payload;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public <T extends PayloadEnclosing> T generateMD5BufferingIfNotRepeatable(T payloadEnclosing) {
|
|
||||||
checkState(payloadEnclosing != null, "payloadEnclosing");
|
|
||||||
Payload newPayload = generateMD5BufferingIfNotRepeatable(payloadEnclosing.getPayload());
|
|
||||||
if (newPayload != payloadEnclosing.getPayload())
|
|
||||||
payloadEnclosing.setPayload(newPayload);
|
|
||||||
return payloadEnclosing;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public PrivateKey privateKeyFromPEM(byte[] pem) {
|
|
||||||
PEMReader reader;
|
|
||||||
try {
|
|
||||||
reader = new PEMReader(pem);
|
|
||||||
|
|
||||||
byte[] bytes = reader.getDerBytes();
|
|
||||||
KeySpec keySpec;
|
|
||||||
|
|
||||||
if (PEMReader.PRIVATE_PKCS1_MARKER.equals(reader.getBeginMarker())) {
|
|
||||||
keySpec = (new PKCS1EncodedKeySpec(bytes)).getKeySpec();
|
|
||||||
} else if (PEMReader.PRIVATE_PKCS8_MARKER.equals(reader.getBeginMarker())) {
|
|
||||||
keySpec = new PKCS8EncodedKeySpec(bytes);
|
|
||||||
} else {
|
|
||||||
throw new IOException("Invalid PEM file: Unknown marker for private key " + reader.getBeginMarker());
|
|
||||||
}
|
|
||||||
return rsaKeyFactory.generatePrivate(keySpec);
|
|
||||||
} catch (Exception e) {
|
|
||||||
Throwables.propagate(e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public PublicKey publicKeyFromPEM(byte[] pem) {
|
|
||||||
PEMReader reader;
|
|
||||||
try {
|
|
||||||
reader = new PEMReader(pem);
|
|
||||||
|
|
||||||
byte[] bytes = reader.getDerBytes();
|
|
||||||
KeySpec keySpec;
|
|
||||||
|
|
||||||
if (PEMReader.PUBLIC_X509_MARKER.equals(reader.getBeginMarker())) {
|
|
||||||
keySpec = new X509EncodedKeySpec(bytes);
|
|
||||||
} else {
|
|
||||||
throw new IOException("Invalid PEM file: Unknown marker for public key " + reader.getBeginMarker());
|
|
||||||
}
|
|
||||||
return rsaKeyFactory.generatePublic(keySpec);
|
|
||||||
} catch (Exception e) {
|
|
||||||
Throwables.propagate(e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public X509Certificate x509CertificateFromPEM(byte[] pem) {
|
|
||||||
PEMReader reader;
|
|
||||||
try {
|
|
||||||
reader = new PEMReader(pem);
|
|
||||||
|
|
||||||
byte[] bytes = reader.getDerBytes();
|
|
||||||
|
|
||||||
if (PEMReader.CERTIFICATE_X509_MARKER.equals(reader.getBeginMarker())) {
|
|
||||||
return (X509Certificate) certFactory.generateCertificate(new ByteArrayInputStream(bytes));
|
|
||||||
} else {
|
|
||||||
throw new IOException("Invalid PEM file: Unknown marker for public key " + reader.getBeginMarker());
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
Throwables.propagate(e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] rsaEncrypt(Payload payload, Key key) {
|
|
||||||
// TODO convert this to BC code
|
|
||||||
Cipher cipher = null;
|
|
||||||
try {
|
|
||||||
cipher = Cipher.getInstance("RSA");
|
|
||||||
cipher.init(Cipher.ENCRYPT_MODE, key);
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
Throwables.propagate(e);
|
|
||||||
} catch (NoSuchPaddingException e) {
|
|
||||||
Throwables.propagate(e);
|
|
||||||
} catch (InvalidKeyException e) {
|
|
||||||
Throwables.propagate(e);
|
|
||||||
}
|
|
||||||
return cipherPayload(cipher, payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] rsaDecrypt(Payload payload, Key key) {
|
|
||||||
// TODO convert this to BC code
|
|
||||||
Cipher cipher = null;
|
|
||||||
try {
|
|
||||||
cipher = Cipher.getInstance("RSA");
|
|
||||||
cipher.init(Cipher.DECRYPT_MODE, key);
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
Throwables.propagate(e);
|
|
||||||
} catch (NoSuchPaddingException e) {
|
|
||||||
Throwables.propagate(e);
|
|
||||||
} catch (InvalidKeyException e) {
|
|
||||||
Throwables.propagate(e);
|
|
||||||
}
|
|
||||||
return cipherPayload(cipher, payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] cipherPayload(Cipher cipher, Payload payload) {
|
|
||||||
byte[] resBuf = new byte[cipher.getOutputSize(payload.getContentLength().intValue())];
|
|
||||||
byte[] buffer = new byte[BUF_SIZE];
|
|
||||||
long length = 0;
|
|
||||||
int numRead = -1;
|
|
||||||
InputStream plainBytes = payload.getInput();
|
|
||||||
try {
|
|
||||||
do {
|
|
||||||
numRead = plainBytes.read(buffer);
|
|
||||||
if (numRead > 0) {
|
|
||||||
length += numRead;
|
|
||||||
cipher.update(buffer, 0, numRead);
|
|
||||||
}
|
|
||||||
} while (numRead != -1);
|
|
||||||
} catch (IOException e) {
|
|
||||||
propagate(e);
|
|
||||||
} finally {
|
|
||||||
closeQuietly(plainBytes);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
int size = cipher.doFinal(resBuf, 0);
|
|
||||||
return Arrays.copyOfRange(resBuf, 0, size);
|
|
||||||
} catch (IllegalBlockSizeException e) {
|
|
||||||
Throwables.propagate(e);
|
|
||||||
} catch (ShortBufferException e) {
|
|
||||||
Throwables.propagate(e);
|
|
||||||
} catch (BadPaddingException e) {
|
|
||||||
Throwables.propagate(e);
|
|
||||||
}
|
|
||||||
assert false;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toPem(X509Certificate cert) {
|
|
||||||
try {
|
|
||||||
return new StringBuilder("-----BEGIN CERTIFICATE-----\n").append(base64(cert.getEncoded())).append(
|
|
||||||
"\n-----END CERTIFICATE-----\n").toString();
|
|
||||||
} catch (CertificateEncodingException e) {
|
|
||||||
Throwables.propagate(e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toPem(PublicKey key) {
|
|
||||||
return new StringBuilder("-----BEGIN PUBLIC KEY-----\n").append(base64(key.getEncoded())).append(
|
|
||||||
"\n-----END PUBLIC KEY-----\n").toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toPem(PrivateKey key) {
|
|
||||||
return new StringBuilder("-----BEGIN PRIVATE KEY-----\n").append(base64(key.getEncoded())).append(
|
|
||||||
"\n-----END PRIVATE KEY-----\n").toString();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,135 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||||
|
*
|
||||||
|
* ====================================================================
|
||||||
|
* Licensed 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.encryption.internal;
|
||||||
|
|
||||||
|
import java.security.InvalidKeyException;
|
||||||
|
import java.security.KeyFactory;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.Provider;
|
||||||
|
import java.security.cert.CertificateException;
|
||||||
|
import java.security.cert.CertificateFactory;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import javax.crypto.Mac;
|
||||||
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
import org.jclouds.crypto.Crypto;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Adrian Cole
|
||||||
|
*/
|
||||||
|
@Singleton
|
||||||
|
public class JCECrypto implements Crypto {
|
||||||
|
|
||||||
|
private final KeyFactory rsaKeyFactory;
|
||||||
|
private final CertificateFactory certFactory;
|
||||||
|
private final Provider provider;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public JCECrypto() throws NoSuchAlgorithmException, CertificateException {
|
||||||
|
this(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public JCECrypto(@Nullable Provider provider) throws NoSuchAlgorithmException, CertificateException {
|
||||||
|
this.rsaKeyFactory = provider == null ? KeyFactory.getInstance("RSA") : KeyFactory.getInstance("RSA", provider);
|
||||||
|
this.certFactory = provider == null ? CertificateFactory.getInstance("X.509") : CertificateFactory.getInstance(
|
||||||
|
"X.509", provider);
|
||||||
|
this.provider = provider;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mac hmac(String algorithm, byte[] key) throws NoSuchAlgorithmException, InvalidKeyException {
|
||||||
|
Mac mac = provider == null ? Mac.getInstance(algorithm) : Mac.getInstance(algorithm, provider);
|
||||||
|
SecretKeySpec signingKey = new SecretKeySpec(key, algorithm);
|
||||||
|
mac.init(signingKey);
|
||||||
|
return mac;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MessageDigest digest(String algorithm) throws NoSuchAlgorithmException {
|
||||||
|
return provider == null ? MessageDigest.getInstance(algorithm) : MessageDigest.getInstance(algorithm, provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
public final static String MD5 = "MD5";
|
||||||
|
public final static String SHA1 = "SHA1";
|
||||||
|
public final static String SHA256 = "SHA256";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MessageDigest md5() {
|
||||||
|
try {
|
||||||
|
return digest(MD5);
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
throw new IllegalStateException("MD5 must be supported", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MessageDigest sha1() {
|
||||||
|
try {
|
||||||
|
return digest(SHA1);
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
throw new IllegalStateException("MD5 must be supported", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MessageDigest sha256() {
|
||||||
|
try {
|
||||||
|
return digest(SHA256);
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
throw new IllegalStateException("SHA256 must be supported", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public final static String HmacSHA256 = "HmacSHA256";
|
||||||
|
public final static String HmacSHA1 = "HmacSHA1";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mac hmacSHA1(byte[] key) throws InvalidKeyException {
|
||||||
|
try {
|
||||||
|
return hmac(HmacSHA1, key);
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
throw new IllegalStateException("HmacSHA1 must be supported", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mac hmacSHA256(byte[] key) throws InvalidKeyException {
|
||||||
|
try {
|
||||||
|
return hmac(HmacSHA256, key);
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
throw new IllegalStateException("HmacSHA256 must be supported", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CertificateFactory certFactory() {
|
||||||
|
return certFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KeyFactory rsaKeyFactory() {
|
||||||
|
return rsaKeyFactory;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,202 +0,0 @@
|
||||||
/**
|
|
||||||
*
|
|
||||||
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
|
|
||||||
*
|
|
||||||
* ====================================================================
|
|
||||||
* Licensed 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.encryption.internal;
|
|
||||||
|
|
||||||
import static com.google.common.base.Throwables.propagate;
|
|
||||||
import static com.google.common.io.Closeables.closeQuietly;
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.security.DigestOutputStream;
|
|
||||||
import java.security.InvalidKeyException;
|
|
||||||
import java.security.KeyFactory;
|
|
||||||
import java.security.MessageDigest;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.security.cert.CertificateException;
|
|
||||||
import java.security.cert.CertificateFactory;
|
|
||||||
|
|
||||||
import javax.crypto.Mac;
|
|
||||||
import javax.crypto.spec.SecretKeySpec;
|
|
||||||
|
|
||||||
import org.jclouds.io.payloads.ByteArrayPayload;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author Adrian Cole
|
|
||||||
*/
|
|
||||||
public class JCEEncryptionService extends BaseEncryptionService {
|
|
||||||
|
|
||||||
public JCEEncryptionService(KeyFactory rsaKeyFactory, CertificateFactory certFactory) {
|
|
||||||
super(rsaKeyFactory, certFactory);
|
|
||||||
}
|
|
||||||
|
|
||||||
public JCEEncryptionService() throws NoSuchAlgorithmException, CertificateException {
|
|
||||||
this(KeyFactory.getInstance("RSA"), CertificateFactory.getInstance("X.509"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] hmacSha256(String toEncode, byte[] key) {
|
|
||||||
return hmac(toEncode, key, "HmacSHA256");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] hmacSha1(String toEncode, byte[] key) {
|
|
||||||
return hmac(toEncode, key, "HmacSHA1");
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] hmac(String toEncode, byte[] key, String algorithm) {
|
|
||||||
SecretKeySpec signingKey = new SecretKeySpec(key, algorithm);
|
|
||||||
|
|
||||||
Mac mac = null;
|
|
||||||
try {
|
|
||||||
mac = Mac.getInstance(algorithm);
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
throw new RuntimeException("Could not find the " + algorithm + " algorithm", e);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
mac.init(signingKey);
|
|
||||||
} catch (InvalidKeyException e) {
|
|
||||||
throw new RuntimeException("Could not initialize the " + algorithm + " algorithm", e);
|
|
||||||
}
|
|
||||||
return mac.doFinal(toEncode.getBytes());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] md5(InputStream toEncode) {
|
|
||||||
MessageDigest eTag = getDigest();
|
|
||||||
byte[] buffer = new byte[BUF_SIZE];
|
|
||||||
int numRead = -1;
|
|
||||||
try {
|
|
||||||
do {
|
|
||||||
numRead = toEncode.read(buffer);
|
|
||||||
if (numRead > 0) {
|
|
||||||
eTag.update(buffer, 0, numRead);
|
|
||||||
}
|
|
||||||
} while (numRead != -1);
|
|
||||||
} catch (IOException e) {
|
|
||||||
propagate(e);
|
|
||||||
} finally {
|
|
||||||
closeQuietly(toEncode);
|
|
||||||
}
|
|
||||||
return eTag.digest();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String base64(byte[] resBuf) {
|
|
||||||
return Base64.encodeBytes(resBuf, Base64.DONT_BREAK_LINES);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] fromBase64(String encoded) {
|
|
||||||
return Base64.decode(encoded);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ByteArrayPayload generatePayloadWithMD5For(InputStream toEncode) {
|
|
||||||
MessageDigest eTag = getDigest();
|
|
||||||
byte[] buffer = new byte[BUF_SIZE];
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
||||||
long length = 0;
|
|
||||||
int numRead = -1;
|
|
||||||
try {
|
|
||||||
do {
|
|
||||||
numRead = toEncode.read(buffer);
|
|
||||||
if (numRead > 0) {
|
|
||||||
length += numRead;
|
|
||||||
eTag.update(buffer, 0, numRead);
|
|
||||||
out.write(buffer, 0, numRead);
|
|
||||||
}
|
|
||||||
} while (numRead != -1);
|
|
||||||
} catch (IOException e) {
|
|
||||||
propagate(e);
|
|
||||||
} finally {
|
|
||||||
closeQuietly(out);
|
|
||||||
closeQuietly(toEncode);
|
|
||||||
}
|
|
||||||
return new ByteArrayPayload(out.toByteArray(), eTag.digest());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static MessageDigest getDigest() {
|
|
||||||
MessageDigest eTag;
|
|
||||||
try {
|
|
||||||
eTag = MessageDigest.getInstance("MD5");
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
throw new RuntimeException("Could not find the MD5 algorithm", e);
|
|
||||||
}
|
|
||||||
return eTag;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MD5OutputStream md5OutputStream(OutputStream out) {
|
|
||||||
return new JCEMD5OutputStream(out);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class JCEMD5OutputStream extends MD5OutputStream {
|
|
||||||
public JCEMD5OutputStream(OutputStream out) {
|
|
||||||
super(new DigestOutputStream(out, getDigest()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] getMD5() {
|
|
||||||
MessageDigest digest = ((DigestOutputStream) out).getMessageDigest();
|
|
||||||
return digest.digest();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] sha1(InputStream plainBytes) {
|
|
||||||
return digest(plainBytes, "SHA1");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] sha256(InputStream plainBytes) {
|
|
||||||
return digest(plainBytes, "SHA256");
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] digest(InputStream plainBytes, String algorithm) {
|
|
||||||
MessageDigest digest;
|
|
||||||
try {
|
|
||||||
digest = MessageDigest.getInstance(algorithm);
|
|
||||||
} catch (NoSuchAlgorithmException e1) {
|
|
||||||
propagate(e1);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
byte[] buffer = new byte[BUF_SIZE];
|
|
||||||
long length = 0;
|
|
||||||
int numRead = -1;
|
|
||||||
try {
|
|
||||||
do {
|
|
||||||
numRead = plainBytes.read(buffer);
|
|
||||||
if (numRead > 0) {
|
|
||||||
length += numRead;
|
|
||||||
digest.update(buffer, 0, numRead);
|
|
||||||
}
|
|
||||||
} while (numRead != -1);
|
|
||||||
} catch (IOException e) {
|
|
||||||
propagate(e);
|
|
||||||
} finally {
|
|
||||||
closeQuietly(plainBytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
return digest.digest();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -67,8 +67,10 @@ import javax.ws.rs.core.HttpHeaders;
|
||||||
import javax.ws.rs.core.UriBuilder;
|
import javax.ws.rs.core.UriBuilder;
|
||||||
|
|
||||||
import org.jclouds.Constants;
|
import org.jclouds.Constants;
|
||||||
import org.jclouds.encryption.EncryptionService;
|
import org.jclouds.crypto.CryptoStreams;
|
||||||
|
import org.jclouds.io.InputSuppliers;
|
||||||
import org.jclouds.io.Payload;
|
import org.jclouds.io.Payload;
|
||||||
|
import org.jclouds.io.PayloadEnclosing;
|
||||||
import org.jclouds.io.Payloads;
|
import org.jclouds.io.Payloads;
|
||||||
import org.jclouds.logging.Logger;
|
import org.jclouds.logging.Logger;
|
||||||
import org.jclouds.logging.internal.Wire;
|
import org.jclouds.logging.internal.Wire;
|
||||||
|
@ -96,13 +98,12 @@ public class HttpUtils {
|
||||||
@Inject(optional = true)
|
@Inject(optional = true)
|
||||||
@Named(Constants.PROPERTY_PROXY_SYSTEM)
|
@Named(Constants.PROPERTY_PROXY_SYSTEM)
|
||||||
private boolean systemProxies = System.getProperty("java.net.useSystemProxies") != null ? Boolean
|
private boolean systemProxies = System.getProperty("java.net.useSystemProxies") != null ? Boolean
|
||||||
.parseBoolean(System.getProperty("java.net.useSystemProxies")) : false;
|
.parseBoolean(System.getProperty("java.net.useSystemProxies")) : false;
|
||||||
|
|
||||||
private final int globalMaxConnections;
|
private final int globalMaxConnections;
|
||||||
private final int globalMaxConnectionsPerHost;
|
private final int globalMaxConnectionsPerHost;
|
||||||
private final int connectionTimeout;
|
private final int connectionTimeout;
|
||||||
private final int soTimeout;
|
private final int soTimeout;
|
||||||
private final EncryptionService encryptionService;
|
|
||||||
@Inject(optional = true)
|
@Inject(optional = true)
|
||||||
@Named(Constants.PROPERTY_PROXY_HOST)
|
@Named(Constants.PROPERTY_PROXY_HOST)
|
||||||
private String proxyHost;
|
private String proxyHost;
|
||||||
|
@ -117,12 +118,10 @@ public class HttpUtils {
|
||||||
private String proxyPassword;
|
private String proxyPassword;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public HttpUtils(EncryptionService encryptionService,
|
public HttpUtils(@Named(Constants.PROPERTY_CONNECTION_TIMEOUT) int connectionTimeout,
|
||||||
@Named(Constants.PROPERTY_CONNECTION_TIMEOUT) int connectionTimeout,
|
@Named(Constants.PROPERTY_SO_TIMEOUT) int soTimeout,
|
||||||
@Named(Constants.PROPERTY_SO_TIMEOUT) int soTimeout,
|
@Named(Constants.PROPERTY_MAX_CONNECTIONS_PER_CONTEXT) int globalMaxConnections,
|
||||||
@Named(Constants.PROPERTY_MAX_CONNECTIONS_PER_CONTEXT) int globalMaxConnections,
|
@Named(Constants.PROPERTY_MAX_CONNECTIONS_PER_HOST) int globalMaxConnectionsPerHost) {
|
||||||
@Named(Constants.PROPERTY_MAX_CONNECTIONS_PER_HOST) int globalMaxConnectionsPerHost) {
|
|
||||||
this.encryptionService = encryptionService;
|
|
||||||
this.soTimeout = soTimeout;
|
this.soTimeout = soTimeout;
|
||||||
this.connectionTimeout = connectionTimeout;
|
this.connectionTimeout = connectionTimeout;
|
||||||
this.globalMaxConnections = globalMaxConnections;
|
this.globalMaxConnections = globalMaxConnections;
|
||||||
|
@ -182,8 +181,8 @@ public class HttpUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* keys to the map are only used for socket information, not path. In this
|
* keys to the map are only used for socket information, not path. In this case, you should
|
||||||
* case, you should remove any path or query details from the URI.
|
* remove any path or query details from the URI.
|
||||||
*/
|
*/
|
||||||
public static URI createBaseEndpointFor(URI endpoint) {
|
public static URI createBaseEndpointFor(URI endpoint) {
|
||||||
if (endpoint.getPort() == -1) {
|
if (endpoint.getPort() == -1) {
|
||||||
|
@ -194,8 +193,7 @@ public class HttpUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Web browsers do not always handle '+' characters well, use the
|
* Web browsers do not always handle '+' characters well, use the well-supported '%20' instead.
|
||||||
* well-supported '%20' instead.
|
|
||||||
*/
|
*/
|
||||||
public static String urlEncode(String in, char... skipEncode) {
|
public static String urlEncode(String in, char... skipEncode) {
|
||||||
if (isUrlEncoded(in))
|
if (isUrlEncoded(in))
|
||||||
|
@ -241,8 +239,7 @@ public class HttpUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Content stream may need to be read. However, we should always close the
|
* Content stream may need to be read. However, we should always close the http stream.
|
||||||
* http stream.
|
|
||||||
*
|
*
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
|
@ -262,11 +259,11 @@ public class HttpUtils {
|
||||||
String scheme = redirectURI.getScheme();
|
String scheme = redirectURI.getScheme();
|
||||||
|
|
||||||
checkState(redirectURI.getScheme().startsWith("http"), String.format(
|
checkState(redirectURI.getScheme().startsWith("http"), String.format(
|
||||||
"header %s didn't parse an http scheme: [%s]", hostHeader, scheme));
|
"header %s didn't parse an http scheme: [%s]", hostHeader, scheme));
|
||||||
int port = redirectURI.getPort() > 0 ? redirectURI.getPort() : redirectURI.getScheme().equals("https") ? 443 : 80;
|
int port = redirectURI.getPort() > 0 ? redirectURI.getPort() : redirectURI.getScheme().equals("https") ? 443 : 80;
|
||||||
String host = redirectURI.getHost();
|
String host = redirectURI.getHost();
|
||||||
checkState(host.indexOf('/') == -1, String.format("header %s didn't parse an http host correctly: [%s]",
|
checkState(host.indexOf('/') == -1, String.format("header %s didn't parse an http host correctly: [%s]",
|
||||||
hostHeader, host));
|
hostHeader, host));
|
||||||
URI endPoint = URI.create(String.format("%s://%s:%d", scheme, host, port));
|
URI endPoint = URI.create(String.format("%s://%s:%d", scheme, host, port));
|
||||||
return endPoint;
|
return endPoint;
|
||||||
}
|
}
|
||||||
|
@ -276,11 +273,10 @@ public class HttpUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to extract the URI and authentication data from a String. Note that
|
* Used to extract the URI and authentication data from a String. Note that the java URI class
|
||||||
* the java URI class breaks, if there are special characters like '/'
|
* breaks, if there are special characters like '/' present. Otherwise, we wouldn't need this
|
||||||
* present. Otherwise, we wouldn't need this class, and we could simply use
|
* class, and we could simply use URI.create("uri").getUserData(); Also, URI breaks if there are
|
||||||
* URI.create("uri").getUserData(); Also, URI breaks if there are curly
|
* curly braces.
|
||||||
* braces.
|
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public static URI createUri(String uriPath) {
|
public static URI createUri(String uriPath) {
|
||||||
|
@ -339,8 +335,12 @@ public class HttpUtils {
|
||||||
if (message.getPayload().getContentLength() != null)
|
if (message.getPayload().getContentLength() != null)
|
||||||
logger.debug("%s %s: %s", prefix, HttpHeaders.CONTENT_LENGTH, message.getPayload().getContentLength());
|
logger.debug("%s %s: %s", prefix, HttpHeaders.CONTENT_LENGTH, message.getPayload().getContentLength());
|
||||||
if (message.getPayload().getContentMD5() != null)
|
if (message.getPayload().getContentMD5() != null)
|
||||||
logger.debug("%s %s: %s", prefix, "Content-MD5", encryptionService.base64(message.getPayload()
|
try {
|
||||||
.getContentMD5()));
|
logger.debug("%s %s: %s", prefix, "Content-MD5", CryptoStreams.base64Encode(InputSuppliers.of(message
|
||||||
|
.getPayload().getContentMD5())));
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.warn(e, " error getting md5 for %s", message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -363,13 +363,12 @@ public class HttpUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* change the destination of the current http command. typically used in
|
* change the destination of the current http command. typically used in handling redirects.
|
||||||
* handling redirects.
|
|
||||||
*
|
*
|
||||||
* @param string
|
* @param string
|
||||||
*/
|
*/
|
||||||
public static void changeSchemeHostAndPortTo(HttpRequest request, String scheme, String host, int port,
|
public static void changeSchemeHostAndPortTo(HttpRequest request, String scheme, String host, int port,
|
||||||
UriBuilder builder) {
|
UriBuilder builder) {
|
||||||
builder.uri(request.getEndpoint());
|
builder.uri(request.getEndpoint());
|
||||||
builder.scheme(scheme);
|
builder.scheme(scheme);
|
||||||
builder.host(host);
|
builder.host(host);
|
||||||
|
@ -403,7 +402,7 @@ public class HttpUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void addQueryParamTo(HttpRequest request, String key, Iterable<?> values, UriBuilder builder,
|
public static void addQueryParamTo(HttpRequest request, String key, Iterable<?> values, UriBuilder builder,
|
||||||
char... skips) {
|
char... skips) {
|
||||||
builder.uri(request.getEndpoint());
|
builder.uri(request.getEndpoint());
|
||||||
Multimap<String, String> map = parseQueryToMap(request.getEndpoint().getQuery());
|
Multimap<String, String> map = parseQueryToMap(request.getEndpoint().getQuery());
|
||||||
for (Object o : values)
|
for (Object o : values)
|
||||||
|
@ -460,16 +459,16 @@ public class HttpUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SortedSet<Entry<String, String>> sortEntries(Collection<Map.Entry<String, String>> in,
|
public static SortedSet<Entry<String, String>> sortEntries(Collection<Map.Entry<String, String>> in,
|
||||||
Comparator<Map.Entry<String, String>> sorter) {
|
Comparator<Map.Entry<String, String>> sorter) {
|
||||||
SortedSet<Entry<String, String>> entries = newTreeSet(sorter);
|
SortedSet<Entry<String, String>> entries = newTreeSet(sorter);
|
||||||
entries.addAll(in);
|
entries.addAll(in);
|
||||||
return entries;
|
return entries;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String makeQueryLine(Multimap<String, String> params,
|
public static String makeQueryLine(Multimap<String, String> params,
|
||||||
@Nullable Comparator<Map.Entry<String, String>> sorter, char... skips) {
|
@Nullable Comparator<Map.Entry<String, String>> sorter, char... skips) {
|
||||||
Iterator<Map.Entry<String, String>> pairs = ((sorter == null) ? params.entries() : sortEntries(params.entries(),
|
Iterator<Map.Entry<String, String>> pairs = ((sorter == null) ? params.entries() : sortEntries(params.entries(),
|
||||||
sorter)).iterator();
|
sorter)).iterator();
|
||||||
StringBuilder formBuilder = new StringBuilder();
|
StringBuilder formBuilder = new StringBuilder();
|
||||||
while (pairs.hasNext()) {
|
while (pairs.hasNext()) {
|
||||||
Map.Entry<String, String> pair = pairs.next();
|
Map.Entry<String, String> pair = pairs.next();
|
||||||
|
@ -499,7 +498,7 @@ public class HttpUtils {
|
||||||
payload.setContentLength(new Long(header.getValue()));
|
payload.setContentLength(new Long(header.getValue()));
|
||||||
} else if ("Content-MD5".equalsIgnoreCase(header.getKey())) {
|
} else if ("Content-MD5".equalsIgnoreCase(header.getKey())) {
|
||||||
if (payload != null)
|
if (payload != null)
|
||||||
payload.setContentMD5(encryptionService.fromBase64(header.getValue()));
|
payload.setContentMD5(CryptoStreams.base64(header.getValue()));
|
||||||
} else if (CONTENT_TYPE.equalsIgnoreCase(header.getKey())) {
|
} else if (CONTENT_TYPE.equalsIgnoreCase(header.getKey())) {
|
||||||
if (payload != null)
|
if (payload != null)
|
||||||
payload.setContentType(header.getValue());
|
payload.setContentType(header.getValue());
|
||||||
|
@ -510,20 +509,20 @@ public class HttpUtils {
|
||||||
|
|
||||||
if (message instanceof HttpRequest) {
|
if (message instanceof HttpRequest) {
|
||||||
checkArgument(
|
checkArgument(
|
||||||
message.getPayload() == null || message.getFirstHeaderOrNull(CONTENT_TYPE) == null,
|
message.getPayload() == null || message.getFirstHeaderOrNull(CONTENT_TYPE) == null,
|
||||||
"configuration error please use request.getPayload().setContentType(value) as opposed to adding a content type header: "
|
"configuration error please use request.getPayload().setContentType(value) as opposed to adding a content type header: "
|
||||||
+ message);
|
+ message);
|
||||||
checkArgument(
|
checkArgument(
|
||||||
message.getPayload() == null || message.getFirstHeaderOrNull(CONTENT_LENGTH) == null,
|
message.getPayload() == null || message.getFirstHeaderOrNull(CONTENT_LENGTH) == null,
|
||||||
"configuration error please use request.getPayload().setContentLength(value) as opposed to adding a content length header: "
|
"configuration error please use request.getPayload().setContentLength(value) as opposed to adding a content length header: "
|
||||||
+ message);
|
+ message);
|
||||||
checkArgument(message.getPayload() == null || message.getPayload().getContentLength() != null
|
checkArgument(message.getPayload() == null || message.getPayload().getContentLength() != null
|
||||||
|| "chunked".equalsIgnoreCase(message.getFirstHeaderOrNull("Transfer-Encoding")),
|
|| "chunked".equalsIgnoreCase(message.getFirstHeaderOrNull("Transfer-Encoding")),
|
||||||
"either chunked encoding must be set on the http request or contentlength set on the payload: "
|
"either chunked encoding must be set on the http request or contentlength set on the payload: "
|
||||||
+ message);
|
+ message);
|
||||||
checkArgument(message.getPayload() == null || message.getFirstHeaderOrNull("Content-MD5") == null,
|
checkArgument(message.getPayload() == null || message.getFirstHeaderOrNull("Content-MD5") == null,
|
||||||
"configuration error please use request.getPayload().setContentMD5(value) as opposed to adding a content md5 header: "
|
"configuration error please use request.getPayload().setContentMD5(value) as opposed to adding a content md5 header: "
|
||||||
+ message);
|
+ message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -537,7 +536,7 @@ public class HttpUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
public String valueOrEmpty(byte[] md5) {
|
public String valueOrEmpty(byte[] md5) {
|
||||||
return md5 != null ? encryptionService.base64(md5) : "";
|
return md5 != null ? CryptoStreams.base64(md5) : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
public String valueOrEmpty(Collection<String> collection) {
|
public String valueOrEmpty(Collection<String> collection) {
|
||||||
|
@ -563,14 +562,14 @@ public class HttpUtils {
|
||||||
if (request.getPayload() != null && wire.enabled()) {
|
if (request.getPayload() != null && wire.enabled()) {
|
||||||
wire.output(request);
|
wire.output(request);
|
||||||
checkRequestHasContentLengthOrChunkedEncoding(request,
|
checkRequestHasContentLengthOrChunkedEncoding(request,
|
||||||
"After wiring, the request has neither chunked encoding nor content length: " + request);
|
"After wiring, the request has neither chunked encoding nor content length: " + request);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> T returnValueOnCodeOrNull(Exception from, T value, Predicate<Integer> codePredicate) {
|
public static <T> T returnValueOnCodeOrNull(Exception from, T value, Predicate<Integer> codePredicate) {
|
||||||
Iterable<HttpResponseException> throwables = filter(getCausalChain(from), HttpResponseException.class);
|
Iterable<HttpResponseException> throwables = filter(getCausalChain(from), HttpResponseException.class);
|
||||||
if (size(throwables) >= 1 && get(throwables, 0).getResponse() != null
|
if (size(throwables) >= 1 && get(throwables, 0).getResponse() != null
|
||||||
&& codePredicate.apply(get(throwables, 0).getResponse().getStatusCode())) {
|
&& codePredicate.apply(get(throwables, 0).getResponse().getStatusCode())) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -24,7 +24,7 @@ import javax.inject.Inject;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
|
|
||||||
import org.jclouds.Constants;
|
import org.jclouds.Constants;
|
||||||
import static org.jclouds.concurrent.ConcurrentUtils.*;
|
import org.jclouds.concurrent.Futures;
|
||||||
|
|
||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
|
@ -50,7 +50,7 @@ public class TransformingHttpCommandExecutorServiceImpl implements TransformingH
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public <T> ListenableFuture<T> submit(HttpCommand command, Function<HttpResponse, T> responseTransformer) {
|
public <T> ListenableFuture<T> submit(HttpCommand command, Function<HttpResponse, T> responseTransformer) {
|
||||||
return compose(client.submit(command), responseTransformer, userThreads);
|
return Futures.compose(client.submit(command), responseTransformer, userThreads);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,8 @@ import javax.inject.Named;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
import javax.ws.rs.core.HttpHeaders;
|
import javax.ws.rs.core.HttpHeaders;
|
||||||
|
|
||||||
import org.jclouds.encryption.EncryptionService;
|
import org.jclouds.crypto.Crypto;
|
||||||
|
import org.jclouds.crypto.CryptoStreams;
|
||||||
import org.jclouds.http.HttpException;
|
import org.jclouds.http.HttpException;
|
||||||
import org.jclouds.http.HttpRequest;
|
import org.jclouds.http.HttpRequest;
|
||||||
import org.jclouds.http.HttpRequestFilter;
|
import org.jclouds.http.HttpRequestFilter;
|
||||||
|
@ -50,11 +51,10 @@ public class BasicAuthentication implements HttpRequestFilter {
|
||||||
private final Set<String> credentialList;
|
private final Set<String> credentialList;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
BasicAuthentication(@Named(PROPERTY_IDENTITY) String user,
|
BasicAuthentication(@Named(PROPERTY_IDENTITY) String user, @Named(PROPERTY_CREDENTIAL) String password, Crypto crypto)
|
||||||
@Named(PROPERTY_CREDENTIAL) String password, EncryptionService encryptionService)
|
|
||||||
throws UnsupportedEncodingException {
|
throws UnsupportedEncodingException {
|
||||||
this.credentialList = ImmutableSet.of("Basic "
|
this.credentialList = ImmutableSet.of("Basic "
|
||||||
+ encryptionService.base64(String.format("%s:%s", checkNotNull(user, "user"),
|
+ CryptoStreams.base64(String.format("%s:%s", checkNotNull(user, "user"),
|
||||||
checkNotNull(password, "password")).getBytes("UTF-8")));
|
checkNotNull(password, "password")).getBytes("UTF-8")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
*/
|
*/
|
||||||
package org.jclouds.http.internal;
|
package org.jclouds.http.internal;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
import static com.google.common.io.ByteStreams.copy;
|
import static com.google.common.io.ByteStreams.copy;
|
||||||
import static org.jclouds.http.HttpUtils.checkRequestHasContentLengthOrChunkedEncoding;
|
import static org.jclouds.http.HttpUtils.checkRequestHasContentLengthOrChunkedEncoding;
|
||||||
import static org.jclouds.http.HttpUtils.wirePayloadIfEnabled;
|
import static org.jclouds.http.HttpUtils.wirePayloadIfEnabled;
|
||||||
|
@ -35,7 +36,6 @@ import javax.inject.Inject;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
|
|
||||||
import org.jclouds.Constants;
|
import org.jclouds.Constants;
|
||||||
import org.jclouds.encryption.EncryptionService;
|
|
||||||
import org.jclouds.http.HttpCommand;
|
import org.jclouds.http.HttpCommand;
|
||||||
import org.jclouds.http.HttpCommandExecutorService;
|
import org.jclouds.http.HttpCommandExecutorService;
|
||||||
import org.jclouds.http.HttpRequest;
|
import org.jclouds.http.HttpRequest;
|
||||||
|
@ -56,7 +56,6 @@ import com.google.common.io.NullOutputStream;
|
||||||
*/
|
*/
|
||||||
public abstract class BaseHttpCommandExecutorService<Q> implements HttpCommandExecutorService {
|
public abstract class BaseHttpCommandExecutorService<Q> implements HttpCommandExecutorService {
|
||||||
protected final HttpUtils utils;
|
protected final HttpUtils utils;
|
||||||
protected final EncryptionService encryptionService;
|
|
||||||
|
|
||||||
private final DelegatingRetryHandler retryHandler;
|
private final DelegatingRetryHandler retryHandler;
|
||||||
private final IOExceptionRetryHandler ioRetryHandler;
|
private final IOExceptionRetryHandler ioRetryHandler;
|
||||||
|
@ -72,17 +71,16 @@ public abstract class BaseHttpCommandExecutorService<Q> implements HttpCommandEx
|
||||||
protected final HttpWire wire;
|
protected final HttpWire wire;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
protected BaseHttpCommandExecutorService(HttpUtils utils, EncryptionService encryptionService,
|
protected BaseHttpCommandExecutorService(HttpUtils utils,
|
||||||
@Named(Constants.PROPERTY_IO_WORKER_THREADS) ExecutorService ioWorkerExecutor,
|
@Named(Constants.PROPERTY_IO_WORKER_THREADS) ExecutorService ioWorkerExecutor,
|
||||||
DelegatingRetryHandler retryHandler, IOExceptionRetryHandler ioRetryHandler,
|
DelegatingRetryHandler retryHandler, IOExceptionRetryHandler ioRetryHandler,
|
||||||
DelegatingErrorHandler errorHandler, HttpWire wire) {
|
DelegatingErrorHandler errorHandler, HttpWire wire) {
|
||||||
this.utils = utils;
|
this.utils = checkNotNull(utils, "utils");
|
||||||
this.encryptionService = encryptionService;
|
this.retryHandler = checkNotNull(retryHandler, "retryHandler");
|
||||||
this.retryHandler = retryHandler;
|
this.ioRetryHandler = checkNotNull(ioRetryHandler, "ioRetryHandler");
|
||||||
this.ioRetryHandler = ioRetryHandler;
|
this.errorHandler = checkNotNull(errorHandler, "errorHandler");
|
||||||
this.errorHandler = errorHandler;
|
this.ioWorkerExecutor = checkNotNull(ioWorkerExecutor, "ioWorkerExecutor");
|
||||||
this.ioWorkerExecutor = ioWorkerExecutor;
|
this.wire = checkNotNull(wire, "wire");
|
||||||
this.wire = wire;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static InputStream consumeOnClose(InputStream in) {
|
public static InputStream consumeOnClose(InputStream in) {
|
||||||
|
|
|
@ -47,7 +47,7 @@ import javax.net.ssl.HttpsURLConnection;
|
||||||
import javax.ws.rs.core.HttpHeaders;
|
import javax.ws.rs.core.HttpHeaders;
|
||||||
|
|
||||||
import org.jclouds.Constants;
|
import org.jclouds.Constants;
|
||||||
import org.jclouds.encryption.EncryptionService;
|
import org.jclouds.crypto.CryptoStreams;
|
||||||
import org.jclouds.http.HttpCommandExecutorService;
|
import org.jclouds.http.HttpCommandExecutorService;
|
||||||
import org.jclouds.http.HttpRequest;
|
import org.jclouds.http.HttpRequest;
|
||||||
import org.jclouds.http.HttpResponse;
|
import org.jclouds.http.HttpResponse;
|
||||||
|
@ -68,8 +68,7 @@ import com.google.common.collect.Multimap;
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
*/
|
*/
|
||||||
@Singleton
|
@Singleton
|
||||||
public class JavaUrlHttpCommandExecutorService extends
|
public class JavaUrlHttpCommandExecutorService extends BaseHttpCommandExecutorService<HttpURLConnection> {
|
||||||
BaseHttpCommandExecutorService<HttpURLConnection> {
|
|
||||||
|
|
||||||
public static final String USER_AGENT = "jclouds/1.0 java/" + System.getProperty("java.version");
|
public static final String USER_AGENT = "jclouds/1.0 java/" + System.getProperty("java.version");
|
||||||
@Resource
|
@Resource
|
||||||
|
@ -80,18 +79,15 @@ public class JavaUrlHttpCommandExecutorService extends
|
||||||
public JavaUrlHttpCommandExecutorService(HttpUtils utils,
|
public JavaUrlHttpCommandExecutorService(HttpUtils utils,
|
||||||
@Named(Constants.PROPERTY_IO_WORKER_THREADS) ExecutorService ioWorkerExecutor,
|
@Named(Constants.PROPERTY_IO_WORKER_THREADS) ExecutorService ioWorkerExecutor,
|
||||||
DelegatingRetryHandler retryHandler, IOExceptionRetryHandler ioRetryHandler,
|
DelegatingRetryHandler retryHandler, IOExceptionRetryHandler ioRetryHandler,
|
||||||
DelegatingErrorHandler errorHandler, HttpWire wire, HostnameVerifier verifier,
|
DelegatingErrorHandler errorHandler, HttpWire wire, HostnameVerifier verifier) {
|
||||||
EncryptionService encryptionService) {
|
super(utils, ioWorkerExecutor, retryHandler, ioRetryHandler, errorHandler, wire);
|
||||||
super(utils, encryptionService, ioWorkerExecutor, retryHandler, ioRetryHandler, errorHandler,
|
|
||||||
wire);
|
|
||||||
if (utils.getMaxConnections() > 0)
|
if (utils.getMaxConnections() > 0)
|
||||||
System.setProperty("http.maxConnections", String.valueOf(utils.getMaxConnections()));
|
System.setProperty("http.maxConnections", String.valueOf(checkNotNull(utils, "utils").getMaxConnections()));
|
||||||
this.verifier = verifier;
|
this.verifier = checkNotNull(verifier, "verifier");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected HttpResponse invoke(HttpURLConnection connection) throws IOException,
|
protected HttpResponse invoke(HttpURLConnection connection) throws IOException, InterruptedException {
|
||||||
InterruptedException {
|
|
||||||
InputStream in = null;
|
InputStream in = null;
|
||||||
try {
|
try {
|
||||||
in = consumeOnClose(connection.getInputStream());
|
in = consumeOnClose(connection.getInputStream());
|
||||||
|
@ -109,8 +105,7 @@ public class JavaUrlHttpCommandExecutorService extends
|
||||||
}
|
}
|
||||||
|
|
||||||
Payload payload = in != null ? Payloads.newInputStreamPayload(in) : null;
|
Payload payload = in != null ? Payloads.newInputStreamPayload(in) : null;
|
||||||
HttpResponse response = new HttpResponse(connection.getResponseCode(), connection
|
HttpResponse response = new HttpResponse(connection.getResponseCode(), connection.getResponseMessage(), payload);
|
||||||
.getResponseMessage(), payload);
|
|
||||||
Multimap<String, String> headers = LinkedHashMultimap.create();
|
Multimap<String, String> headers = LinkedHashMultimap.create();
|
||||||
for (String header : connection.getHeaderFields().keySet()) {
|
for (String header : connection.getHeaderFields().keySet()) {
|
||||||
headers.putAll(header, connection.getHeaderFields().get(header));
|
headers.putAll(header, connection.getHeaderFields().get(header));
|
||||||
|
@ -133,8 +128,7 @@ public class JavaUrlHttpCommandExecutorService extends
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected HttpURLConnection convert(HttpRequest request) throws IOException,
|
protected HttpURLConnection convert(HttpRequest request) throws IOException, InterruptedException {
|
||||||
InterruptedException {
|
|
||||||
boolean chunked = "chunked".equals(request.getFirstHeaderOrNull("Transfer-Encoding"));
|
boolean chunked = "chunked".equals(request.getFirstHeaderOrNull("Transfer-Encoding"));
|
||||||
URL url = request.getEndpoint().toURL();
|
URL url = request.getEndpoint().toURL();
|
||||||
|
|
||||||
|
@ -150,8 +144,7 @@ public class JavaUrlHttpCommandExecutorService extends
|
||||||
Proxy proxy = new Proxy(Proxy.Type.HTTP, addr);
|
Proxy proxy = new Proxy(Proxy.Type.HTTP, addr);
|
||||||
Authenticator authenticator = new Authenticator() {
|
Authenticator authenticator = new Authenticator() {
|
||||||
public PasswordAuthentication getPasswordAuthentication() {
|
public PasswordAuthentication getPasswordAuthentication() {
|
||||||
return (new PasswordAuthentication(utils.getProxyUser(), utils.getProxyPassword()
|
return (new PasswordAuthentication(utils.getProxyUser(), utils.getProxyPassword().toCharArray()));
|
||||||
.toCharArray()));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Authenticator.setDefault(authenticator);
|
Authenticator.setDefault(authenticator);
|
||||||
|
@ -182,16 +175,13 @@ public class JavaUrlHttpCommandExecutorService extends
|
||||||
OutputStream out = null;
|
OutputStream out = null;
|
||||||
try {
|
try {
|
||||||
if (request.getPayload().getContentMD5() != null)
|
if (request.getPayload().getContentMD5() != null)
|
||||||
connection.setRequestProperty("Content-MD5", encryptionService.base64(request
|
connection.setRequestProperty("Content-MD5", CryptoStreams.base64(request.getPayload().getContentMD5()));
|
||||||
.getPayload().getContentMD5()));
|
|
||||||
if (request.getPayload().getContentType() != null)
|
if (request.getPayload().getContentType() != null)
|
||||||
connection.setRequestProperty(HttpHeaders.CONTENT_TYPE, request.getPayload()
|
connection.setRequestProperty(HttpHeaders.CONTENT_TYPE, request.getPayload().getContentType());
|
||||||
.getContentType());
|
|
||||||
if (chunked) {
|
if (chunked) {
|
||||||
connection.setChunkedStreamingMode(8196);
|
connection.setChunkedStreamingMode(8196);
|
||||||
} else {
|
} else {
|
||||||
Long length = checkNotNull(request.getPayload().getContentLength(),
|
Long length = checkNotNull(request.getPayload().getContentLength(), "payload.getContentLength");
|
||||||
"payload.getContentLength");
|
|
||||||
connection.setRequestProperty(HttpHeaders.CONTENT_LENGTH, length.toString());
|
connection.setRequestProperty(HttpHeaders.CONTENT_LENGTH, length.toString());
|
||||||
connection.setFixedLengthStreamingMode(length.intValue());
|
connection.setFixedLengthStreamingMode(length.intValue());
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,8 +26,8 @@ import java.io.InputStream;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import org.jclouds.http.PayloadEnclosing;
|
|
||||||
import org.jclouds.io.Payload;
|
import org.jclouds.io.Payload;
|
||||||
|
import org.jclouds.io.PayloadEnclosing;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
package org.jclouds.io;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import org.jclouds.encryption.internal.Base64;
|
||||||
|
|
||||||
|
import com.google.common.annotations.Beta;
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
import com.google.common.base.Charsets;
|
||||||
|
import com.google.common.io.ByteStreams;
|
||||||
|
import com.google.common.io.InputSupplier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* functions related to or replacing those in {@link com.google.common.io.InputSupplier}
|
||||||
|
*
|
||||||
|
* @author Adrian Cole
|
||||||
|
*/
|
||||||
|
@Beta
|
||||||
|
public class InputSuppliers {
|
||||||
|
/**
|
||||||
|
* base64 encodes bytes from the supplied supplier as they are read.
|
||||||
|
*/
|
||||||
|
public static Base64InputSupplier base64Encoder(InputSupplier<? extends InputStream> supplier) throws IOException {
|
||||||
|
return new Base64InputSupplier(supplier, Base64.ENCODE + Base64.DONT_BREAK_LINES);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* base64 decodes bytes from the supplied supplier as they are read.
|
||||||
|
*/
|
||||||
|
public static Base64InputSupplier base64Decoder(InputSupplier<? extends InputStream> supplier) throws IOException {
|
||||||
|
return new Base64InputSupplier(supplier, Base64.DECODE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
static class Base64InputSupplier implements InputSupplier<InputStream> {
|
||||||
|
|
||||||
|
private final InputSupplier<? extends InputStream> delegate;
|
||||||
|
private final int mode;
|
||||||
|
|
||||||
|
Base64InputSupplier(InputSupplier<? extends InputStream> inputSupplier, int mode) {
|
||||||
|
this.delegate = checkNotNull(inputSupplier, "delegate");
|
||||||
|
this.mode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream getInput() throws IOException {
|
||||||
|
return new Base64.InputStream(delegate.getInput(), mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static InputSupplier<? extends InputStream> of(final InputStream in) {
|
||||||
|
checkNotNull(in, "in");
|
||||||
|
return new InputSupplier<InputStream>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream getInput() throws IOException {
|
||||||
|
return in;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static InputSupplier<? extends InputStream> of(byte[] in) {
|
||||||
|
return ByteStreams.newInputStreamSupplier(checkNotNull(in, "in"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static InputSupplier<? extends InputStream> of(String in) {
|
||||||
|
return of(checkNotNull(in, "in").getBytes(Charsets.UTF_8));
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,12 +16,11 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
* ====================================================================
|
* ====================================================================
|
||||||
*/
|
*/
|
||||||
package org.jclouds.http;
|
package org.jclouds.io;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
import org.jclouds.io.Payload;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
|
@ -19,20 +19,27 @@
|
||||||
package org.jclouds.io;
|
package org.jclouds.io;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
import static com.google.common.base.Preconditions.checkState;
|
||||||
|
import static com.google.common.io.ByteStreams.toByteArray;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import org.jclouds.crypto.CryptoStreams;
|
||||||
import org.jclouds.io.payloads.ByteArrayPayload;
|
import org.jclouds.io.payloads.ByteArrayPayload;
|
||||||
import org.jclouds.io.payloads.FilePayload;
|
import org.jclouds.io.payloads.FilePayload;
|
||||||
import org.jclouds.io.payloads.InputStreamPayload;
|
import org.jclouds.io.payloads.InputStreamPayload;
|
||||||
import org.jclouds.io.payloads.StringPayload;
|
import org.jclouds.io.payloads.StringPayload;
|
||||||
import org.jclouds.io.payloads.UrlEncodedFormPayload;
|
import org.jclouds.io.payloads.UrlEncodedFormPayload;
|
||||||
|
|
||||||
|
import com.google.common.base.Throwables;
|
||||||
import com.google.common.collect.Multimap;
|
import com.google.common.collect.Multimap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -40,6 +47,8 @@ import com.google.common.collect.Multimap;
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
*/
|
*/
|
||||||
public class Payloads {
|
public class Payloads {
|
||||||
|
private Payloads() {
|
||||||
|
}
|
||||||
|
|
||||||
public static Payload newPayload(Object data) {
|
public static Payload newPayload(Object data) {
|
||||||
checkNotNull(data, "data");
|
checkNotNull(data, "data");
|
||||||
|
@ -74,14 +83,84 @@ public class Payloads {
|
||||||
return new FilePayload(checkNotNull(data, "data"));
|
return new FilePayload(checkNotNull(data, "data"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static UrlEncodedFormPayload newUrlEncodedFormPayload(
|
public static UrlEncodedFormPayload newUrlEncodedFormPayload(Multimap<String, String> formParams, char... skips) {
|
||||||
Multimap<String, String> formParams, char... skips) {
|
|
||||||
return new UrlEncodedFormPayload(formParams, skips);
|
return new UrlEncodedFormPayload(formParams, skips);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static UrlEncodedFormPayload newUrlEncodedFormPayload(
|
public static UrlEncodedFormPayload newUrlEncodedFormPayload(Multimap<String, String> formParams,
|
||||||
Multimap<String, String> formParams,
|
|
||||||
@Nullable Comparator<Map.Entry<String, String>> sorter, char... skips) {
|
@Nullable Comparator<Map.Entry<String, String>> sorter, char... skips) {
|
||||||
return new UrlEncodedFormPayload(formParams, sorter, skips);
|
return new UrlEncodedFormPayload(formParams, sorter, skips);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates and sets {@link Payload#setContentMD5} on the payload.
|
||||||
|
*
|
||||||
|
* <p/>
|
||||||
|
* note that this will rebuffer in memory if the payload is not repeatable.
|
||||||
|
*
|
||||||
|
* @param payload
|
||||||
|
* payload to calculate
|
||||||
|
* @param md5
|
||||||
|
* digester to calculate payloads with.
|
||||||
|
* @return new Payload with md5 set.
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public static Payload calculateMD5(Payload payload, MessageDigest md5) throws IOException {
|
||||||
|
checkNotNull(payload, "payload");
|
||||||
|
if (!payload.isRepeatable()) {
|
||||||
|
String oldContentType = payload.getContentType();
|
||||||
|
Payload oldPayload = payload;
|
||||||
|
try {
|
||||||
|
payload = newByteArrayPayload(toByteArray(payload));
|
||||||
|
} finally {
|
||||||
|
oldPayload.release();
|
||||||
|
}
|
||||||
|
payload.setContentType(oldContentType);
|
||||||
|
}
|
||||||
|
payload.setContentMD5(CryptoStreams.digest(payload, md5));
|
||||||
|
return payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses default md5 generator.
|
||||||
|
*
|
||||||
|
* @see #calculateMD5(Payload, MessageDigest)
|
||||||
|
*/
|
||||||
|
public static Payload calculateMD5(Payload payload) throws IOException {
|
||||||
|
try {
|
||||||
|
return calculateMD5(payload, MessageDigest.getInstance("MD5"));
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
Throwables.propagate(e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the md5 on a payload, replacing as necessary.
|
||||||
|
*
|
||||||
|
* @see #calculateMD5(Payload, MessageDigest)
|
||||||
|
*/
|
||||||
|
public static <T extends PayloadEnclosing> T calculateMD5(T payloadEnclosing, MessageDigest md5) throws IOException {
|
||||||
|
checkState(payloadEnclosing != null, "payloadEnclosing");
|
||||||
|
Payload newPayload = calculateMD5(payloadEnclosing.getPayload(), md5);
|
||||||
|
if (newPayload != payloadEnclosing.getPayload())
|
||||||
|
payloadEnclosing.setPayload(newPayload);
|
||||||
|
return payloadEnclosing;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the md5 on a payload, replacing as necessary.
|
||||||
|
* <p/>
|
||||||
|
* uses default md5 generator.
|
||||||
|
*
|
||||||
|
* @see #calculateMD5(Payload, MessageDigest)
|
||||||
|
*/
|
||||||
|
public static <T extends PayloadEnclosing> T calculateMD5(T payloadEnclosing) throws IOException {
|
||||||
|
try {
|
||||||
|
return calculateMD5(payloadEnclosing, MessageDigest.getInstance("MD5"));
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
Throwables.propagate(e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||||
|
*
|
||||||
|
* ====================================================================
|
||||||
|
* Licensed 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.io.payloads;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.security.Key;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.CipherInputStream;
|
||||||
|
import javax.crypto.CipherOutputStream;
|
||||||
|
|
||||||
|
import org.jclouds.io.Payload;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Adrian Cole
|
||||||
|
*/
|
||||||
|
public abstract class BaseCipherPayload extends DelegatingPayload {
|
||||||
|
|
||||||
|
private final Key key;
|
||||||
|
|
||||||
|
public BaseCipherPayload(Payload delegate, Key key) {
|
||||||
|
super(delegate);
|
||||||
|
this.key = checkNotNull(key, "key");
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract Cipher initializeCipher(Key key);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CipherInputStream getInput() {
|
||||||
|
return new CipherInputStream(super.getInput(), initializeCipher(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeTo(OutputStream outstream) throws IOException {
|
||||||
|
super.writeTo(new CipherOutputStream(outstream, initializeCipher(key)));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||||
|
*
|
||||||
|
* ====================================================================
|
||||||
|
* Licensed 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.io.payloads;
|
||||||
|
|
||||||
|
import java.security.InvalidKeyException;
|
||||||
|
import java.security.Key;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.NoSuchPaddingException;
|
||||||
|
|
||||||
|
import org.jclouds.io.Payload;
|
||||||
|
|
||||||
|
import com.google.common.base.Throwables;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Adrian Cole
|
||||||
|
*/
|
||||||
|
public class RSADecryptingPayload extends BaseCipherPayload {
|
||||||
|
|
||||||
|
public RSADecryptingPayload(Payload delegate, Key key) {
|
||||||
|
super(delegate, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Cipher initializeCipher(Key key) {
|
||||||
|
Cipher cipher = null;
|
||||||
|
try {
|
||||||
|
cipher = Cipher.getInstance("RSA");
|
||||||
|
cipher.init(Cipher.DECRYPT_MODE, key);
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
Throwables.propagate(e);
|
||||||
|
} catch (NoSuchPaddingException e) {
|
||||||
|
Throwables.propagate(e);
|
||||||
|
} catch (InvalidKeyException e) {
|
||||||
|
Throwables.propagate(e);
|
||||||
|
}
|
||||||
|
return cipher;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue