mirror of https://github.com/apache/jclouds.git
Deprecated remaining code related to RestContext, RestApiMetadata, and RestClientModule for ApiContext, HttpApiMetadata, HttpApiModule
This commit is contained in:
parent
b4819b6c17
commit
61067d3683
|
@ -18,7 +18,7 @@
|
||||||
*/
|
*/
|
||||||
package org.jclouds.cloudstack.config;
|
package org.jclouds.cloudstack.config;
|
||||||
|
|
||||||
import static org.jclouds.rest.config.BinderUtils.bindHttpApi;
|
import static org.jclouds.rest.config.BinderUtils.bindMappedHttpApi;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
@ -207,9 +207,9 @@ public class CloudStackRestClientModule extends RestClientModule<CloudStackClien
|
||||||
});
|
});
|
||||||
bind(CredentialType.class).toProvider(CredentialTypeFromPropertyOrDefault.class);
|
bind(CredentialType.class).toProvider(CredentialTypeFromPropertyOrDefault.class);
|
||||||
// session client is used directly for filters and retry handlers, so let's bind it explicitly
|
// session client is used directly for filters and retry handlers, so let's bind it explicitly
|
||||||
bindHttpApi(binder(), SessionClient.class, SessionAsyncClient.class);
|
bindMappedHttpApi(binder(), SessionClient.class, SessionAsyncClient.class);
|
||||||
bindHttpApi(binder(), CloudStackDomainClient.class, CloudStackDomainAsyncClient.class);
|
bindMappedHttpApi(binder(), CloudStackDomainClient.class, CloudStackDomainAsyncClient.class);
|
||||||
bindHttpApi(binder(), CloudStackGlobalClient.class, CloudStackGlobalAsyncClient.class);
|
bindMappedHttpApi(binder(), CloudStackGlobalClient.class, CloudStackGlobalAsyncClient.class);
|
||||||
bind(HttpRetryHandler.class).annotatedWith(ClientError.class).to(InvalidateSessionAndRetryOn401AndLogoutOnClose.class);
|
bind(HttpRetryHandler.class).annotatedWith(ClientError.class).to(InvalidateSessionAndRetryOn401AndLogoutOnClose.class);
|
||||||
|
|
||||||
super.configure();
|
super.configure();
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
*/
|
*/
|
||||||
package org.jclouds.filesystem.config;
|
package org.jclouds.filesystem.config;
|
||||||
|
|
||||||
import static org.jclouds.rest.config.BinderUtils.bindBlockingApi;
|
import static org.jclouds.rest.config.BinderUtils.bindMappedApi;
|
||||||
|
|
||||||
import org.jclouds.blobstore.AsyncBlobStore;
|
import org.jclouds.blobstore.AsyncBlobStore;
|
||||||
import org.jclouds.blobstore.BlobRequestSigner;
|
import org.jclouds.blobstore.BlobRequestSigner;
|
||||||
|
@ -50,7 +50,7 @@ public class FilesystemBlobStoreContextModule extends AbstractModule {
|
||||||
protected void configure() {
|
protected void configure() {
|
||||||
bind(AsyncBlobStore.class).to(LocalAsyncBlobStore.class).asEagerSingleton();
|
bind(AsyncBlobStore.class).to(LocalAsyncBlobStore.class).asEagerSingleton();
|
||||||
// forward all requests from TransientBlobStore to TransientAsyncBlobStore. needs above binding as cannot proxy a class
|
// forward all requests from TransientBlobStore to TransientAsyncBlobStore. needs above binding as cannot proxy a class
|
||||||
bindBlockingApi(binder(), LocalBlobStore.class, AsyncBlobStore.class);
|
bindMappedApi(binder(), LocalBlobStore.class, AsyncBlobStore.class);
|
||||||
bind(BlobStore.class).to(LocalBlobStore.class);
|
bind(BlobStore.class).to(LocalBlobStore.class);
|
||||||
|
|
||||||
install(new BlobStoreObjectModule());
|
install(new BlobStoreObjectModule());
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
package org.jclouds.openstack.keystone.v2_0.config;
|
package org.jclouds.openstack.keystone.v2_0.config;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkArgument;
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
import static org.jclouds.rest.config.BinderUtils.bindHttpApi;
|
import static org.jclouds.rest.config.BinderUtils.bindMappedHttpApi;
|
||||||
import static org.jclouds.util.Suppliers2.getLastValueInMap;
|
import static org.jclouds.util.Suppliers2.getLastValueInMap;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
@ -189,7 +189,7 @@ public class KeystoneAuthenticationModule extends AbstractModule {
|
||||||
|
|
||||||
protected void bindAuthenticationApi() {
|
protected void bindAuthenticationApi() {
|
||||||
// AuthenticationApi is used directly for filters and retry handlers, so let's bind it explicitly
|
// AuthenticationApi is used directly for filters and retry handlers, so let's bind it explicitly
|
||||||
bindHttpApi(binder(), AuthenticationApi.class, AuthenticationAsyncApi.class);
|
bindMappedHttpApi(binder(), AuthenticationApi.class, AuthenticationAsyncApi.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
*/
|
*/
|
||||||
package org.jclouds.rackspace.cloudidentity.v2_0.config;
|
package org.jclouds.rackspace.cloudidentity.v2_0.config;
|
||||||
|
|
||||||
import static org.jclouds.rest.config.BinderUtils.bindHttpApi;
|
import static org.jclouds.rest.config.BinderUtils.bindMappedHttpApi;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ public class CloudIdentityAuthenticationModule extends KeystoneAuthenticationMod
|
||||||
@Override
|
@Override
|
||||||
protected void bindAuthenticationApi() {
|
protected void bindAuthenticationApi() {
|
||||||
// AuthenticationApi is used directly for filters and retry handlers, so let's bind it explicitly
|
// AuthenticationApi is used directly for filters and retry handlers, so let's bind it explicitly
|
||||||
bindHttpApi(binder(), CloudIdentityAuthenticationApi.class,
|
bindMappedHttpApi(binder(), CloudIdentityAuthenticationApi.class,
|
||||||
CloudIdentityAuthenticationAsyncApi.class);
|
CloudIdentityAuthenticationAsyncApi.class);
|
||||||
bind(AuthenticationApi.class).to(CloudIdentityAuthenticationApi.class).in(Scopes.SINGLETON);
|
bind(AuthenticationApi.class).to(CloudIdentityAuthenticationApi.class).in(Scopes.SINGLETON);
|
||||||
bind(AuthenticationAsyncApi.class).to(CloudIdentityAuthenticationAsyncApi.class).in(Scopes.SINGLETON);
|
bind(AuthenticationAsyncApi.class).to(CloudIdentityAuthenticationAsyncApi.class).in(Scopes.SINGLETON);
|
||||||
|
|
|
@ -32,6 +32,7 @@ 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.rest.ConfiguresRestClient;
|
import org.jclouds.rest.ConfiguresRestClient;
|
||||||
|
import org.jclouds.rest.config.HttpApiModule;
|
||||||
import org.jclouds.rest.config.RestClientModule;
|
import org.jclouds.rest.config.RestClientModule;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
@ -42,7 +43,10 @@ import com.google.inject.Provides;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
|
*
|
||||||
|
* @deprecated will be removed in jclouds 1.7; use {@link AWSHttpApiModule}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
@ConfiguresRestClient
|
@ConfiguresRestClient
|
||||||
public abstract class AWSRestClientModule<S, A> extends RestClientModule<S, A> {
|
public abstract class AWSRestClientModule<S, A> extends RestClientModule<S, A> {
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
*/
|
*/
|
||||||
package org.jclouds.openstack.swift.blobstore.config;
|
package org.jclouds.openstack.swift.blobstore.config;
|
||||||
|
|
||||||
import static org.jclouds.rest.config.BinderUtils.bindHttpApi;
|
import static org.jclouds.rest.config.BinderUtils.bindMappedHttpApi;
|
||||||
import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
|
import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
@ -70,7 +70,7 @@ public abstract class TemporaryUrlExtensionModule<A extends CommonSwiftAsyncClie
|
||||||
TemporaryUrlExtensionModule<SwiftKeystoneAsyncClient> {
|
TemporaryUrlExtensionModule<SwiftKeystoneAsyncClient> {
|
||||||
|
|
||||||
protected void bindTemporaryUrlKeyApi() {
|
protected void bindTemporaryUrlKeyApi() {
|
||||||
bindHttpApi(binder(), TemporaryUrlKeyApi.class, KeystoneTemporaryUrlKeyAsyncApi.class);
|
bindMappedHttpApi(binder(), TemporaryUrlKeyApi.class, KeystoneTemporaryUrlKeyAsyncApi.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -115,7 +115,7 @@ public abstract class TemporaryUrlExtensionModule<A extends CommonSwiftAsyncClie
|
||||||
protected abstract void bindRequestSigner();
|
protected abstract void bindRequestSigner();
|
||||||
|
|
||||||
protected void bindTemporaryUrlKeyApi() {
|
protected void bindTemporaryUrlKeyApi() {
|
||||||
bindHttpApi(binder(), TemporaryUrlKeyApi.class, TemporaryUrlKeyAsyncApi.class);
|
bindMappedHttpApi(binder(), TemporaryUrlKeyApi.class, TemporaryUrlKeyAsyncApi.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ import static com.google.common.collect.Iterables.transform;
|
||||||
import static com.google.common.collect.Maps.transformValues;
|
import static com.google.common.collect.Maps.transformValues;
|
||||||
import static com.google.common.collect.Maps.uniqueIndex;
|
import static com.google.common.collect.Maps.uniqueIndex;
|
||||||
import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
|
import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
|
||||||
import static org.jclouds.rest.config.BinderUtils.bindHttpApi;
|
import static org.jclouds.rest.config.BinderUtils.bindMappedHttpApi;
|
||||||
import static org.jclouds.util.Predicates2.retry;
|
import static org.jclouds.util.Predicates2.retry;
|
||||||
import static org.jclouds.vcloud.reference.VCloudConstants.PROPERTY_VCLOUD_DEFAULT_FENCEMODE;
|
import static org.jclouds.vcloud.reference.VCloudConstants.PROPERTY_VCLOUD_DEFAULT_FENCEMODE;
|
||||||
import static org.jclouds.vcloud.reference.VCloudConstants.PROPERTY_VCLOUD_TIMEOUT_TASK_COMPLETED;
|
import static org.jclouds.vcloud.reference.VCloudConstants.PROPERTY_VCLOUD_TIMEOUT_TASK_COMPLETED;
|
||||||
|
@ -206,8 +206,8 @@ public class VCloudRestClientModule extends RestClientModule<VCloudClient, VClou
|
||||||
bind(new TypeLiteral<Function<VAppTemplate, Envelope>>() {
|
bind(new TypeLiteral<Function<VAppTemplate, Envelope>>() {
|
||||||
}).to(new TypeLiteral<ValidateVAppTemplateAndReturnEnvelopeOrThrowIllegalArgumentException>() {
|
}).to(new TypeLiteral<ValidateVAppTemplateAndReturnEnvelopeOrThrowIllegalArgumentException>() {
|
||||||
});
|
});
|
||||||
bindHttpApi(binder(), VCloudVersionsClient.class, VCloudVersionsAsyncClient.class);
|
bindMappedHttpApi(binder(), VCloudVersionsClient.class, VCloudVersionsAsyncClient.class);
|
||||||
bindHttpApi(binder(), VCloudLoginClient.class, VCloudLoginAsyncClient.class);
|
bindMappedHttpApi(binder(), VCloudLoginClient.class, VCloudLoginAsyncClient.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void bindCacheLoaders() {
|
protected void bindCacheLoaders() {
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
*/
|
*/
|
||||||
package org.jclouds.blobstore.config;
|
package org.jclouds.blobstore.config;
|
||||||
|
|
||||||
import static org.jclouds.rest.config.BinderUtils.bindBlockingApi;
|
import static org.jclouds.rest.config.BinderUtils.bindMappedApi;
|
||||||
|
|
||||||
import org.jclouds.blobstore.AsyncBlobStore;
|
import org.jclouds.blobstore.AsyncBlobStore;
|
||||||
import org.jclouds.blobstore.BlobRequestSigner;
|
import org.jclouds.blobstore.BlobRequestSigner;
|
||||||
|
@ -41,7 +41,7 @@ public class TransientBlobStoreContextModule extends AbstractModule {
|
||||||
protected void configure() {
|
protected void configure() {
|
||||||
bind(AsyncBlobStore.class).to(LocalAsyncBlobStore.class).asEagerSingleton();
|
bind(AsyncBlobStore.class).to(LocalAsyncBlobStore.class).asEagerSingleton();
|
||||||
// forward all requests from TransientBlobStore to TransientAsyncBlobStore. needs above binding as cannot proxy a class
|
// forward all requests from TransientBlobStore to TransientAsyncBlobStore. needs above binding as cannot proxy a class
|
||||||
bindBlockingApi(binder(), LocalBlobStore.class, AsyncBlobStore.class);
|
bindMappedApi(binder(), LocalBlobStore.class, AsyncBlobStore.class);
|
||||||
install(new BlobStoreObjectModule());
|
install(new BlobStoreObjectModule());
|
||||||
install(new BlobStoreMapModule());
|
install(new BlobStoreMapModule());
|
||||||
bind(BlobStore.class).to(LocalBlobStore.class);
|
bind(BlobStore.class).to(LocalBlobStore.class);
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
package org.jclouds.openstack.config;
|
package org.jclouds.openstack.config;
|
||||||
|
|
||||||
import static com.google.common.base.Suppliers.memoizeWithExpiration;
|
import static com.google.common.base.Suppliers.memoizeWithExpiration;
|
||||||
import static org.jclouds.rest.config.BinderUtils.bindHttpApi;
|
import static org.jclouds.rest.config.BinderUtils.bindMappedHttpApi;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
|
@ -59,7 +59,7 @@ public class OpenStackAuthenticationModule extends AbstractModule {
|
||||||
@Override
|
@Override
|
||||||
protected void configure() {
|
protected void configure() {
|
||||||
// OpenStackAuthClient is used directly for filters and retry handlers, so let's bind it explicitly
|
// OpenStackAuthClient is used directly for filters and retry handlers, so let's bind it explicitly
|
||||||
bindHttpApi(binder(), OpenStackAuthClient.class, OpenStackAuthAsyncClient.class);
|
bindMappedHttpApi(binder(), OpenStackAuthClient.class, OpenStackAuthAsyncClient.class);
|
||||||
install(new FactoryModuleBuilder().build(URIFromAuthenticationResponseForService.Factory.class));
|
install(new FactoryModuleBuilder().build(URIFromAuthenticationResponseForService.Factory.class));
|
||||||
bind(HttpRetryHandler.class).annotatedWith(ClientError.class).to(RetryOnRenew.class);
|
bind(HttpRetryHandler.class).annotatedWith(ClientError.class).to(RetryOnRenew.class);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
*/
|
*/
|
||||||
package org.jclouds.openstack.keystone.v1_1.config;
|
package org.jclouds.openstack.keystone.v1_1.config;
|
||||||
|
|
||||||
import static org.jclouds.rest.config.BinderUtils.bindHttpApi;
|
import static org.jclouds.rest.config.BinderUtils.bindMappedHttpApi;
|
||||||
import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
|
import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
|
||||||
|
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
|
@ -60,7 +60,7 @@ public class AuthenticationServiceModule extends AbstractModule {
|
||||||
@Override
|
@Override
|
||||||
protected void configure() {
|
protected void configure() {
|
||||||
// ServiceClient is used directly for filters and retry handlers, so let's bind it explicitly
|
// ServiceClient is used directly for filters and retry handlers, so let's bind it explicitly
|
||||||
bindHttpApi(binder(), AuthenticationClient.class, AuthenticationAsyncClient.class);
|
bindMappedHttpApi(binder(), AuthenticationClient.class, AuthenticationAsyncClient.class);
|
||||||
install(new FactoryModuleBuilder().implement(RegionIdToURISupplier.class,
|
install(new FactoryModuleBuilder().implement(RegionIdToURISupplier.class,
|
||||||
RegionIdToURIFromAuthForServiceSupplier.class).build(RegionIdToURISupplier.Factory.class));
|
RegionIdToURIFromAuthForServiceSupplier.class).build(RegionIdToURISupplier.Factory.class));
|
||||||
install(new FactoryModuleBuilder().implement(ImplicitRegionIdSupplier.class, V1DefaultRegionIdSupplier.class)
|
install(new FactoryModuleBuilder().implement(ImplicitRegionIdSupplier.class, V1DefaultRegionIdSupplier.class)
|
||||||
|
|
|
@ -26,7 +26,7 @@ import static com.google.common.collect.Iterables.transform;
|
||||||
import static com.google.common.collect.Maps.transformValues;
|
import static com.google.common.collect.Maps.transformValues;
|
||||||
import static com.google.common.collect.Maps.uniqueIndex;
|
import static com.google.common.collect.Maps.uniqueIndex;
|
||||||
import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
|
import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
|
||||||
import static org.jclouds.rest.config.BinderUtils.bindHttpApi;
|
import static org.jclouds.rest.config.BinderUtils.bindMappedHttpApi;
|
||||||
import static org.jclouds.trmk.vcloud_0_8.reference.VCloudConstants.PROPERTY_VCLOUD_TIMEOUT_TASK_COMPLETED;
|
import static org.jclouds.trmk.vcloud_0_8.reference.VCloudConstants.PROPERTY_VCLOUD_TIMEOUT_TASK_COMPLETED;
|
||||||
import static org.jclouds.util.Predicates2.retry;
|
import static org.jclouds.util.Predicates2.retry;
|
||||||
|
|
||||||
|
@ -134,8 +134,8 @@ public class TerremarkVCloudRestClientModule<S, A> extends RestClientModule<S, A
|
||||||
bind(new TypeLiteral<Function<org.jclouds.trmk.vcloud_0_8.domain.Org, Iterable<? extends CatalogItem>>>() {
|
bind(new TypeLiteral<Function<org.jclouds.trmk.vcloud_0_8.domain.Org, Iterable<? extends CatalogItem>>>() {
|
||||||
}).to(new TypeLiteral<AllCatalogItemsInOrg>() {
|
}).to(new TypeLiteral<AllCatalogItemsInOrg>() {
|
||||||
});
|
});
|
||||||
bindHttpApi(binder(), TerremarkVCloudVersionsClient.class, TerremarkVCloudVersionsAsyncClient.class);
|
bindMappedHttpApi(binder(), TerremarkVCloudVersionsClient.class, TerremarkVCloudVersionsAsyncClient.class);
|
||||||
bindHttpApi(binder(), TerremarkVCloudLoginClient.class, TerremarkVCloudLoginAsyncClient.class);
|
bindMappedHttpApi(binder(), TerremarkVCloudLoginClient.class, TerremarkVCloudLoginAsyncClient.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
|
|
|
@ -31,7 +31,6 @@ import org.jclouds.date.DateService;
|
||||||
import org.jclouds.domain.Credentials;
|
import org.jclouds.domain.Credentials;
|
||||||
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.HttpClient;
|
import org.jclouds.rest.HttpClient;
|
||||||
import org.jclouds.ssh.SshClient;
|
import org.jclouds.ssh.SshClient;
|
||||||
import org.jclouds.ssh.SshClient.Factory;
|
import org.jclouds.ssh.SshClient.Factory;
|
||||||
|
@ -54,8 +53,9 @@ public class UtilsImpl extends org.jclouds.rest.internal.UtilsImpl implements Ut
|
||||||
private final Function<NodeMetadata, SshClient> sshForNode;
|
private final Function<NodeMetadata, SshClient> sshForNode;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
UtilsImpl(Injector injector, Json json, XMLParser xml, HttpClient simpleClient, HttpAsyncClient simpleAsyncClient,
|
UtilsImpl(Injector injector, Json json, XMLParser xml, HttpClient simpleClient,
|
||||||
Crypto encryption, DateService date, @Named(Constants.PROPERTY_USER_THREADS) ListeningExecutorService userExecutor,
|
org.jclouds.rest.HttpAsyncClient simpleAsyncClient, Crypto encryption, DateService date,
|
||||||
|
@Named(Constants.PROPERTY_USER_THREADS) ListeningExecutorService userExecutor,
|
||||||
@Named(Constants.PROPERTY_IO_WORKER_THREADS) ListeningExecutorService ioExecutor, EventBus eventBus,
|
@Named(Constants.PROPERTY_IO_WORKER_THREADS) ListeningExecutorService ioExecutor, EventBus eventBus,
|
||||||
Map<String, Credentials> credentialStore, LoggerFactory loggerFactory,
|
Map<String, Credentials> credentialStore, LoggerFactory loggerFactory,
|
||||||
Function<NodeMetadata, SshClient> sshForNode) {
|
Function<NodeMetadata, SshClient> sshForNode) {
|
||||||
|
|
|
@ -20,10 +20,12 @@ package org.jclouds;
|
||||||
|
|
||||||
import static com.google.common.base.Objects.toStringHelper;
|
import static com.google.common.base.Objects.toStringHelper;
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
import static com.google.common.base.Predicates.and;
|
||||||
import static com.google.common.base.Predicates.containsPattern;
|
import static com.google.common.base.Predicates.containsPattern;
|
||||||
import static com.google.common.base.Predicates.instanceOf;
|
import static com.google.common.base.Predicates.instanceOf;
|
||||||
import static com.google.common.base.Predicates.not;
|
import static com.google.common.base.Predicates.not;
|
||||||
import static com.google.common.base.Predicates.notNull;
|
import static com.google.common.base.Predicates.notNull;
|
||||||
|
import static com.google.common.base.Predicates.or;
|
||||||
import static com.google.common.base.Throwables.propagate;
|
import static com.google.common.base.Throwables.propagate;
|
||||||
import static com.google.common.collect.Iterables.addAll;
|
import static com.google.common.collect.Iterables.addAll;
|
||||||
import static com.google.common.collect.Iterables.any;
|
import static com.google.common.collect.Iterables.any;
|
||||||
|
@ -57,6 +59,7 @@ import org.jclouds.apis.Apis;
|
||||||
import org.jclouds.concurrent.SingleThreaded;
|
import org.jclouds.concurrent.SingleThreaded;
|
||||||
import org.jclouds.concurrent.config.ConfiguresExecutorService;
|
import org.jclouds.concurrent.config.ConfiguresExecutorService;
|
||||||
import org.jclouds.concurrent.config.ExecutorServiceModule;
|
import org.jclouds.concurrent.config.ExecutorServiceModule;
|
||||||
|
import org.jclouds.config.BindApiContextWithWildcardExtendsExplicitAndRawType;
|
||||||
import org.jclouds.config.BindNameToContext;
|
import org.jclouds.config.BindNameToContext;
|
||||||
import org.jclouds.config.BindPropertiesToExpandedValues;
|
import org.jclouds.config.BindPropertiesToExpandedValues;
|
||||||
import org.jclouds.config.BindRestContextWithWildcardExtendsExplicitAndRawType;
|
import org.jclouds.config.BindRestContextWithWildcardExtendsExplicitAndRawType;
|
||||||
|
@ -75,9 +78,14 @@ import org.jclouds.providers.Providers;
|
||||||
import org.jclouds.providers.config.BindProviderMetadataContextAndCredentials;
|
import org.jclouds.providers.config.BindProviderMetadataContextAndCredentials;
|
||||||
import org.jclouds.providers.internal.UpdateProviderMetadataFromProperties;
|
import org.jclouds.providers.internal.UpdateProviderMetadataFromProperties;
|
||||||
import org.jclouds.rest.ConfiguresCredentialStore;
|
import org.jclouds.rest.ConfiguresCredentialStore;
|
||||||
|
import org.jclouds.rest.ConfiguresHttpApi;
|
||||||
import org.jclouds.rest.ConfiguresRestClient;
|
import org.jclouds.rest.ConfiguresRestClient;
|
||||||
|
import org.jclouds.rest.HttpApiMetadata;
|
||||||
import org.jclouds.rest.RestApiMetadata;
|
import org.jclouds.rest.RestApiMetadata;
|
||||||
|
import org.jclouds.rest.RestContext;
|
||||||
import org.jclouds.rest.config.CredentialStoreModule;
|
import org.jclouds.rest.config.CredentialStoreModule;
|
||||||
|
import org.jclouds.rest.config.HttpApiModule;
|
||||||
|
import org.jclouds.rest.config.MappedHttpInvocationModule;
|
||||||
import org.jclouds.rest.config.RestClientModule;
|
import org.jclouds.rest.config.RestClientModule;
|
||||||
import org.jclouds.rest.config.RestModule;
|
import org.jclouds.rest.config.RestModule;
|
||||||
|
|
||||||
|
@ -382,9 +390,9 @@ public class ContextBuilder {
|
||||||
public static Injector buildInjector(String name, ProviderMetadata providerMetadata, Supplier<Credentials> creds, List<Module> inputModules) {
|
public static Injector buildInjector(String name, ProviderMetadata providerMetadata, Supplier<Credentials> creds, List<Module> inputModules) {
|
||||||
List<Module> modules = newArrayList();
|
List<Module> modules = newArrayList();
|
||||||
modules.addAll(inputModules);
|
modules.addAll(inputModules);
|
||||||
boolean restModuleSpecifiedByUser = restClientModulePresent(inputModules);
|
boolean apiModuleSpecifiedByUser = apiModulePresent(inputModules);
|
||||||
Iterable<Module> defaultModules = ifSpecifiedByUserDontIncludeDefaultRestModule(
|
Iterable<Module> defaultModules = ifSpecifiedByUserDontIncludeDefaultApiModule(
|
||||||
providerMetadata.getApiMetadata(), restModuleSpecifiedByUser);
|
providerMetadata.getApiMetadata(), apiModuleSpecifiedByUser);
|
||||||
addAll(modules, defaultModules);
|
addAll(modules, defaultModules);
|
||||||
addClientModuleIfNotPresent(providerMetadata.getApiMetadata(), modules);
|
addClientModuleIfNotPresent(providerMetadata.getApiMetadata(), modules);
|
||||||
addRestContextBinding(providerMetadata.getApiMetadata(), modules);
|
addRestContextBinding(providerMetadata.getApiMetadata(), modules);
|
||||||
|
@ -415,7 +423,14 @@ public class ContextBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void addRestContextBinding(ApiMetadata apiMetadata, List<Module> modules) {
|
static void addRestContextBinding(ApiMetadata apiMetadata, List<Module> modules) {
|
||||||
if (apiMetadata instanceof RestApiMetadata) {
|
if (apiMetadata instanceof HttpApiMetadata) {
|
||||||
|
try {
|
||||||
|
modules
|
||||||
|
.add(new BindApiContextWithWildcardExtendsExplicitAndRawType(HttpApiMetadata.class.cast(apiMetadata)));
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
} else if (apiMetadata instanceof RestApiMetadata) {
|
||||||
try {
|
try {
|
||||||
modules.add(new BindRestContextWithWildcardExtendsExplicitAndRawType(RestApiMetadata.class
|
modules.add(new BindRestContextWithWildcardExtendsExplicitAndRawType(RestApiMetadata.class
|
||||||
.cast(apiMetadata)));
|
.cast(apiMetadata)));
|
||||||
|
@ -425,7 +440,7 @@ public class ContextBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Iterable<Module> ifSpecifiedByUserDontIncludeDefaultRestModule(ApiMetadata apiMetadata,
|
static Iterable<Module> ifSpecifiedByUserDontIncludeDefaultApiModule(ApiMetadata apiMetadata,
|
||||||
boolean restModuleSpecifiedByUser) {
|
boolean restModuleSpecifiedByUser) {
|
||||||
Iterable<Module> defaultModules = transform(apiMetadata.getDefaultModules(),
|
Iterable<Module> defaultModules = transform(apiMetadata.getDefaultModules(),
|
||||||
new Function<Class<? extends Module>, Module>() {
|
new Function<Class<? extends Module>, Module>() {
|
||||||
|
@ -443,7 +458,7 @@ public class ContextBuilder {
|
||||||
|
|
||||||
});
|
});
|
||||||
if (restModuleSpecifiedByUser)
|
if (restModuleSpecifiedByUser)
|
||||||
defaultModules = filter(defaultModules, not(configuresRest));
|
defaultModules = filter(defaultModules, and(not(configuresApi), not(configuresRest)));
|
||||||
return defaultModules;
|
return defaultModules;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -476,30 +491,40 @@ public class ContextBuilder {
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
static void addClientModuleIfNotPresent(ApiMetadata apiMetadata, List<Module> modules) {
|
static void addClientModuleIfNotPresent(ApiMetadata apiMetadata, List<Module> modules) {
|
||||||
if (!restClientModulePresent(modules)) {
|
if (!apiModulePresent(modules)) {
|
||||||
addClientModule(apiMetadata, modules);
|
addClientModule(apiMetadata, modules);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
private static boolean apiModulePresent(List<Module> modules) {
|
||||||
|
return any(modules, or(configuresApi, configuresRest));
|
||||||
|
}
|
||||||
|
|
||||||
static Predicate<Module> configuresRest = new Predicate<Module>() {
|
private static Predicate<Module> configuresApi = new Predicate<Module>() {
|
||||||
|
public boolean apply(Module input) {
|
||||||
|
return input.getClass().isAnnotationPresent(ConfiguresHttpApi.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
private static Predicate<Module> configuresRest = new Predicate<Module>() {
|
||||||
public boolean apply(Module input) {
|
public boolean apply(Module input) {
|
||||||
return input.getClass().isAnnotationPresent(ConfiguresRestClient.class);
|
return input.getClass().isAnnotationPresent(ConfiguresRestClient.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static boolean restClientModulePresent(List<Module> modules) {
|
|
||||||
return any(modules, configuresRest);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||||
static void addClientModule(ApiMetadata apiMetadata, List<Module> modules) {
|
static void addClientModule(ApiMetadata apiMetadata, List<Module> modules) {
|
||||||
// TODO: move this up
|
// TODO: move this up
|
||||||
if (apiMetadata instanceof RestApiMetadata) {
|
if (apiMetadata instanceof HttpApiMetadata) {
|
||||||
|
HttpApiMetadata api = HttpApiMetadata.class.cast(apiMetadata);
|
||||||
|
modules.add(new HttpApiModule(api.getApi()));
|
||||||
|
} else if (apiMetadata instanceof RestApiMetadata) {
|
||||||
RestApiMetadata rest = RestApiMetadata.class.cast(apiMetadata);
|
RestApiMetadata rest = RestApiMetadata.class.cast(apiMetadata);
|
||||||
modules.add(new RestClientModule(typeToken(rest.getApi()), typeToken(rest.getAsyncApi())));
|
modules.add(new RestClientModule(typeToken(rest.getApi()), typeToken(rest.getAsyncApi())));
|
||||||
} else {
|
} else {
|
||||||
modules.add(new RestModule());
|
modules.add(new RestModule());
|
||||||
|
modules.add(new MappedHttpInvocationModule());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
/**
|
||||||
|
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. jclouds licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.jclouds.config;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
import static org.jclouds.reflect.Reflection2.typeToken;
|
||||||
|
|
||||||
|
import org.jclouds.rest.ApiContext;
|
||||||
|
import org.jclouds.rest.HttpApiMetadata;
|
||||||
|
import org.jclouds.rest.internal.ApiContextImpl;
|
||||||
|
import org.jclouds.rest.internal.BaseHttpApiMetadata;
|
||||||
|
|
||||||
|
import com.google.common.reflect.TypeToken;
|
||||||
|
import com.google.inject.AbstractModule;
|
||||||
|
import com.google.inject.TypeLiteral;
|
||||||
|
import com.google.inject.util.Types;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows you to lookup the {@link HttpApiMetadata#getContext()} as
|
||||||
|
* {@link ApiContext}, {@code ApiContext<Api>}, and {@code ApiContext<?>}.
|
||||||
|
*
|
||||||
|
* @author Adrian Cole
|
||||||
|
*/
|
||||||
|
public class BindApiContextWithWildcardExtendsExplicitAndRawType extends AbstractModule {
|
||||||
|
private final HttpApiMetadata<?> httpApiMetadata;
|
||||||
|
|
||||||
|
public BindApiContextWithWildcardExtendsExplicitAndRawType(HttpApiMetadata<?> httpApiMetadata)
|
||||||
|
throws IllegalArgumentException {
|
||||||
|
this.httpApiMetadata = checkNotNull(httpApiMetadata, "httpApiMetadata");
|
||||||
|
checkArgument(httpApiMetadata.getContext().getRawType().equals(ApiContext.class),
|
||||||
|
"this does not work as %s raw type is not ApiContext", httpApiMetadata.getContext());
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
protected void configure() {
|
||||||
|
TypeToken<?> concreteType = BaseHttpApiMetadata.contextToken(typeToken(httpApiMetadata.getApi()));
|
||||||
|
// bind explicit type
|
||||||
|
bind(TypeLiteral.get(concreteType.getType())).to(
|
||||||
|
TypeLiteral.class.cast(TypeLiteral.get(Types.newParameterizedType(ApiContextImpl.class,
|
||||||
|
httpApiMetadata.getApi()))));
|
||||||
|
// bind potentially wildcard type
|
||||||
|
if (!concreteType.equals(httpApiMetadata.getContext())) {
|
||||||
|
bind(TypeLiteral.get(httpApiMetadata.getContext().getType())).to(
|
||||||
|
TypeLiteral.class.cast(TypeLiteral.get(Types.newParameterizedType(ApiContextImpl.class,
|
||||||
|
httpApiMetadata.getApi()))));
|
||||||
|
}
|
||||||
|
// bind w/o types
|
||||||
|
bind(TypeLiteral.get(ApiContext.class)).to(
|
||||||
|
TypeLiteral.class.cast(TypeLiteral.get(Types.newParameterizedType(ApiContextImpl.class,
|
||||||
|
httpApiMetadata.getApi()))));
|
||||||
|
}
|
||||||
|
}
|
|
@ -35,7 +35,10 @@ import com.google.inject.util.Types;
|
||||||
* Allows you to lookup the {@link RestApiMetadata#getContext()} as {@link RestContext}, {@code RestContext<Client, AsyncClient>}, and {@code
|
* Allows you to lookup the {@link RestApiMetadata#getContext()} as {@link RestContext}, {@code RestContext<Client, AsyncClient>}, and {@code
|
||||||
*
|
*
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
|
* @deprecated please use {@link BindApiContextWithWildcardExtendsExplicitAndRawType} as
|
||||||
|
* async interface will be removed in jclouds 1.7.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public class BindRestContextWithWildcardExtendsExplicitAndRawType extends AbstractModule {
|
public class BindRestContextWithWildcardExtendsExplicitAndRawType extends AbstractModule {
|
||||||
private final RestApiMetadata restApiMetadata;
|
private final RestApiMetadata restApiMetadata;
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
import org.jclouds.apis.ApiMetadata;
|
import org.jclouds.apis.ApiMetadata;
|
||||||
import org.jclouds.providers.internal.BaseProviderMetadata;
|
import org.jclouds.providers.internal.BaseProviderMetadata;
|
||||||
|
import org.jclouds.rest.AnonymousHttpApiMetadata;
|
||||||
import org.jclouds.rest.AnonymousRestApiMetadata;
|
import org.jclouds.rest.AnonymousRestApiMetadata;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -31,11 +32,14 @@ import org.jclouds.rest.AnonymousRestApiMetadata;
|
||||||
*/
|
*/
|
||||||
public class AnonymousProviderMetadata extends BaseProviderMetadata {
|
public class AnonymousProviderMetadata extends BaseProviderMetadata {
|
||||||
|
|
||||||
|
public static <A> ProviderMetadata forApiOnEndpoint(Class<A> api, String endpoint) {
|
||||||
|
return forApiWithEndpoint(AnonymousHttpApiMetadata.forApi(api), endpoint);
|
||||||
|
}
|
||||||
|
|
||||||
public static ProviderMetadata forClientMappedToAsyncClientOnEndpoint(Class<?> client, Class<?> asyncClient,
|
public static ProviderMetadata forClientMappedToAsyncClientOnEndpoint(Class<?> client, Class<?> asyncClient,
|
||||||
String endpoint) {
|
String endpoint) {
|
||||||
return forApiWithEndpoint(AnonymousRestApiMetadata.forClientMappedToAsyncClient(client, asyncClient), endpoint);
|
return forApiWithEndpoint(AnonymousRestApiMetadata.forClientMappedToAsyncClient(client, asyncClient), endpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ProviderMetadata forApiWithEndpoint(ApiMetadata md, String endpoint) {
|
public static ProviderMetadata forApiWithEndpoint(ApiMetadata md, String endpoint) {
|
||||||
checkNotNull(md, "api");
|
checkNotNull(md, "api");
|
||||||
checkNotNull(endpoint, "endpoint (%s)", md.getEndpointName());
|
checkNotNull(endpoint, "endpoint (%s)", md.getEndpointName());
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
/**
|
||||||
|
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. jclouds licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.jclouds.reflect;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkState;
|
||||||
|
|
||||||
|
import java.lang.reflect.TypeVariable;
|
||||||
|
|
||||||
|
import com.google.common.annotations.Beta;
|
||||||
|
import com.google.common.reflect.TypeToken;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 1.7
|
||||||
|
*/
|
||||||
|
@Beta
|
||||||
|
public class Types2 {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helpful when you are capturing the type inside a constructor.
|
||||||
|
*
|
||||||
|
* @throws IllegalStateException
|
||||||
|
* if the type is an instanceof {@link TypeVariable}
|
||||||
|
*/
|
||||||
|
public static <T> TypeToken<T> checkBound(TypeToken<T> type) throws IllegalStateException {
|
||||||
|
checkState(!(type.getType() instanceof TypeVariable<?>),
|
||||||
|
"unbound type variable: %s, use ctor that explicitly assigns this", type);
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
/**
|
||||||
|
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. jclouds licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.jclouds.rest;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
|
||||||
|
import org.jclouds.rest.internal.BaseHttpApiMetadata;
|
||||||
|
|
||||||
|
import com.google.common.annotations.Beta;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Useful in creating arbitrary http apis.
|
||||||
|
*
|
||||||
|
* @author Adrian Cole
|
||||||
|
*/
|
||||||
|
@Beta
|
||||||
|
public class AnonymousHttpApiMetadata<A> extends BaseHttpApiMetadata<A> {
|
||||||
|
|
||||||
|
public static <A> AnonymousHttpApiMetadata<A> forApi(Class<A> httpApi) {
|
||||||
|
return new Builder<A>(httpApi).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Builder<A> toBuilder() {
|
||||||
|
return new Builder<A>(getApi()).fromApiMetadata(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private AnonymousHttpApiMetadata(Builder<A> builder) {
|
||||||
|
super(builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class Builder<A> extends BaseHttpApiMetadata.Builder<A, Builder<A>> {
|
||||||
|
|
||||||
|
private Builder(Class<A> api) {
|
||||||
|
super(api);
|
||||||
|
id(api.getSimpleName())
|
||||||
|
.identityName("unused")
|
||||||
|
.defaultIdentity("foo")
|
||||||
|
.version("1")
|
||||||
|
.documentation(URI.create("http://jclouds.org/documentation"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AnonymousHttpApiMetadata<A> build() {
|
||||||
|
return new AnonymousHttpApiMetadata<A>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Builder<A> self() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,6 +20,7 @@ package org.jclouds.rest;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
|
||||||
|
import org.jclouds.rest.internal.BaseHttpApiMetadata;
|
||||||
import org.jclouds.rest.internal.BaseRestApiMetadata;
|
import org.jclouds.rest.internal.BaseRestApiMetadata;
|
||||||
|
|
||||||
import com.google.common.annotations.Beta;
|
import com.google.common.annotations.Beta;
|
||||||
|
@ -28,6 +29,8 @@ import com.google.common.annotations.Beta;
|
||||||
* Useful in creating arbitrary clients.
|
* Useful in creating arbitrary clients.
|
||||||
*
|
*
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
|
* @deprecated please use {@link AnonymousHttpApiMetadata} as
|
||||||
|
* async interface will be removed in jclouds 1.7.
|
||||||
*/
|
*/
|
||||||
@Beta
|
@Beta
|
||||||
public class AnonymousRestApiMetadata extends BaseRestApiMetadata {
|
public class AnonymousRestApiMetadata extends BaseRestApiMetadata {
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
/**
|
||||||
|
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. jclouds licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.jclouds.rest;
|
||||||
|
|
||||||
|
import org.jclouds.Context;
|
||||||
|
import org.jclouds.rest.internal.ApiContextImpl;
|
||||||
|
|
||||||
|
import com.google.inject.ImplementedBy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an authenticated context to the cloud.
|
||||||
|
*
|
||||||
|
* <h2>Note</h2> Please issue {@link #close()} when you are finished with this context in order to
|
||||||
|
* release resources.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @author Adrian Cole
|
||||||
|
*/
|
||||||
|
@ImplementedBy(ApiContextImpl.class)
|
||||||
|
public interface ApiContext<A> extends Context {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* low-level api to the cloud. Threadsafe implementations will return a singleton.
|
||||||
|
*
|
||||||
|
* @return a connection to the cloud where all methods block
|
||||||
|
*/
|
||||||
|
A getApi();
|
||||||
|
}
|
|
@ -25,13 +25,13 @@ import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* designates the module configures a Client to a cloud.
|
* designates the module configures a top-level api which is annotated with http methods.
|
||||||
*
|
*
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@Retention(RUNTIME)
|
@Retention(RUNTIME)
|
||||||
@Target(TYPE)
|
@Target(TYPE)
|
||||||
public @interface ConfiguresRestContext {
|
public @interface ConfiguresHttpApi {
|
||||||
|
|
||||||
}
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
/**
|
||||||
|
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. jclouds licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.jclouds.rest;
|
||||||
|
|
||||||
|
import org.jclouds.apis.ApiMetadata;
|
||||||
|
|
||||||
|
import com.google.common.annotations.Beta;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Adrian Cole
|
||||||
|
* @since 1.6
|
||||||
|
* @see ConfiguresHttpApi
|
||||||
|
*/
|
||||||
|
@Beta
|
||||||
|
public interface HttpApiMetadata<A> extends ApiMetadata {
|
||||||
|
|
||||||
|
public static interface Builder<A, T extends Builder<A, T>> extends ApiMetadata.Builder<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see ApiMetadata#getApi()
|
||||||
|
*/
|
||||||
|
T javaApi(Class<A> api);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return the type of the api which has http annotations on its methods.
|
||||||
|
*/
|
||||||
|
Class<A> getApi();
|
||||||
|
}
|
|
@ -43,7 +43,10 @@ import com.google.common.util.concurrent.ListenableFuture;
|
||||||
* Simple rest client
|
* Simple rest client
|
||||||
*
|
*
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
|
* @deprecated will be removed in jclouds 1.7, as async interfaces are no longer
|
||||||
|
* supported.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public interface HttpAsyncClient {
|
public interface HttpAsyncClient {
|
||||||
/**
|
/**
|
||||||
* @see HttpClient#put
|
* @see HttpClient#put
|
||||||
|
|
|
@ -21,9 +21,21 @@ package org.jclouds.rest;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
|
||||||
|
import javax.ws.rs.DELETE;
|
||||||
|
import javax.ws.rs.GET;
|
||||||
|
import javax.ws.rs.HEAD;
|
||||||
|
import javax.ws.rs.POST;
|
||||||
|
import javax.ws.rs.PUT;
|
||||||
|
|
||||||
|
import org.jclouds.Fallbacks.FalseOnNotFoundOr404;
|
||||||
|
import org.jclouds.Fallbacks.NullOnNotFoundOr404;
|
||||||
import org.jclouds.http.HttpRequest;
|
import org.jclouds.http.HttpRequest;
|
||||||
import org.jclouds.http.HttpResponse;
|
import org.jclouds.http.HttpResponse;
|
||||||
|
import org.jclouds.http.functions.ParseETagHeader;
|
||||||
import org.jclouds.io.Payload;
|
import org.jclouds.io.Payload;
|
||||||
|
import org.jclouds.rest.annotations.EndpointParam;
|
||||||
|
import org.jclouds.rest.annotations.Fallback;
|
||||||
|
import org.jclouds.rest.annotations.ResponseParser;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple client
|
* Simple client
|
||||||
|
@ -31,6 +43,34 @@ import org.jclouds.io.Payload;
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
*/
|
*/
|
||||||
public interface HttpClient {
|
public interface HttpClient {
|
||||||
|
/**
|
||||||
|
* @return eTag
|
||||||
|
*/
|
||||||
|
@PUT
|
||||||
|
@ResponseParser(ParseETagHeader.class)
|
||||||
|
String put(@EndpointParam URI location, Payload payload);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return eTag
|
||||||
|
*/
|
||||||
|
@POST
|
||||||
|
@ResponseParser(ParseETagHeader.class)
|
||||||
|
String post(@EndpointParam URI location, Payload payload);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see HttpClient#exists
|
||||||
|
*/
|
||||||
|
@HEAD
|
||||||
|
@Fallback(FalseOnNotFoundOr404.class)
|
||||||
|
boolean exists(@EndpointParam URI location);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return null if the resource didn't exist.
|
||||||
|
*/
|
||||||
|
@GET
|
||||||
|
@Fallback(NullOnNotFoundOr404.class)
|
||||||
|
InputStream get(@EndpointParam URI location);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param request
|
* @param request
|
||||||
|
@ -38,25 +78,11 @@ public interface HttpClient {
|
||||||
*/
|
*/
|
||||||
HttpResponse invoke(HttpRequest request);
|
HttpResponse invoke(HttpRequest request);
|
||||||
|
|
||||||
/**
|
|
||||||
* @return eTag
|
|
||||||
*/
|
|
||||||
String put(URI location, Payload payload);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return eTag
|
|
||||||
*/
|
|
||||||
String post(URI location, Payload payload);
|
|
||||||
|
|
||||||
boolean exists(URI location);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return null if the resource didn't exist.
|
|
||||||
*/
|
|
||||||
InputStream get(URI location);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return false if the resource didn't exist.
|
* @return false if the resource didn't exist.
|
||||||
*/
|
*/
|
||||||
boolean delete(URI location);
|
@DELETE
|
||||||
|
@Fallback(FalseOnNotFoundOr404.class)
|
||||||
|
boolean delete(@EndpointParam URI location);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,12 +21,17 @@ package org.jclouds.rest;
|
||||||
import org.jclouds.apis.ApiMetadata;
|
import org.jclouds.apis.ApiMetadata;
|
||||||
|
|
||||||
import com.google.common.annotations.Beta;
|
import com.google.common.annotations.Beta;
|
||||||
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
* @since 1.5
|
* @since 1.5
|
||||||
|
*
|
||||||
|
* @deprecated please use {@link HttpApiMetadata} as
|
||||||
|
* async interface will be removed in jclouds 1.7.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
@Beta
|
@Beta
|
||||||
public interface RestApiMetadata extends ApiMetadata {
|
public interface RestApiMetadata extends ApiMetadata {
|
||||||
|
|
||||||
|
|
|
@ -75,11 +75,22 @@ public interface Utils {
|
||||||
*/
|
*/
|
||||||
Json json();
|
Json json();
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @deprecated will be removed in jclouds 1.7, as async interfaces are no
|
||||||
|
* longer supported.
|
||||||
|
* @see #getHttpClient()
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
HttpAsyncClient getHttpAsyncClient();
|
HttpAsyncClient getHttpAsyncClient();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* #see #getHttpAsyncClient
|
*
|
||||||
|
* @deprecated will be removed in jclouds 1.7, as async interfaces are no
|
||||||
|
* longer supported.
|
||||||
|
* @see #http()
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
HttpAsyncClient asyncHttp();
|
HttpAsyncClient asyncHttp();
|
||||||
|
|
||||||
HttpClient getHttpClient();
|
HttpClient getHttpClient();
|
||||||
|
|
|
@ -28,27 +28,26 @@ import org.jclouds.rest.internal.DelegatesToInvocationFunction;
|
||||||
|
|
||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
import com.google.inject.TypeLiteral;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
*/
|
*/
|
||||||
@Singleton
|
@Singleton
|
||||||
public class AsyncHttpApiProvider<A> implements Provider<A> {
|
public class AnnotatedHttpApiProvider<A> implements Provider<A> {
|
||||||
private final Class<? super A> asyncApiType;
|
private final Class<A> annotatedApiType;
|
||||||
private final DelegatesToInvocationFunction<A, Function<Invocation, Object>> httpInvoker;
|
private final DelegatesToInvocationFunction<A, Function<Invocation, Object>> httpInvoker;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private AsyncHttpApiProvider(DelegatesToInvocationFunction<A, Function<Invocation, Object>> httpInvoker,
|
private AnnotatedHttpApiProvider(DelegatesToInvocationFunction<A, Function<Invocation, Object>> httpInvoker,
|
||||||
TypeLiteral<A> asyncApiType) {
|
Class<A> annotatedApiType) {
|
||||||
this.httpInvoker = httpInvoker;
|
this.httpInvoker = httpInvoker;
|
||||||
this.asyncApiType = asyncApiType.getRawType();
|
this.annotatedApiType = annotatedApiType;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@Override
|
@Override
|
||||||
public A get() {
|
public A get() {
|
||||||
return (A) Proxy.newProxyInstance(asyncApiType.getClassLoader(), new Class<?>[] { asyncApiType }, httpInvoker);
|
return (A) Proxy.newProxyInstance(annotatedApiType.getClassLoader(), new Class<?>[] { annotatedApiType }, httpInvoker);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
/**
|
||||||
|
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. jclouds licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.jclouds.rest.config;
|
||||||
|
|
||||||
|
import java.lang.reflect.Proxy;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
import org.jclouds.reflect.Invocation;
|
||||||
|
import org.jclouds.rest.internal.DelegatesToInvocationFunction;
|
||||||
|
import org.jclouds.rest.internal.DelegatesToPotentiallyMappedInvocationFunction;
|
||||||
|
|
||||||
|
import com.google.common.base.Function;
|
||||||
|
import com.google.inject.Provider;
|
||||||
|
import com.google.inject.TypeLiteral;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Adrian Cole
|
||||||
|
* @deprecated please use {@link DelegatesToInvocationFunction} as async
|
||||||
|
* interface will be removed in jclouds 1.7.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
@Singleton
|
||||||
|
public class AnnotatedMappedHttpApiProvider<A> implements Provider<A> {
|
||||||
|
private final Class<? super A> annotatedApiType;
|
||||||
|
private final DelegatesToPotentiallyMappedInvocationFunction<A, Function<Invocation, Object>> httpInvoker;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private AnnotatedMappedHttpApiProvider(
|
||||||
|
DelegatesToPotentiallyMappedInvocationFunction<A, Function<Invocation, Object>> httpInvoker,
|
||||||
|
TypeLiteral<A> annotatedApiType) {
|
||||||
|
this.httpInvoker = httpInvoker;
|
||||||
|
this.annotatedApiType = annotatedApiType.getRawType();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public A get() {
|
||||||
|
return (A) Proxy.newProxyInstance(annotatedApiType.getClassLoader(), new Class<?>[] { annotatedApiType },
|
||||||
|
httpInvoker);
|
||||||
|
}
|
||||||
|
}
|
|
@ -31,7 +31,33 @@ import com.google.inject.TypeLiteral;
|
||||||
public class BinderUtils {
|
public class BinderUtils {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* adds an explicit binding for {@code async} by parsing its annotations. Then. adds an explicit binding for an
|
* adds an explicit binding for {@code async} by parsing its annotations.
|
||||||
|
*
|
||||||
|
* @param <S>
|
||||||
|
* sync interface that blocks
|
||||||
|
* @param <A>
|
||||||
|
* api type with http annotations
|
||||||
|
* @param binder
|
||||||
|
* guice binder
|
||||||
|
* @param api
|
||||||
|
* type with http annotations
|
||||||
|
*/
|
||||||
|
public static <S, A> void bindHttpApi(Binder binder, Class<A> api) {
|
||||||
|
bindClass(binder, api);
|
||||||
|
bindAnnotatedHttpApiProvider(binder, api);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private static <T> void bindAnnotatedHttpApiProvider(Binder binder, Class<T> annotated) {
|
||||||
|
TypeToken<AnnotatedHttpApiProvider<T>> token = new TypeToken<AnnotatedHttpApiProvider<T>>() {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
}.where(new TypeParameter<T>() {
|
||||||
|
}, annotated);
|
||||||
|
binder.bind(annotated).toProvider(TypeLiteral.class.cast(TypeLiteral.get(token.getType())));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* adds an explicit binding for {@code async} by parsing its annotations. Then, adds an explicit binding for an
|
||||||
* interface which synchronously blocks on similar calls to an {@code async} type.
|
* interface which synchronously blocks on similar calls to an {@code async} type.
|
||||||
*
|
*
|
||||||
* @param <S>
|
* @param <S>
|
||||||
|
@ -44,52 +70,79 @@ public class BinderUtils {
|
||||||
* type interface that blocks
|
* type interface that blocks
|
||||||
* @param async
|
* @param async
|
||||||
* type type that returns {@link ListenableFuture}
|
* type type that returns {@link ListenableFuture}
|
||||||
|
*
|
||||||
|
* @deprecated will be removed in jclouds 1.7, as async interfaces are no
|
||||||
|
* longer supported.
|
||||||
*/
|
*/
|
||||||
public static <S, A> void bindHttpApi(Binder binder, Class<S> sync, Class<A> async) {
|
@Deprecated
|
||||||
|
public static <S, A> void bindMappedHttpApi(Binder binder, Class<S> sync, Class<A> async) {
|
||||||
bindClass(binder, sync);
|
bindClass(binder, sync);
|
||||||
bindClass(binder, async);
|
bindClass(binder, async);
|
||||||
bindAsyncHttpApiProvider(binder, async);
|
bindAnnotatedMappedHttpApiProvider(binder, async);
|
||||||
bindHttpApiProvider(binder, sync, async);
|
bindHttpApiProvider(binder, sync, async);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated will be removed in jclouds 1.7, as async interfaces are no
|
||||||
|
* longer supported.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private static <T> void bindAsyncHttpApiProvider(Binder binder, Class<T> async) {
|
private static <T> void bindAnnotatedMappedHttpApiProvider(Binder binder, Class<T> annotated) {
|
||||||
TypeToken<AsyncHttpApiProvider<T>> token = new TypeToken<AsyncHttpApiProvider<T>>() {
|
TypeToken<AnnotatedMappedHttpApiProvider<T>> token = new TypeToken<AnnotatedMappedHttpApiProvider<T>>() {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
}.where(new TypeParameter<T>() {
|
}.where(new TypeParameter<T>() {
|
||||||
}, async);
|
}, annotated);
|
||||||
binder.bind(async).toProvider(TypeLiteral.class.cast(TypeLiteral.get(token.getType())));
|
binder.bind(annotated).toProvider(TypeLiteral.class.cast(TypeLiteral.get(token.getType())));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @deprecated will be removed in jclouds 1.7, as async interfaces are no
|
||||||
|
* longer supported.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private static <S, A> void bindHttpApiProvider(Binder binder, Class<S> sync, Class<A> async) {
|
private static <S, A> void bindHttpApiProvider(Binder binder, Class<S> sync, Class<A> async) {
|
||||||
TypeToken<HttpApiProvider<S, A>> token = new TypeToken<HttpApiProvider<S, A>>() {
|
TypeToken<MappedHttpApiProvider<S, A>> token = new TypeToken<MappedHttpApiProvider<S, A>>() {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
}.where(new TypeParameter<S>() {
|
}.where(new TypeParameter<S>() {
|
||||||
}, sync).where(new TypeParameter<A>() {
|
}, sync).where(new TypeParameter<A>() {
|
||||||
}, async);
|
}, async);
|
||||||
binder.bind(sync).toProvider(TypeLiteral.class.cast(TypeLiteral.get(token.getType())));
|
binder.bind(sync).toProvider(TypeLiteral.class.cast(TypeLiteral.get(token.getType())));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* adds an explicit binding for an interface which synchronously blocks on similar calls to an {@code async} type.
|
* adds an explicit binding for an interface which synchronously blocks on
|
||||||
|
* similar calls to an {@code async} type.
|
||||||
*
|
*
|
||||||
* @param <S>
|
* @param <S>
|
||||||
* sync interface that blocks
|
* sync interface that blocks
|
||||||
* @param <A>
|
* @param <A>
|
||||||
* async type where all methods have same args as {@code sync}, but returns {@link ListenableFuture}
|
* async type where all methods have same args as {@code sync}, but
|
||||||
|
* returns {@link ListenableFuture}
|
||||||
* @param binder
|
* @param binder
|
||||||
* guice binder
|
* guice binder
|
||||||
* @param sync
|
* @param sync
|
||||||
* type interface that blocks
|
* type interface that blocks
|
||||||
* @param async
|
* @param async
|
||||||
* type type that returns {@link ListenableFuture}
|
* type type that returns {@link ListenableFuture}
|
||||||
|
*
|
||||||
|
* @deprecated will be removed in jclouds 1.7, as async interfaces are no
|
||||||
|
* longer supported.
|
||||||
*/
|
*/
|
||||||
public static <S, A> void bindBlockingApi(Binder binder, Class<S> sync, Class<A> async) {
|
@Deprecated
|
||||||
|
public static <S, A> void bindMappedApi(Binder binder, Class<S> sync, Class<A> async) {
|
||||||
bindClass(binder, sync);
|
bindClass(binder, sync);
|
||||||
bindClass(binder, async);
|
bindClass(binder, async);
|
||||||
bindCallGetOnFutures(binder, sync, async);
|
bindCallGetOnFutures(binder, sync, async);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated will be removed in jclouds 1.7, as async interfaces are no
|
||||||
|
* longer supported.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private static <S, A> void bindCallGetOnFutures(Binder binder, Class<S> sync, Class<A> async) {
|
private static <S, A> void bindCallGetOnFutures(Binder binder, Class<S> sync, Class<A> async) {
|
||||||
TypeToken<CallGetOnFuturesProvider<S, A>> token = new TypeToken<CallGetOnFuturesProvider<S, A>>() {
|
TypeToken<CallGetOnFuturesProvider<S, A>> token = new TypeToken<CallGetOnFuturesProvider<S, A>>() {
|
||||||
|
|
|
@ -24,7 +24,7 @@ import java.lang.reflect.Proxy;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
import org.jclouds.rest.internal.DelegatesToInvocationFunction;
|
import org.jclouds.rest.internal.DelegatesToPotentiallyMappedInvocationFunction;
|
||||||
import org.jclouds.rest.internal.InvokeAndCallGetOnFutures;
|
import org.jclouds.rest.internal.InvokeAndCallGetOnFutures;
|
||||||
|
|
||||||
import com.google.common.cache.Cache;
|
import com.google.common.cache.Cache;
|
||||||
|
@ -33,20 +33,22 @@ import com.google.inject.Provider;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
|
* @deprecated will be removed in jclouds 1.7, as async interfaces are no longer supported.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
@Singleton
|
@Singleton
|
||||||
public class CallGetOnFuturesProvider<S, A> implements Provider<S> {
|
public class CallGetOnFuturesProvider<S, A> implements Provider<S> {
|
||||||
|
|
||||||
private final Class<? super S> apiType;
|
private final Class<? super S> apiType;
|
||||||
private final DelegatesToInvocationFunction<S, InvokeAndCallGetOnFutures<A>> syncInvoker;
|
private final DelegatesToPotentiallyMappedInvocationFunction<S, InvokeAndCallGetOnFutures<A>> syncInvoker;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private CallGetOnFuturesProvider(Cache<Invokable<?, ?>, Invokable<?, ?>> invokables,
|
private CallGetOnFuturesProvider(Cache<Invokable<?, ?>, Invokable<?, ?>> invokables,
|
||||||
DelegatesToInvocationFunction<S, InvokeAndCallGetOnFutures<A>> syncInvoker, Class<S> apiType,
|
DelegatesToPotentiallyMappedInvocationFunction<S, InvokeAndCallGetOnFutures<A>> syncInvoker, Class<S> apiType,
|
||||||
Class<A> asyncApiType) {
|
Class<A> asyncApiType) {
|
||||||
this.syncInvoker = syncInvoker;
|
this.syncInvoker = syncInvoker;
|
||||||
this.apiType = apiType;
|
this.apiType = apiType;
|
||||||
RestModule.putInvokables(apiType, asyncApiType, invokables);
|
MappedHttpInvocationModule.putInvokables(apiType, asyncApiType, invokables);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
|
|
|
@ -0,0 +1,102 @@
|
||||||
|
/**
|
||||||
|
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. jclouds licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.jclouds.rest.config;
|
||||||
|
|
||||||
|
import static org.jclouds.reflect.Types2.checkBound;
|
||||||
|
import static org.jclouds.rest.config.BinderUtils.bindHttpApi;
|
||||||
|
|
||||||
|
import org.jclouds.reflect.Invocation;
|
||||||
|
import org.jclouds.rest.ConfiguresHttpApi;
|
||||||
|
import org.jclouds.rest.HttpAsyncClient;
|
||||||
|
import org.jclouds.rest.HttpClient;
|
||||||
|
import org.jclouds.rest.internal.InvokeHttpMethod;
|
||||||
|
|
||||||
|
import com.google.common.base.Function;
|
||||||
|
import com.google.common.reflect.TypeToken;
|
||||||
|
import com.google.inject.TypeLiteral;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Adrian Cole
|
||||||
|
*/
|
||||||
|
@ConfiguresHttpApi
|
||||||
|
public class HttpApiModule<A> extends RestModule {
|
||||||
|
protected final Class<A> api;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Note that this ctor requires that you instantiate w/resolved generic
|
||||||
|
* params. For example, via a subclass of a bound type, or natural
|
||||||
|
* instantiation w/resolved type params.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
protected HttpApiModule() {
|
||||||
|
this.api = Class.class.cast(checkBound(new TypeToken<A>(getClass()) {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
}).getRawType());
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpApiModule(Class<A> api) {
|
||||||
|
this.api = api;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configure() {
|
||||||
|
super.configure();
|
||||||
|
bind(new TypeLiteral<Function<Invocation, Object>>() {
|
||||||
|
}).to(InvokeHttpMethod.class);
|
||||||
|
bindHttpApi(binder(), api);
|
||||||
|
bindHttpApi(binder(), HttpClient.class);
|
||||||
|
// TODO: remove when references are gone
|
||||||
|
bindHttpApi(binder(), HttpAsyncClient.class);
|
||||||
|
bindErrorHandlers();
|
||||||
|
bindRetryHandlers();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* overrides this to change the default retry handlers for the http engine
|
||||||
|
*
|
||||||
|
* ex.
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* bind(HttpRetryHandler.class).annotatedWith(Redirection.class).to(AWSRedirectionRetryHandler.class);
|
||||||
|
* bind(HttpRetryHandler.class).annotatedWith(ClientError.class).to(AWSClientErrorRetryHandler.class);
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
protected void bindRetryHandlers() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* overrides this to change the default error handlers for the http engine
|
||||||
|
*
|
||||||
|
* ex.
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* bind(HttpErrorHandler.class).annotatedWith(Redirection.class).to(ParseAWSErrorFromXmlContent.class);
|
||||||
|
* bind(HttpErrorHandler.class).annotatedWith(ClientError.class).to(ParseAWSErrorFromXmlContent.class);
|
||||||
|
* bind(HttpErrorHandler.class).annotatedWith(ServerError.class).to(ParseAWSErrorFromXmlContent.class);
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
protected void bindErrorHandlers() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -25,7 +25,7 @@ import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
import org.jclouds.reflect.Invocation;
|
import org.jclouds.reflect.Invocation;
|
||||||
import org.jclouds.rest.internal.DelegatesToInvocationFunction;
|
import org.jclouds.rest.internal.DelegatesToPotentiallyMappedInvocationFunction;
|
||||||
|
|
||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
import com.google.common.cache.Cache;
|
import com.google.common.cache.Cache;
|
||||||
|
@ -35,18 +35,20 @@ import com.google.inject.Provider;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
|
* @deprecated will be removed in jclouds 1.7; use {@link AnnotatedHttpApiProvider}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
@Singleton
|
@Singleton
|
||||||
public class HttpApiProvider<S, A> implements Provider<S> {
|
public class MappedHttpApiProvider<S, A> implements Provider<S> {
|
||||||
private final Class<? super S> apiType;
|
private final Class<? super S> apiType;
|
||||||
private final DelegatesToInvocationFunction<S, Function<Invocation, Object>> httpInvoker;
|
private final DelegatesToPotentiallyMappedInvocationFunction<S, Function<Invocation, Object>> httpInvoker;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private HttpApiProvider(Cache<Invokable<?, ?>, Invokable<?, ?>> invokables,
|
private MappedHttpApiProvider(Cache<Invokable<?, ?>, Invokable<?, ?>> invokables,
|
||||||
DelegatesToInvocationFunction<S, Function<Invocation, Object>> httpInvoker, Class<S> apiType, Class<A> asyncApiType) {
|
DelegatesToPotentiallyMappedInvocationFunction<S, Function<Invocation, Object>> httpInvoker, Class<S> apiType, Class<A> asyncApiType) {
|
||||||
this.httpInvoker = httpInvoker;
|
this.httpInvoker = httpInvoker;
|
||||||
this.apiType = apiType;
|
this.apiType = apiType;
|
||||||
RestModule.putInvokables(apiType, asyncApiType, invokables);
|
MappedHttpInvocationModule.putInvokables(apiType, asyncApiType, invokables);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
|
@ -0,0 +1,150 @@
|
||||||
|
/**
|
||||||
|
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. jclouds licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.jclouds.rest.config;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
import static com.google.common.collect.Iterables.toArray;
|
||||||
|
import static com.google.common.collect.Iterables.transform;
|
||||||
|
import static org.jclouds.reflect.Reflection2.method;
|
||||||
|
import static org.jclouds.reflect.Reflection2.methods;
|
||||||
|
|
||||||
|
import java.io.Closeable;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
import org.jclouds.reflect.Invocation;
|
||||||
|
import org.jclouds.rest.HttpAsyncClient;
|
||||||
|
import org.jclouds.rest.HttpClient;
|
||||||
|
import org.jclouds.rest.internal.DelegatesToInvocationFunction;
|
||||||
|
import org.jclouds.rest.internal.DelegatesToPotentiallyMappedInvocationFunction;
|
||||||
|
import org.jclouds.rest.internal.InvokeAndCallGetOnFutures;
|
||||||
|
import org.jclouds.rest.internal.InvokeMappedHttpMethod;
|
||||||
|
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
import com.google.common.base.Function;
|
||||||
|
import com.google.common.cache.Cache;
|
||||||
|
import com.google.common.cache.CacheBuilder;
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.google.common.reflect.Invokable;
|
||||||
|
import com.google.common.reflect.Parameter;
|
||||||
|
import com.google.inject.AbstractModule;
|
||||||
|
import com.google.inject.Provides;
|
||||||
|
import com.google.inject.TypeLiteral;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* supports sync-async mapping
|
||||||
|
*
|
||||||
|
* @deprecated will be removed in jclouds 1.7; use {@link HttpApiModule}
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public class MappedHttpInvocationModule extends AbstractModule {
|
||||||
|
protected final Map<Class<?>, Class<?>> sync2Async;
|
||||||
|
|
||||||
|
public MappedHttpInvocationModule() {
|
||||||
|
this(ImmutableMap.<Class<?>, Class<?>> of());
|
||||||
|
}
|
||||||
|
|
||||||
|
public MappedHttpInvocationModule(Map<Class<?>, Class<?>> sync2Async) {
|
||||||
|
this.sync2Async = sync2Async;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configure() {
|
||||||
|
bind(new TypeLiteral<Map<Class<?>, Class<?>>>() {
|
||||||
|
}).toInstance(sync2Async);
|
||||||
|
bind(new TypeLiteral<Function<Invocation, Object>>() {
|
||||||
|
}).to(InvokeMappedHttpMethod.class);
|
||||||
|
org.jclouds.rest.config.BinderUtils.bindMappedHttpApi(binder(), HttpClient.class, HttpAsyncClient.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* seeds well-known invokables.
|
||||||
|
*/
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
protected Cache<Invokable<?, ?>, Invokable<?, ?>> seedKnownSync2AsyncInvokables() {
|
||||||
|
return seedKnownSync2AsyncInvokables(sync2Async);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* function view of above
|
||||||
|
*
|
||||||
|
* @see InvokeAndCallGetOnFutures
|
||||||
|
* @see InvokeMappedHttpMethod
|
||||||
|
*/
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
protected Function<Invocation, Invocation> sync2async(final Cache<Invokable<?, ?>, Invokable<?, ?>> cache) {
|
||||||
|
return new Function<Invocation, Invocation>() {
|
||||||
|
public Invocation apply(Invocation in) {
|
||||||
|
return Invocation.create(
|
||||||
|
checkNotNull(cache.getIfPresent(in.getInvokable()), "invokable %s not in %s", in.getInvokable(),
|
||||||
|
cache), in.getArgs());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
static Cache<Invokable<?, ?>, Invokable<?, ?>> seedKnownSync2AsyncInvokables(Map<Class<?>, Class<?>> sync2Async) {
|
||||||
|
Cache<Invokable<?, ?>, Invokable<?, ?>> sync2AsyncBuilder = CacheBuilder.newBuilder().build();
|
||||||
|
putInvokables(HttpClient.class, HttpAsyncClient.class, sync2AsyncBuilder);
|
||||||
|
for (Map.Entry<Class<?>, Class<?>> entry : sync2Async.entrySet()) {
|
||||||
|
putInvokables(entry.getKey(), entry.getValue(), sync2AsyncBuilder);
|
||||||
|
}
|
||||||
|
return sync2AsyncBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
|
// accessible for ClientProvider
|
||||||
|
public static void putInvokables(Class<?> sync, Class<?> async, Cache<Invokable<?, ?>, Invokable<?, ?>> cache) {
|
||||||
|
for (Invokable<?, ?> invoked : methods(sync)) {
|
||||||
|
Invokable<?, ?> delegatedMethod = method(async, invoked.getName(), getParameterTypes(invoked));
|
||||||
|
checkArgument(delegatedMethod.getExceptionTypes().equals(invoked.getExceptionTypes())
|
||||||
|
|| isCloseable(delegatedMethod), "invoked %s has different typed exceptions than target %s", invoked,
|
||||||
|
delegatedMethod);
|
||||||
|
cache.put(invoked, delegatedMethod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In JDK7 Closeable.close is declared in AutoCloseable, which throws
|
||||||
|
* Exception vs IOException, so we have to be more lenient about exception
|
||||||
|
* type declarations.
|
||||||
|
*
|
||||||
|
* <h4>note</h4>
|
||||||
|
*
|
||||||
|
* This will be refactored out when we delete Async code in jclouds 1.7.
|
||||||
|
*/
|
||||||
|
private static boolean isCloseable(Invokable<?, ?> delegatedMethod) {
|
||||||
|
return "close".equals(delegatedMethod.getName())
|
||||||
|
&& Closeable.class.isAssignableFrom(delegatedMethod.getDeclaringClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* for portability with {@link Class#getMethod(String, Class...)}
|
||||||
|
*/
|
||||||
|
private static Class<?>[] getParameterTypes(Invokable<?, ?> in) {
|
||||||
|
return toArray(transform(checkNotNull(in, "invokable").getParameters(), new Function<Parameter, Class<?>>() {
|
||||||
|
public Class<?> apply(Parameter input) {
|
||||||
|
return input.getType().getRawType();
|
||||||
|
}
|
||||||
|
}), Class.class);
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,10 +18,9 @@
|
||||||
*/
|
*/
|
||||||
package org.jclouds.rest.config;
|
package org.jclouds.rest.config;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkState;
|
import static org.jclouds.reflect.Types2.checkBound;
|
||||||
import static org.jclouds.rest.config.BinderUtils.bindHttpApi;
|
import static org.jclouds.rest.config.BinderUtils.bindMappedHttpApi;
|
||||||
|
|
||||||
import java.lang.reflect.TypeVariable;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.jclouds.rest.ConfiguresRestClient;
|
import org.jclouds.rest.ConfiguresRestClient;
|
||||||
|
@ -32,18 +31,23 @@ import com.google.common.reflect.TypeToken;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
|
*
|
||||||
|
* @deprecated will be removed in jclouds 1.7; use {@link HttpApiModule}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
@ConfiguresRestClient
|
@ConfiguresRestClient
|
||||||
public class RestClientModule<S, A> extends RestModule {
|
public class RestClientModule<S, A> extends RestModule {
|
||||||
protected final TypeToken<S> syncClientType;
|
protected final TypeToken<S> syncClientType;
|
||||||
protected final TypeToken<A> asyncClientType;
|
protected final TypeToken<A> asyncClientType;
|
||||||
|
|
||||||
|
private final MappedHttpInvocationModule invocationModule;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Note that this ctor requires that you instantiate w/resolved generic params. For example, via
|
* Note that this ctor requires that you instantiate w/resolved generic params. For example, via
|
||||||
* a subclass of a bound type, or natural instantiation w/resolved type params.
|
* a subclass of a bound type, or natural instantiation w/resolved type params.
|
||||||
*/
|
*/
|
||||||
protected RestClientModule(Map<Class<?>, Class<?>> sync2Async) {
|
protected RestClientModule(Map<Class<?>, Class<?>> sync2Async) {
|
||||||
super(sync2Async);
|
this.invocationModule = new MappedHttpInvocationModule(sync2Async);
|
||||||
this.syncClientType = checkBound(new TypeToken<S>(getClass()) {
|
this.syncClientType = checkBound(new TypeToken<S>(getClass()) {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
});
|
});
|
||||||
|
@ -52,15 +56,6 @@ public class RestClientModule<S, A> extends RestModule {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws IllegalStateException if the type is an instanceof {@link TypeVariable}
|
|
||||||
*/
|
|
||||||
private static <T> TypeToken<T> checkBound(TypeToken<T> type) throws IllegalStateException {
|
|
||||||
checkState(!(type.getType() instanceof TypeVariable<?>),
|
|
||||||
"unbound type variable: %s, use ctor that explicitly assigns this", type);
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see #RestClientModule(Map)
|
* @see #RestClientModule(Map)
|
||||||
*/
|
*/
|
||||||
|
@ -79,7 +74,7 @@ public class RestClientModule<S, A> extends RestModule {
|
||||||
* only necessary when type params are not resolvable at runtime.
|
* only necessary when type params are not resolvable at runtime.
|
||||||
*/
|
*/
|
||||||
public RestClientModule(TypeToken<S> syncClientType, TypeToken<A> asyncClientType, Map<Class<?>, Class<?>> sync2Async) {
|
public RestClientModule(TypeToken<S> syncClientType, TypeToken<A> asyncClientType, Map<Class<?>, Class<?>> sync2Async) {
|
||||||
super(sync2Async);
|
this.invocationModule = new MappedHttpInvocationModule(sync2Async);
|
||||||
this.syncClientType = checkBound(syncClientType);
|
this.syncClientType = checkBound(syncClientType);
|
||||||
this.asyncClientType = checkBound(asyncClientType);
|
this.asyncClientType = checkBound(asyncClientType);
|
||||||
}
|
}
|
||||||
|
@ -87,7 +82,8 @@ public class RestClientModule<S, A> extends RestModule {
|
||||||
@Override
|
@Override
|
||||||
protected void configure() {
|
protected void configure() {
|
||||||
super.configure();
|
super.configure();
|
||||||
bindHttpApi(binder(), syncClientType.getRawType(), asyncClientType.getRawType());
|
install(invocationModule);
|
||||||
|
bindMappedHttpApi(binder(), syncClientType.getRawType(), asyncClientType.getRawType());
|
||||||
bindErrorHandlers();
|
bindErrorHandlers();
|
||||||
bindRetryHandlers();
|
bindRetryHandlers();
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,23 +18,13 @@
|
||||||
*/
|
*/
|
||||||
package org.jclouds.rest.config;
|
package org.jclouds.rest.config;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkArgument;
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
|
||||||
import static com.google.common.collect.Iterables.toArray;
|
|
||||||
import static com.google.common.collect.Iterables.transform;
|
|
||||||
import static com.google.common.util.concurrent.Atomics.newReference;
|
import static com.google.common.util.concurrent.Atomics.newReference;
|
||||||
import static org.jclouds.reflect.Reflection2.method;
|
|
||||||
import static org.jclouds.reflect.Reflection2.methods;
|
|
||||||
import static org.jclouds.rest.config.BinderUtils.bindHttpApi;
|
|
||||||
|
|
||||||
import java.io.Closeable;
|
|
||||||
import java.net.Proxy;
|
import java.net.Proxy;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
|
|
||||||
import org.jclouds.fallbacks.MapHttp4xxCodesToExceptions;
|
import org.jclouds.fallbacks.MapHttp4xxCodesToExceptions;
|
||||||
import org.jclouds.functions.IdentityFunction;
|
import org.jclouds.functions.IdentityFunction;
|
||||||
import org.jclouds.http.HttpRequest;
|
import org.jclouds.http.HttpRequest;
|
||||||
|
@ -46,24 +36,14 @@ import org.jclouds.location.config.LocationModule;
|
||||||
import org.jclouds.proxy.ProxyForURI;
|
import org.jclouds.proxy.ProxyForURI;
|
||||||
import org.jclouds.reflect.Invocation;
|
import org.jclouds.reflect.Invocation;
|
||||||
import org.jclouds.rest.AuthorizationException;
|
import org.jclouds.rest.AuthorizationException;
|
||||||
import org.jclouds.rest.HttpAsyncClient;
|
|
||||||
import org.jclouds.rest.HttpClient;
|
|
||||||
import org.jclouds.rest.binders.BindToJsonPayloadWrappedWith;
|
import org.jclouds.rest.binders.BindToJsonPayloadWrappedWith;
|
||||||
import org.jclouds.rest.internal.InvokeHttpMethod;
|
|
||||||
import org.jclouds.rest.internal.RestAnnotationProcessor;
|
import org.jclouds.rest.internal.RestAnnotationProcessor;
|
||||||
import org.jclouds.rest.internal.TransformerForRequest;
|
import org.jclouds.rest.internal.TransformerForRequest;
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
|
||||||
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.Supplier;
|
import com.google.common.base.Supplier;
|
||||||
import com.google.common.cache.Cache;
|
|
||||||
import com.google.common.cache.CacheBuilder;
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
|
||||||
import com.google.common.reflect.Invokable;
|
|
||||||
import com.google.common.reflect.Parameter;
|
|
||||||
import com.google.inject.AbstractModule;
|
import com.google.inject.AbstractModule;
|
||||||
import com.google.inject.Provides;
|
|
||||||
import com.google.inject.TypeLiteral;
|
import com.google.inject.TypeLiteral;
|
||||||
import com.google.inject.assistedinject.FactoryModuleBuilder;
|
import com.google.inject.assistedinject.FactoryModuleBuilder;
|
||||||
|
|
||||||
|
@ -72,109 +52,25 @@ public class RestModule extends AbstractModule {
|
||||||
public static final TypeLiteral<Supplier<URI>> URI_SUPPLIER_TYPE = new TypeLiteral<Supplier<URI>>() {
|
public static final TypeLiteral<Supplier<URI>> URI_SUPPLIER_TYPE = new TypeLiteral<Supplier<URI>>() {
|
||||||
};
|
};
|
||||||
|
|
||||||
protected final Map<Class<?>, Class<?>> sync2Async;
|
|
||||||
protected final AtomicReference<AuthorizationException> authException = newReference();
|
protected final AtomicReference<AuthorizationException> authException = newReference();
|
||||||
|
|
||||||
public RestModule() {
|
|
||||||
this(ImmutableMap.<Class<?>, Class<?>> of());
|
|
||||||
}
|
|
||||||
|
|
||||||
public RestModule(Map<Class<?>, Class<?>> sync2Async) {
|
|
||||||
this.sync2Async = sync2Async;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* seeds well-known invokables.
|
|
||||||
*/
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
protected Cache<Invokable<?, ?>, Invokable<?, ?>> seedKnownSync2AsyncInvokables() {
|
|
||||||
return seedKnownSync2AsyncInvokables(sync2Async);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* function view of above
|
|
||||||
*/
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
protected Function<Invocation, Invocation> sync2async(final Cache<Invokable<?, ?>, Invokable<?, ?>> cache) {
|
|
||||||
return new Function<Invocation, Invocation>() {
|
|
||||||
public Invocation apply(Invocation in) {
|
|
||||||
return Invocation.create(
|
|
||||||
checkNotNull(cache.getIfPresent(in.getInvokable()), "invokable %s not in %s", in.getInvokable(),
|
|
||||||
cache), in.getArgs());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
static Cache<Invokable<?, ?>, Invokable<?, ?>> seedKnownSync2AsyncInvokables(Map<Class<?>, Class<?>> sync2Async) {
|
|
||||||
Cache<Invokable<?, ?>, Invokable<?, ?>> sync2AsyncBuilder = CacheBuilder.newBuilder().build();
|
|
||||||
putInvokables(HttpClient.class, HttpAsyncClient.class, sync2AsyncBuilder);
|
|
||||||
for (Map.Entry<Class<?>, Class<?>> entry : sync2Async.entrySet()) {
|
|
||||||
putInvokables(entry.getKey(), entry.getValue(), sync2AsyncBuilder);
|
|
||||||
}
|
|
||||||
return sync2AsyncBuilder;
|
|
||||||
}
|
|
||||||
|
|
||||||
// accessible for ClientProvider
|
|
||||||
public static void putInvokables(Class<?> sync, Class<?> async, Cache<Invokable<?, ?>, Invokable<?, ?>> cache) {
|
|
||||||
for (Invokable<?, ?> invoked : methods(sync)) {
|
|
||||||
Invokable<?, ?> delegatedMethod = method(async, invoked.getName(), getParameterTypes(invoked));
|
|
||||||
checkArgument(
|
|
||||||
delegatedMethod.getExceptionTypes().equals(invoked.getExceptionTypes()) || isCloseable(delegatedMethod),
|
|
||||||
"invoked %s has different typed exceptions than target %s", invoked, delegatedMethod);
|
|
||||||
cache.put(invoked, delegatedMethod);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* In JDK7 Closeable.close is declared in AutoCloseable, which throws
|
|
||||||
* Exception vs IOException, so we have to be more lenient about exception
|
|
||||||
* type declarations.
|
|
||||||
*
|
|
||||||
* <h4>note</h4>
|
|
||||||
*
|
|
||||||
* This will be refactored out when we delete Async code in jclouds 1.7.
|
|
||||||
*/
|
|
||||||
private static boolean isCloseable(Invokable<?, ?> delegatedMethod) {
|
|
||||||
return "close".equals(delegatedMethod.getName())
|
|
||||||
&& Closeable.class.isAssignableFrom(delegatedMethod.getDeclaringClass());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* for portability with {@link Class#getMethod(String, Class...)}
|
|
||||||
*/
|
|
||||||
private static Class<?>[] getParameterTypes(Invokable<?, ?> in) {
|
|
||||||
return toArray(transform(checkNotNull(in, "invokable").getParameters(), new Function<Parameter, Class<?>>() {
|
|
||||||
public Class<?> apply(Parameter input) {
|
|
||||||
return input.getType().getRawType();
|
|
||||||
}
|
|
||||||
}), Class.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void installLocations() {
|
protected void installLocations() {
|
||||||
install(new LocationModule());
|
install(new LocationModule());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void configure() {
|
protected void configure() {
|
||||||
bind(new TypeLiteral<Map<Class<?>, Class<?>>>() {
|
|
||||||
}).toInstance(sync2Async);
|
|
||||||
install(new SaxParserModule());
|
install(new SaxParserModule());
|
||||||
install(new GsonModule());
|
install(new GsonModule());
|
||||||
install(new SetCaller.Module());
|
install(new SetCaller.Module());
|
||||||
install(new FactoryModuleBuilder().build(BindToJsonPayloadWrappedWith.Factory.class));
|
install(new FactoryModuleBuilder().build(BindToJsonPayloadWrappedWith.Factory.class));
|
||||||
bind(new TypeLiteral<Function<HttpRequest, Function<HttpResponse, ?>>>() {
|
bind(new TypeLiteral<Function<HttpRequest, Function<HttpResponse, ?>>>() {
|
||||||
}).to(TransformerForRequest.class);
|
}).to(TransformerForRequest.class);
|
||||||
bind(new TypeLiteral<Function<Invocation, Object>>() {
|
|
||||||
}).to(InvokeHttpMethod.class);
|
|
||||||
bind(new TypeLiteral<org.jclouds.Fallback<Object>>() {
|
bind(new TypeLiteral<org.jclouds.Fallback<Object>>() {
|
||||||
}).to(MapHttp4xxCodesToExceptions.class);
|
}).to(MapHttp4xxCodesToExceptions.class);
|
||||||
bind(new TypeLiteral<Function<Invocation, HttpRequest>>() {
|
bind(new TypeLiteral<Function<Invocation, HttpRequest>>() {
|
||||||
}).to(RestAnnotationProcessor.class);
|
}).to(RestAnnotationProcessor.class);
|
||||||
bind(IdentityFunction.class).toInstance(IdentityFunction.INSTANCE);
|
bind(IdentityFunction.class).toInstance(IdentityFunction.INSTANCE);
|
||||||
bindHttpApi(binder(), HttpClient.class, HttpAsyncClient.class);
|
|
||||||
// this will help short circuit scenarios that can otherwise lock out users
|
// this will help short circuit scenarios that can otherwise lock out users
|
||||||
bind(new TypeLiteral<AtomicReference<AuthorizationException>>() {
|
bind(new TypeLiteral<AtomicReference<AuthorizationException>>() {
|
||||||
}).toInstance(authException);
|
}).toInstance(authException);
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
/**
|
||||||
|
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. jclouds licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.jclouds.rest.internal;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import org.jclouds.annotations.Name;
|
||||||
|
import org.jclouds.domain.Credentials;
|
||||||
|
import org.jclouds.internal.ContextImpl;
|
||||||
|
import org.jclouds.lifecycle.Closer;
|
||||||
|
import org.jclouds.location.Provider;
|
||||||
|
import org.jclouds.providers.ProviderMetadata;
|
||||||
|
import org.jclouds.rest.ApiContext;
|
||||||
|
import org.jclouds.rest.Utils;
|
||||||
|
|
||||||
|
import com.google.common.base.Supplier;
|
||||||
|
import com.google.inject.Injector;
|
||||||
|
import com.google.inject.Key;
|
||||||
|
import com.google.inject.Singleton;
|
||||||
|
import com.google.inject.TypeLiteral;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Adrian Cole
|
||||||
|
*/
|
||||||
|
@Singleton
|
||||||
|
public class ApiContextImpl<A> extends ContextImpl implements ApiContext<A> {
|
||||||
|
|
||||||
|
private final A api;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
protected ApiContextImpl(@Name String name, ProviderMetadata providerMetadata,
|
||||||
|
@Provider Supplier<Credentials> creds, Utils utils, Closer closer, Injector injector, TypeLiteral<A> api) {
|
||||||
|
super(name, providerMetadata, creds, utils, closer);
|
||||||
|
checkNotNull(injector, "injector");
|
||||||
|
this.api = injector.getInstance(Key.get(checkNotNull(api, "api")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public A getApi() {
|
||||||
|
return api;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,121 @@
|
||||||
|
/**
|
||||||
|
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. jclouds licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.jclouds.rest.internal;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
import static org.jclouds.reflect.Reflection2.typeToken;
|
||||||
|
import static org.jclouds.reflect.Types2.checkBound;
|
||||||
|
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
import org.jclouds.apis.ApiMetadata;
|
||||||
|
import org.jclouds.apis.internal.BaseApiMetadata;
|
||||||
|
import org.jclouds.rest.ApiContext;
|
||||||
|
import org.jclouds.rest.HttpApiMetadata;
|
||||||
|
|
||||||
|
import com.google.common.annotations.Beta;
|
||||||
|
import com.google.common.base.Objects.ToStringHelper;
|
||||||
|
import com.google.common.reflect.TypeParameter;
|
||||||
|
import com.google.common.reflect.TypeToken;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Useful in creating http apis.
|
||||||
|
*
|
||||||
|
* @author Adrian Cole
|
||||||
|
*/
|
||||||
|
@Beta
|
||||||
|
public abstract class BaseHttpApiMetadata<A> extends BaseApiMetadata implements HttpApiMetadata<A> {
|
||||||
|
|
||||||
|
protected final Class<A> api;
|
||||||
|
|
||||||
|
protected BaseHttpApiMetadata(Builder<A, ?> builder) {
|
||||||
|
super(builder);
|
||||||
|
this.api = checkNotNull(builder.api, "api");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Properties defaultProperties() {
|
||||||
|
Properties props = BaseApiMetadata.defaultProperties();
|
||||||
|
return props;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <S, A> TypeToken<ApiContext<A>> contextToken(TypeToken<A> apiToken) {
|
||||||
|
return new TypeToken<ApiContext<A>>() {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
}.where(new TypeParameter<A>() {
|
||||||
|
}, apiToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static abstract class Builder<A, T extends Builder<A, T>> extends BaseApiMetadata.Builder<T> implements
|
||||||
|
HttpApiMetadata.Builder<A, T> {
|
||||||
|
protected Class<A> api;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Note that this ctor requires that you instantiate w/resolved generic
|
||||||
|
* params. For example, via a subclass of a bound type, or natural
|
||||||
|
* instantiation w/resolved type params.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
protected Builder() {
|
||||||
|
this.api = Class.class.cast(checkBound(new TypeToken<A>(getClass()) {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
}).getRawType());
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Builder(Class<A> api) {
|
||||||
|
this.api = checkNotNull(api, "api");
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init() {
|
||||||
|
javaApi(api)
|
||||||
|
.name(api.getSimpleName())
|
||||||
|
.context(contextToken(typeToken(api)))
|
||||||
|
.defaultProperties(BaseHttpApiMetadata.defaultProperties());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T javaApi(Class<A> api) {
|
||||||
|
this.api = checkNotNull(api, "api");
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public T fromApiMetadata(ApiMetadata in) {
|
||||||
|
if (in instanceof HttpApiMetadata) {
|
||||||
|
HttpApiMetadata<?> http = HttpApiMetadata.class.cast(in);
|
||||||
|
javaApi(Class.class.cast(http.getApi()));
|
||||||
|
}
|
||||||
|
super.fromApiMetadata(in);
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<A> getApi() {
|
||||||
|
return api;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ToStringHelper string() {
|
||||||
|
return super.string().add("api", getApi());
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,7 +26,6 @@ import java.util.Properties;
|
||||||
import org.jclouds.apis.ApiMetadata;
|
import org.jclouds.apis.ApiMetadata;
|
||||||
import org.jclouds.apis.internal.BaseApiMetadata;
|
import org.jclouds.apis.internal.BaseApiMetadata;
|
||||||
import org.jclouds.rest.RestApiMetadata;
|
import org.jclouds.rest.RestApiMetadata;
|
||||||
import org.jclouds.rest.RestContext;
|
|
||||||
|
|
||||||
import com.google.common.annotations.Beta;
|
import com.google.common.annotations.Beta;
|
||||||
import com.google.common.base.Objects.ToStringHelper;
|
import com.google.common.base.Objects.ToStringHelper;
|
||||||
|
@ -37,6 +36,8 @@ import com.google.common.reflect.TypeToken;
|
||||||
* Useful in creating rest apis.
|
* Useful in creating rest apis.
|
||||||
*
|
*
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
|
* @deprecated please use {@link BaseHttpApiMetadata} as
|
||||||
|
* async interface will be removed in jclouds 1.7.
|
||||||
*/
|
*/
|
||||||
@Beta
|
@Beta
|
||||||
public abstract class BaseRestApiMetadata extends BaseApiMetadata implements RestApiMetadata {
|
public abstract class BaseRestApiMetadata extends BaseApiMetadata implements RestApiMetadata {
|
||||||
|
@ -55,8 +56,8 @@ public abstract class BaseRestApiMetadata extends BaseApiMetadata implements Res
|
||||||
return props;
|
return props;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <S, A> TypeToken<RestContext<S, A>> contextToken(TypeToken<S> apiToken, TypeToken<A> asyncApiToken) {
|
public static <S, A> TypeToken<org.jclouds.rest.RestContext<S, A>> contextToken(TypeToken<S> apiToken, TypeToken<A> asyncApiToken) {
|
||||||
return new TypeToken<RestContext<S, A>>() {
|
return new TypeToken<org.jclouds.rest.RestContext<S, A>>() {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
}.where(new TypeParameter<S>() {
|
}.where(new TypeParameter<S>() {
|
||||||
}, apiToken).where(new TypeParameter<A>() {
|
}, apiToken).where(new TypeParameter<A>() {
|
||||||
|
|
|
@ -39,7 +39,6 @@ import java.lang.reflect.Type;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Qualifier;
|
import javax.inject.Qualifier;
|
||||||
|
@ -78,7 +77,7 @@ import com.google.inject.util.Types;
|
||||||
* The function that implements this dynamic proxy
|
* The function that implements this dynamic proxy
|
||||||
*/
|
*/
|
||||||
@Beta
|
@Beta
|
||||||
public final class DelegatesToInvocationFunction<S, F extends Function<Invocation, Object>> implements
|
public class DelegatesToInvocationFunction<S, F extends Function<Invocation, Object>> implements
|
||||||
InvocationHandler {
|
InvocationHandler {
|
||||||
|
|
||||||
private static final Object[] NO_ARGS = {};
|
private static final Object[] NO_ARGS = {};
|
||||||
|
@ -161,20 +160,18 @@ public final class DelegatesToInvocationFunction<S, F extends Function<Invocatio
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Injector injector;
|
protected final Injector injector;
|
||||||
private final TypeToken<S> ownerType;
|
protected final TypeToken<S> ownerType;
|
||||||
private final SetCaller setCaller;
|
protected final SetCaller setCaller;
|
||||||
private final Map<Class<?>, Class<?>> syncToAsync;
|
protected final Function<InvocationSuccess, Optional<Object>> optionalConverter;
|
||||||
private final Function<InvocationSuccess, Optional<Object>> optionalConverter;
|
protected final F methodInvoker;
|
||||||
private final F methodInvoker;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
DelegatesToInvocationFunction(Injector injector, SetCaller setCaller, Map<Class<?>, Class<?>> syncToAsync,
|
DelegatesToInvocationFunction(Injector injector, SetCaller setCaller,
|
||||||
Class<S> ownerType, Function<InvocationSuccess, Optional<Object>> optionalConverter, F methodInvoker) {
|
Class<S> ownerType, Function<InvocationSuccess, Optional<Object>> optionalConverter, F methodInvoker) {
|
||||||
this.injector = checkNotNull(injector, "injector");
|
this.injector = checkNotNull(injector, "injector");
|
||||||
this.ownerType = typeToken(checkNotNull(ownerType, "ownerType"));
|
this.ownerType = typeToken(checkNotNull(ownerType, "ownerType"));
|
||||||
this.setCaller = checkNotNull(setCaller, "setCaller");
|
this.setCaller = checkNotNull(setCaller, "setCaller");
|
||||||
this.syncToAsync = checkNotNull(syncToAsync, "syncToAsync");
|
|
||||||
this.optionalConverter = checkNotNull(optionalConverter, "optionalConverter");
|
this.optionalConverter = checkNotNull(optionalConverter, "optionalConverter");
|
||||||
this.methodInvoker = checkNotNull(methodInvoker, "methodInvoker");
|
this.methodInvoker = checkNotNull(methodInvoker, "methodInvoker");
|
||||||
}
|
}
|
||||||
|
@ -200,20 +197,12 @@ public final class DelegatesToInvocationFunction<S, F extends Function<Invocatio
|
||||||
/**
|
/**
|
||||||
* attempts to guess the generic type params for the delegate's invocation function based on the supplied type
|
* attempts to guess the generic type params for the delegate's invocation function based on the supplied type
|
||||||
*/
|
*/
|
||||||
private Key<?> methodInvokerFor(Class<?> returnType) {
|
protected Key<?> methodInvokerFor(Class<?> returnType) {
|
||||||
switch (methodInvoker.getClass().getTypeParameters().length) {
|
switch (methodInvoker.getClass().getTypeParameters().length) {
|
||||||
case 0:
|
case 0:
|
||||||
return Key.get(methodInvoker.getClass());
|
return Key.get(methodInvoker.getClass());
|
||||||
case 1:
|
case 1:
|
||||||
return Key.get(Types.newParameterizedType(methodInvoker.getClass(), returnType));
|
return Key.get(Types.newParameterizedType(methodInvoker.getClass(), returnType));
|
||||||
case 2:
|
|
||||||
if (syncToAsync.containsValue(returnType))
|
|
||||||
return Key.get(Types.newParameterizedType(methodInvoker.getClass(), returnType, returnType));
|
|
||||||
return Key.get(Types.newParameterizedType(
|
|
||||||
methodInvoker.getClass(),
|
|
||||||
returnType,
|
|
||||||
checkNotNull(syncToAsync.get(returnType), "need async type of %s for %s", returnType,
|
|
||||||
methodInvoker.getClass())));
|
|
||||||
}
|
}
|
||||||
throw new IllegalArgumentException(returnType + " has too many type parameters");
|
throw new IllegalArgumentException(returnType + " has too many type parameters");
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
/**
|
||||||
|
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. jclouds licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.jclouds.rest.internal;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import org.jclouds.reflect.Invocation;
|
||||||
|
import org.jclouds.reflect.InvocationSuccess;
|
||||||
|
import org.jclouds.rest.config.SetCaller;
|
||||||
|
|
||||||
|
import com.google.common.annotations.Beta;
|
||||||
|
import com.google.common.base.Function;
|
||||||
|
import com.google.common.base.Optional;
|
||||||
|
import com.google.inject.Injector;
|
||||||
|
import com.google.inject.Key;
|
||||||
|
import com.google.inject.util.Types;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param <S>
|
||||||
|
* The enclosing type of the interface that a dynamic proxy like this
|
||||||
|
* implements
|
||||||
|
* @param <F>
|
||||||
|
* The function that implements this dynamic proxy
|
||||||
|
*
|
||||||
|
* @deprecated please use {@link DelegatesToInvocationFunction} as
|
||||||
|
* async interface will be removed in jclouds 1.7.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
@Beta
|
||||||
|
public final class DelegatesToPotentiallyMappedInvocationFunction<S, F extends Function<Invocation, Object>> extends
|
||||||
|
DelegatesToInvocationFunction<S, F> {
|
||||||
|
private final Map<Class<?>, Class<?>> syncToAsync;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
DelegatesToPotentiallyMappedInvocationFunction(Injector injector, SetCaller setCaller, Class<S> ownerType,
|
||||||
|
Function<InvocationSuccess, Optional<Object>> optionalConverter, F methodInvoker,
|
||||||
|
Map<Class<?>, Class<?>> syncToAsync) {
|
||||||
|
super(injector, setCaller, ownerType, optionalConverter, methodInvoker);
|
||||||
|
this.syncToAsync = checkNotNull(syncToAsync, "syncToAsync");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Key<?> methodInvokerFor(Class<?> returnType) {
|
||||||
|
if (methodInvoker.getClass().getTypeParameters().length == 2) {
|
||||||
|
if (syncToAsync.containsValue(returnType))
|
||||||
|
return Key.get(Types.newParameterizedType(methodInvoker.getClass(), returnType, returnType));
|
||||||
|
return Key.get(Types.newParameterizedType(
|
||||||
|
methodInvoker.getClass(),
|
||||||
|
returnType,
|
||||||
|
checkNotNull(syncToAsync.get(returnType), "need async type of %s for %s", returnType,
|
||||||
|
methodInvoker.getClass())));
|
||||||
|
}
|
||||||
|
return super.methodInvokerFor(returnType);
|
||||||
|
}
|
||||||
|
}
|
|
@ -37,7 +37,9 @@ import com.google.common.util.concurrent.ListenableFuture;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
|
* @deprecated will be removed in jclouds 1.7, as async interfaces are no longer supported.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public final class InvokeAndCallGetOnFutures<R> implements Function<Invocation, Object> {
|
public final class InvokeAndCallGetOnFutures<R> implements Function<Invocation, Object> {
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
|
|
|
@ -20,19 +20,14 @@ package org.jclouds.rest.internal;
|
||||||
|
|
||||||
import static com.google.common.base.Objects.equal;
|
import static com.google.common.base.Objects.equal;
|
||||||
import static com.google.common.base.Objects.toStringHelper;
|
import static com.google.common.base.Objects.toStringHelper;
|
||||||
import static com.google.common.base.Preconditions.checkState;
|
|
||||||
import static com.google.common.base.Throwables.propagate;
|
import static com.google.common.base.Throwables.propagate;
|
||||||
import static com.google.common.util.concurrent.Futures.transform;
|
|
||||||
import static com.google.common.util.concurrent.Futures.withFallback;
|
|
||||||
import static java.util.concurrent.TimeUnit.NANOSECONDS;
|
import static java.util.concurrent.TimeUnit.NANOSECONDS;
|
||||||
import static org.jclouds.Constants.PROPERTY_USER_THREADS;
|
|
||||||
|
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Named;
|
|
||||||
|
|
||||||
import org.jclouds.http.HttpCommand;
|
import org.jclouds.http.HttpCommand;
|
||||||
import org.jclouds.http.HttpCommandExecutorService;
|
import org.jclouds.http.HttpCommandExecutorService;
|
||||||
|
@ -47,9 +42,6 @@ import com.google.common.annotations.VisibleForTesting;
|
||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
import com.google.common.base.Objects;
|
import com.google.common.base.Objects;
|
||||||
import com.google.common.base.Optional;
|
import com.google.common.base.Optional;
|
||||||
import com.google.common.reflect.Invokable;
|
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
|
||||||
import com.google.common.util.concurrent.ListeningExecutorService;
|
|
||||||
import com.google.common.util.concurrent.TimeLimiter;
|
import com.google.common.util.concurrent.TimeLimiter;
|
||||||
import com.google.common.util.concurrent.UncheckedTimeoutException;
|
import com.google.common.util.concurrent.UncheckedTimeoutException;
|
||||||
|
|
||||||
|
@ -61,24 +53,19 @@ public class InvokeHttpMethod implements Function<Invocation, Object> {
|
||||||
@Resource
|
@Resource
|
||||||
private Logger logger = Logger.NULL;
|
private Logger logger = Logger.NULL;
|
||||||
|
|
||||||
private final Function<Invocation, Invocation> sync2async;
|
|
||||||
private final Function<Invocation, HttpRequest> annotationProcessor;
|
private final Function<Invocation, HttpRequest> annotationProcessor;
|
||||||
private final HttpCommandExecutorService http;
|
private final HttpCommandExecutorService http;
|
||||||
private final ListeningExecutorService userExecutor;
|
|
||||||
private final TimeLimiter timeLimiter;
|
private final TimeLimiter timeLimiter;
|
||||||
private final Function<HttpRequest, Function<HttpResponse, ?>> transformerForRequest;
|
private final Function<HttpRequest, Function<HttpResponse, ?>> transformerForRequest;
|
||||||
private final InvocationConfig config;
|
private final InvocationConfig config;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
InvokeHttpMethod(Function<Invocation, Invocation> sync2async, Function<Invocation, HttpRequest> annotationProcessor,
|
InvokeHttpMethod(Function<Invocation, HttpRequest> annotationProcessor,
|
||||||
HttpCommandExecutorService http, Function<HttpRequest, Function<HttpResponse, ?>> transformerForRequest,
|
HttpCommandExecutorService http, Function<HttpRequest, Function<HttpResponse, ?>> transformerForRequest,
|
||||||
TimeLimiter timeLimiter, InvocationConfig config,
|
TimeLimiter timeLimiter, InvocationConfig config) {
|
||||||
@Named(PROPERTY_USER_THREADS) ListeningExecutorService userExecutor) {
|
|
||||||
this.sync2async = sync2async;
|
|
||||||
this.annotationProcessor = annotationProcessor;
|
this.annotationProcessor = annotationProcessor;
|
||||||
this.http = http;
|
this.http = http;
|
||||||
this.userExecutor = userExecutor;
|
|
||||||
this.timeLimiter = timeLimiter;
|
this.timeLimiter = timeLimiter;
|
||||||
this.transformerForRequest = transformerForRequest;
|
this.transformerForRequest = transformerForRequest;
|
||||||
this.config = config;
|
this.config = config;
|
||||||
|
@ -86,32 +73,11 @@ public class InvokeHttpMethod implements Function<Invocation, Object> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object apply(Invocation in) {
|
public Object apply(Invocation in) {
|
||||||
if (isFuture(in.getInvokable())) {
|
Optional<Long> timeoutNanos = config.getTimeoutNanos(in);
|
||||||
return submit(in);
|
|
||||||
}
|
|
||||||
Invocation async = toAsync(in);
|
|
||||||
Optional<Long> timeoutNanos = config.getTimeoutNanos(async);
|
|
||||||
if (timeoutNanos.isPresent()) {
|
if (timeoutNanos.isPresent()) {
|
||||||
return invokeWithTimeout(async, timeoutNanos.get());
|
return invokeWithTimeout(in, timeoutNanos.get());
|
||||||
}
|
}
|
||||||
return invoke(async);
|
return invoke(in);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* submits the {@linkplain HttpCommand} associated with {@code invocation},
|
|
||||||
* {@link #getTransformer(String, HttpCommand) parses its response}, and
|
|
||||||
* applies a {@link #getFallback(String, Invocation, HttpCommand) fallback}
|
|
||||||
* if a {@code Throwable} is encountered. Parsing and Fallback occur on the
|
|
||||||
* {@code userExecutor} thread.
|
|
||||||
*/
|
|
||||||
public ListenableFuture<?> submit(Invocation invocation) {
|
|
||||||
String commandName = config.getCommandName(invocation);
|
|
||||||
HttpCommand command = toCommand(commandName, invocation);
|
|
||||||
Function<HttpResponse, ?> transformer = getTransformer(commandName, command);
|
|
||||||
org.jclouds.Fallback<?> fallback = getFallback(commandName, invocation, command);
|
|
||||||
|
|
||||||
logger.debug(">> submitting %s", commandName);
|
|
||||||
return withFallback(transform(http.submit(command), transformer, userExecutor), fallback);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -224,17 +190,6 @@ public class InvokeHttpMethod implements Function<Invocation, Object> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* looks up the corresponding {@code Invocation} that returns a
|
|
||||||
* {@code Future}. Only Invokables that return {@code Futures} are annotated
|
|
||||||
* in a way that can be parsed into an {@linkplain HttpRequest}.
|
|
||||||
*/
|
|
||||||
private Invocation toAsync(Invocation in) {
|
|
||||||
Invocation async = sync2async.apply(in);
|
|
||||||
checkState(isFuture(async.getInvokable()), "not a future: %s", async);
|
|
||||||
return async;
|
|
||||||
}
|
|
||||||
|
|
||||||
private HttpCommand toCommand(String commandName, Invocation invocation) {
|
private HttpCommand toCommand(String commandName, Invocation invocation) {
|
||||||
logger.trace(">> converting %s", commandName);
|
logger.trace(">> converting %s", commandName);
|
||||||
HttpRequest request = annotationProcessor.apply(invocation);
|
HttpRequest request = annotationProcessor.apply(invocation);
|
||||||
|
@ -249,10 +204,6 @@ public class InvokeHttpMethod implements Function<Invocation, Object> {
|
||||||
return transformer;
|
return transformer;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isFuture(Invokable<?, ?> in) {
|
|
||||||
return in.getReturnType().getRawType().equals(ListenableFuture.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o)
|
if (this == o)
|
||||||
|
|
|
@ -0,0 +1,277 @@
|
||||||
|
/**
|
||||||
|
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. jclouds licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.jclouds.rest.internal;
|
||||||
|
|
||||||
|
import static com.google.common.base.Objects.equal;
|
||||||
|
import static com.google.common.base.Objects.toStringHelper;
|
||||||
|
import static com.google.common.base.Preconditions.checkState;
|
||||||
|
import static com.google.common.base.Throwables.propagate;
|
||||||
|
import static com.google.common.util.concurrent.Futures.transform;
|
||||||
|
import static com.google.common.util.concurrent.Futures.withFallback;
|
||||||
|
import static java.util.concurrent.TimeUnit.NANOSECONDS;
|
||||||
|
import static org.jclouds.Constants.PROPERTY_USER_THREADS;
|
||||||
|
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
|
||||||
|
import org.jclouds.http.HttpCommand;
|
||||||
|
import org.jclouds.http.HttpCommandExecutorService;
|
||||||
|
import org.jclouds.http.HttpRequest;
|
||||||
|
import org.jclouds.http.HttpResponse;
|
||||||
|
import org.jclouds.logging.Logger;
|
||||||
|
import org.jclouds.reflect.Invocation;
|
||||||
|
import org.jclouds.rest.InvocationContext;
|
||||||
|
import org.jclouds.rest.config.InvocationConfig;
|
||||||
|
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
import com.google.common.base.Function;
|
||||||
|
import com.google.common.base.Objects;
|
||||||
|
import com.google.common.base.Optional;
|
||||||
|
import com.google.common.reflect.Invokable;
|
||||||
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
|
import com.google.common.util.concurrent.ListeningExecutorService;
|
||||||
|
import com.google.common.util.concurrent.TimeLimiter;
|
||||||
|
import com.google.common.util.concurrent.UncheckedTimeoutException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Adrian Cole
|
||||||
|
* @deprecated will be replaced in jclouds 1.7 with {@link InvokeHttpMethod}, as async interfaces are no longer supported.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public class InvokeMappedHttpMethod implements Function<Invocation, Object> {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private Logger logger = Logger.NULL;
|
||||||
|
|
||||||
|
private final Function<Invocation, Invocation> sync2async;
|
||||||
|
private final Function<Invocation, HttpRequest> annotationProcessor;
|
||||||
|
private final HttpCommandExecutorService http;
|
||||||
|
private final ListeningExecutorService userExecutor;
|
||||||
|
private final TimeLimiter timeLimiter;
|
||||||
|
private final Function<HttpRequest, Function<HttpResponse, ?>> transformerForRequest;
|
||||||
|
private final InvocationConfig config;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
@VisibleForTesting
|
||||||
|
InvokeMappedHttpMethod(Function<Invocation, Invocation> sync2async, Function<Invocation, HttpRequest> annotationProcessor,
|
||||||
|
HttpCommandExecutorService http, Function<HttpRequest, Function<HttpResponse, ?>> transformerForRequest,
|
||||||
|
TimeLimiter timeLimiter, InvocationConfig config,
|
||||||
|
@Named(PROPERTY_USER_THREADS) ListeningExecutorService userExecutor) {
|
||||||
|
this.sync2async = sync2async;
|
||||||
|
this.annotationProcessor = annotationProcessor;
|
||||||
|
this.http = http;
|
||||||
|
this.userExecutor = userExecutor;
|
||||||
|
this.timeLimiter = timeLimiter;
|
||||||
|
this.transformerForRequest = transformerForRequest;
|
||||||
|
this.config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object apply(Invocation in) {
|
||||||
|
if (isFuture(in.getInvokable())) {
|
||||||
|
return submit(in);
|
||||||
|
}
|
||||||
|
Invocation async = toAsync(in);
|
||||||
|
Optional<Long> timeoutNanos = config.getTimeoutNanos(async);
|
||||||
|
if (timeoutNanos.isPresent()) {
|
||||||
|
return invokeWithTimeout(async, timeoutNanos.get());
|
||||||
|
}
|
||||||
|
return invoke(async);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* submits the {@linkplain HttpCommand} associated with {@code invocation},
|
||||||
|
* {@link #getTransformer(String, HttpCommand) parses its response}, and
|
||||||
|
* applies a {@link #getFallback(String, Invocation, HttpCommand) fallback}
|
||||||
|
* if a {@code Throwable} is encountered. Parsing and Fallback occur on the
|
||||||
|
* {@code userExecutor} thread.
|
||||||
|
*/
|
||||||
|
public ListenableFuture<?> submit(Invocation invocation) {
|
||||||
|
String commandName = config.getCommandName(invocation);
|
||||||
|
HttpCommand command = toCommand(commandName, invocation);
|
||||||
|
Function<HttpResponse, ?> transformer = getTransformer(commandName, command);
|
||||||
|
org.jclouds.Fallback<?> fallback = getFallback(commandName, invocation, command);
|
||||||
|
|
||||||
|
logger.debug(">> submitting %s", commandName);
|
||||||
|
return withFallback(transform(http.submit(command), transformer, userExecutor), fallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* invokes the {@linkplain HttpCommand} associated with {@code invocation},
|
||||||
|
* {@link #getTransformer(String, HttpCommand) parses its response}, and
|
||||||
|
* applies a {@link #getFallback(String, Invocation, HttpCommand) fallback}
|
||||||
|
* if a {@code Throwable} is encountered.
|
||||||
|
*/
|
||||||
|
public Object invoke(Invocation invocation) {
|
||||||
|
String commandName = config.getCommandName(invocation);
|
||||||
|
HttpCommand command = toCommand(commandName, invocation);
|
||||||
|
Function<HttpResponse, ?> transformer = getTransformer(commandName, command);
|
||||||
|
org.jclouds.Fallback<?> fallback = getFallback(commandName, invocation, command);
|
||||||
|
|
||||||
|
logger.debug(">> invoking %s", commandName);
|
||||||
|
try {
|
||||||
|
return transformer.apply(http.invoke(command));
|
||||||
|
} catch (Throwable t) {
|
||||||
|
try {
|
||||||
|
return fallback.createOrPropagate(t);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw propagate(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* calls {@link #invoke(Invocation)}, timing out after the specified time
|
||||||
|
* limit. If the target method call finished before the limit is reached, the
|
||||||
|
* return value or exception is propagated to the caller exactly as-is. If,
|
||||||
|
* on the other hand, the time limit is reached, we attempt to abort the call
|
||||||
|
* to the target, and throw an {@link UncheckedTimeoutException} to the
|
||||||
|
* caller.
|
||||||
|
*
|
||||||
|
* @param invocation
|
||||||
|
* the Invocation to invoke via {@link #invoke(Invocation)}
|
||||||
|
* @param limitNanos
|
||||||
|
* with timeoutUnit, the maximum length of time to wait in
|
||||||
|
* nanoseconds
|
||||||
|
* @throws InterruptedException
|
||||||
|
* if our thread is interrupted during execution
|
||||||
|
* @throws UncheckedTimeoutException
|
||||||
|
* if the time limit is reached
|
||||||
|
* @see TimeLimiter#callWithTimeout(Callable, long, TimeUnit, boolean)
|
||||||
|
*/
|
||||||
|
public Object invokeWithTimeout(final Invocation invocation, final long limitNanos) {
|
||||||
|
String commandName = config.getCommandName(invocation);
|
||||||
|
HttpCommand command = toCommand(commandName, invocation);
|
||||||
|
org.jclouds.Fallback<?> fallback = getFallback(commandName, invocation, command);
|
||||||
|
|
||||||
|
logger.debug(">> blocking on %s for %s", invocation, limitNanos);
|
||||||
|
try {
|
||||||
|
return timeLimiter
|
||||||
|
.callWithTimeout(new InvokeAndTransform(commandName, command), limitNanos, NANOSECONDS, true);
|
||||||
|
} catch (Throwable t) {
|
||||||
|
try {
|
||||||
|
return fallback.createOrPropagate(t);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw propagate(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private org.jclouds.Fallback<?> getFallback(String commandName, Invocation invocation, HttpCommand command) {
|
||||||
|
HttpRequest request = command.getCurrentRequest();
|
||||||
|
org.jclouds.Fallback<?> fallback = config.getFallback(invocation);
|
||||||
|
if (fallback instanceof InvocationContext)
|
||||||
|
InvocationContext.class.cast(fallback).setContext(request);
|
||||||
|
logger.trace("<< exceptions from %s are parsed by %s", commandName, fallback.getClass().getSimpleName());
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
final class InvokeAndTransform implements Callable<Object> {
|
||||||
|
private final String commandName;
|
||||||
|
private final HttpCommand command;
|
||||||
|
private final Function<HttpResponse, ?> transformer;
|
||||||
|
|
||||||
|
InvokeAndTransform(String commandName, HttpCommand command) {
|
||||||
|
this.commandName = commandName;
|
||||||
|
this.command = command;
|
||||||
|
this.transformer = getTransformer(commandName, command);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object call() throws Exception {
|
||||||
|
return transformer.apply(http.invoke(command));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hashCode(commandName, command, transformer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj)
|
||||||
|
return true;
|
||||||
|
if (obj == null || getClass() != obj.getClass())
|
||||||
|
return false;
|
||||||
|
InvokeAndTransform that = InvokeAndTransform.class.cast(obj);
|
||||||
|
return equal(this.commandName, that.commandName) && equal(this.command, that.command)
|
||||||
|
&& equal(this.transformer, that.transformer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return toStringHelper(this).add("commandName", commandName).add("command", command)
|
||||||
|
.add("transformer", transformer).toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* looks up the corresponding {@code Invocation} that returns a
|
||||||
|
* {@code Future}. Only Invokables that return {@code Futures} are annotated
|
||||||
|
* in a way that can be parsed into an {@linkplain HttpRequest}.
|
||||||
|
*/
|
||||||
|
private Invocation toAsync(Invocation in) {
|
||||||
|
Invocation async = sync2async.apply(in);
|
||||||
|
checkState(isFuture(async.getInvokable()), "not a future: %s", async);
|
||||||
|
return async;
|
||||||
|
}
|
||||||
|
|
||||||
|
private HttpCommand toCommand(String commandName, Invocation invocation) {
|
||||||
|
logger.trace(">> converting %s", commandName);
|
||||||
|
HttpRequest request = annotationProcessor.apply(invocation);
|
||||||
|
logger.trace("<< converted %s to %s", commandName, request.getRequestLine());
|
||||||
|
return new HttpCommand(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Function<HttpResponse, ?> getTransformer(String commandName, HttpCommand command) {
|
||||||
|
HttpRequest request = command.getCurrentRequest();
|
||||||
|
Function<HttpResponse, ?> transformer = transformerForRequest.apply(request);
|
||||||
|
logger.trace("<< response from %s is parsed by %s", commandName, transformer.getClass().getSimpleName());
|
||||||
|
return transformer;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isFuture(Invokable<?, ?> in) {
|
||||||
|
return in.getReturnType().getRawType().equals(ListenableFuture.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o)
|
||||||
|
return true;
|
||||||
|
if (o == null || getClass() != o.getClass())
|
||||||
|
return false;
|
||||||
|
InvokeMappedHttpMethod that = InvokeMappedHttpMethod.class.cast(o);
|
||||||
|
return equal(this.annotationProcessor, that.annotationProcessor);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hashCode(annotationProcessor);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return Objects.toStringHelper("").omitNullValues().add("annotationParser", annotationProcessor).toString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -29,7 +29,6 @@ import org.jclouds.date.DateService;
|
||||||
import org.jclouds.domain.Credentials;
|
import org.jclouds.domain.Credentials;
|
||||||
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.HttpClient;
|
import org.jclouds.rest.HttpClient;
|
||||||
import org.jclouds.rest.Utils;
|
import org.jclouds.rest.Utils;
|
||||||
import org.jclouds.xml.XMLParser;
|
import org.jclouds.xml.XMLParser;
|
||||||
|
@ -48,7 +47,7 @@ public class UtilsImpl implements Utils {
|
||||||
|
|
||||||
private final Json json;
|
private final Json json;
|
||||||
private final HttpClient simpleClient;
|
private final HttpClient simpleClient;
|
||||||
private final HttpAsyncClient simpleAsyncClient;
|
private final org.jclouds.rest.HttpAsyncClient simpleAsyncClient;
|
||||||
private final Crypto encryption;
|
private final Crypto encryption;
|
||||||
private final DateService date;
|
private final DateService date;
|
||||||
private final ListeningExecutorService userExecutor;
|
private final ListeningExecutorService userExecutor;
|
||||||
|
@ -60,7 +59,7 @@ public class UtilsImpl implements Utils {
|
||||||
private XMLParser xml;
|
private XMLParser xml;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
protected UtilsImpl(Injector injector, Json json, XMLParser xml, HttpClient simpleClient, HttpAsyncClient simpleAsyncClient,
|
protected UtilsImpl(Injector injector, Json json, XMLParser xml, HttpClient simpleClient, org.jclouds.rest.HttpAsyncClient simpleAsyncClient,
|
||||||
Crypto encryption, DateService date, @Named(Constants.PROPERTY_USER_THREADS) ListeningExecutorService userExecutor,
|
Crypto encryption, DateService date, @Named(Constants.PROPERTY_USER_THREADS) ListeningExecutorService userExecutor,
|
||||||
@Named(Constants.PROPERTY_IO_WORKER_THREADS) ListeningExecutorService ioExecutor, EventBus eventBus,
|
@Named(Constants.PROPERTY_IO_WORKER_THREADS) ListeningExecutorService ioExecutor, EventBus eventBus,
|
||||||
Map<String, Credentials> credentialStore, LoggerFactory loggerFactory) {
|
Map<String, Credentials> credentialStore, LoggerFactory loggerFactory) {
|
||||||
|
@ -79,7 +78,8 @@ public class UtilsImpl implements Utils {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HttpAsyncClient asyncHttp() {
|
@Deprecated
|
||||||
|
public org.jclouds.rest.HttpAsyncClient asyncHttp() {
|
||||||
return simpleAsyncClient;
|
return simpleAsyncClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,7 +104,8 @@ public class UtilsImpl implements Utils {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HttpAsyncClient getHttpAsyncClient() {
|
@Deprecated
|
||||||
|
public org.jclouds.rest.HttpAsyncClient getHttpAsyncClient() {
|
||||||
return simpleAsyncClient;
|
return simpleAsyncClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,123 @@
|
||||||
|
/**
|
||||||
|
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. jclouds licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.jclouds.config;
|
||||||
|
|
||||||
|
import static com.google.common.base.Suppliers.ofInstance;
|
||||||
|
import static org.easymock.EasyMock.createMock;
|
||||||
|
import static org.testng.Assert.assertEquals;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import org.jclouds.Context;
|
||||||
|
import org.jclouds.domain.Credentials;
|
||||||
|
import org.jclouds.http.IntegrationTestAsyncClient;
|
||||||
|
import org.jclouds.http.IntegrationTestClient;
|
||||||
|
import org.jclouds.providers.AnonymousProviderMetadata;
|
||||||
|
import org.jclouds.providers.ProviderMetadata;
|
||||||
|
import org.jclouds.providers.config.BindProviderMetadataContextAndCredentials;
|
||||||
|
import org.jclouds.rest.ApiContext;
|
||||||
|
import org.jclouds.rest.HttpApiMetadata;
|
||||||
|
import org.jclouds.rest.Utils;
|
||||||
|
import org.jclouds.rest.internal.BaseRestApiTest.MockModule;
|
||||||
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
|
import com.google.common.reflect.TypeToken;
|
||||||
|
import com.google.inject.AbstractModule;
|
||||||
|
import com.google.inject.Guice;
|
||||||
|
import com.google.inject.Injector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Adrian Cole
|
||||||
|
*/
|
||||||
|
@Test(groups = "unit", testName = "BindApiContextWithWildcardExtendsExplicitAndRawTypeTest")
|
||||||
|
public class BindApiContextWithWildcardExtendsExplicitAndRawTypeTest {
|
||||||
|
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
private static class ExpectedBindings {
|
||||||
|
|
||||||
|
private final ApiContext raw;
|
||||||
|
private final ApiContext<IntegrationTestClient> explicit;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public ExpectedBindings(ApiContext raw, ApiContext<IntegrationTestClient> explicit) {
|
||||||
|
this.raw = raw;
|
||||||
|
this.explicit = explicit;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRawAndExplicit() {
|
||||||
|
ProviderMetadata md = AnonymousProviderMetadata.forApiOnEndpoint(IntegrationTestClient.class, "http://localhost");
|
||||||
|
|
||||||
|
ExpectedBindings bindings = injectorFor(md).getInstance(ExpectedBindings.class);
|
||||||
|
assertEquals(bindings.raw, bindings.explicit);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Injector injectorFor(ProviderMetadata md) {
|
||||||
|
return Guice.createInjector(new BindNameToContext("test"), new BindProviderMetadataContextAndCredentials(md,
|
||||||
|
ofInstance(new Credentials("user", "pass"))), new BindApiContextWithWildcardExtendsExplicitAndRawType(
|
||||||
|
HttpApiMetadata.class.cast(md.getApiMetadata())),
|
||||||
|
|
||||||
|
// stuff needed for ApiContextImpl
|
||||||
|
new MockModule(), new AbstractModule() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configure() {
|
||||||
|
bind(Utils.class).toInstance(createMock(Utils.class));
|
||||||
|
bind(IntegrationTestClient.class).toInstance(createMock(IntegrationTestClient.class));
|
||||||
|
bind(IntegrationTestAsyncClient.class).toInstance(createMock(IntegrationTestAsyncClient.class));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
private static class ExpectedBindingsWithWildCardExtends {
|
||||||
|
|
||||||
|
private final ApiContext raw;
|
||||||
|
private final ApiContext<IntegrationTestClient> explicit;
|
||||||
|
private final ApiContext<? extends IntegrationTestClient> wildcardExtends;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public ExpectedBindingsWithWildCardExtends(ApiContext raw, ApiContext<IntegrationTestClient> explicit,
|
||||||
|
ApiContext<? extends IntegrationTestClient> wildcardExtends) {
|
||||||
|
this.raw = raw;
|
||||||
|
this.explicit = explicit;
|
||||||
|
this.wildcardExtends = wildcardExtends;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRawExplicitAndWildCardExtends() {
|
||||||
|
ProviderMetadata md = AnonymousProviderMetadata.forApiOnEndpoint(IntegrationTestClient.class, "http://localhost");
|
||||||
|
|
||||||
|
TypeToken<? extends Context> wildCardExtendsType = new TypeToken<ApiContext<? extends IntegrationTestClient>>() {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
};
|
||||||
|
|
||||||
|
md = md.toBuilder().apiMetadata(md.getApiMetadata().toBuilder().context(wildCardExtendsType).build()).build();
|
||||||
|
|
||||||
|
ExpectedBindingsWithWildCardExtends bindings = injectorFor(md).getInstance(
|
||||||
|
ExpectedBindingsWithWildCardExtends.class);
|
||||||
|
assertEquals(bindings.raw, bindings.explicit);
|
||||||
|
assertEquals(bindings.explicit, bindings.wildcardExtends);
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,12 +18,42 @@
|
||||||
*/
|
*/
|
||||||
package org.jclouds.http;
|
package org.jclouds.http;
|
||||||
|
|
||||||
import java.io.Closeable;
|
import static com.google.common.util.concurrent.Futures.immediateFuture;
|
||||||
|
|
||||||
|
import java.io.Closeable;
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
import javax.ws.rs.GET;
|
||||||
|
import javax.ws.rs.HEAD;
|
||||||
|
import javax.ws.rs.HeaderParam;
|
||||||
|
import javax.ws.rs.HttpMethod;
|
||||||
|
import javax.ws.rs.POST;
|
||||||
|
import javax.ws.rs.PUT;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.PathParam;
|
||||||
|
|
||||||
|
import org.jclouds.Fallbacks.FalseOnNotFoundOr404;
|
||||||
|
import org.jclouds.http.functions.ParseSax;
|
||||||
import org.jclouds.http.options.HttpRequestOptions;
|
import org.jclouds.http.options.HttpRequestOptions;
|
||||||
import org.jclouds.io.Payload;
|
import org.jclouds.io.Payload;
|
||||||
|
import org.jclouds.rest.annotations.BinderParam;
|
||||||
|
import org.jclouds.rest.annotations.Fallback;
|
||||||
|
import org.jclouds.rest.annotations.MapBinder;
|
||||||
|
import org.jclouds.rest.annotations.PayloadParam;
|
||||||
|
import org.jclouds.rest.annotations.RequestFilters;
|
||||||
|
import org.jclouds.rest.annotations.ResponseParser;
|
||||||
|
import org.jclouds.rest.annotations.XMLResponseParser;
|
||||||
|
import org.jclouds.rest.binders.BindToJsonPayload;
|
||||||
|
import org.jclouds.rest.binders.BindToStringPayload;
|
||||||
|
import org.jclouds.util.Strings2;
|
||||||
|
|
||||||
|
import com.google.common.base.Function;
|
||||||
import com.google.common.collect.Multimap;
|
import com.google.common.collect.Multimap;
|
||||||
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
import com.google.inject.Provides;
|
import com.google.inject.Provides;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -32,37 +62,144 @@ import com.google.inject.Provides;
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
*/
|
*/
|
||||||
public interface IntegrationTestClient extends Closeable {
|
public interface IntegrationTestClient extends Closeable {
|
||||||
String rowdy(String path);
|
@Target({ ElementType.METHOD })
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@HttpMethod("ROWDY")
|
||||||
|
public @interface ROWDY {
|
||||||
|
}
|
||||||
|
|
||||||
boolean exists(String path);
|
@ROWDY
|
||||||
|
@Path("/objects/{id}")
|
||||||
|
String rowdy(@PathParam("id") String path);
|
||||||
|
|
||||||
String synch(String id);
|
@HEAD
|
||||||
|
@Path("/objects/{id}")
|
||||||
|
@Fallback(FalseOnNotFoundOr404.class)
|
||||||
|
boolean exists(@PathParam("id") String path);
|
||||||
|
|
||||||
String download(String id);
|
@GET
|
||||||
|
@Path("/objects/{id}")
|
||||||
|
String download(@PathParam("id") String id);
|
||||||
|
|
||||||
HttpResponse invoke(HttpRequest request);
|
HttpResponse invoke(HttpRequest request);
|
||||||
|
|
||||||
String downloadException(String id, HttpRequestOptions options);
|
@GET
|
||||||
|
@Path("/{path}")
|
||||||
|
String synch(@PathParam("path") String id);
|
||||||
|
|
||||||
String synchException(String id, String header);
|
@GET
|
||||||
|
@Path("/objects/{id}")
|
||||||
|
@Fallback(FooOnException.class)
|
||||||
|
String downloadException(@PathParam("id") String id, HttpRequestOptions options);
|
||||||
|
|
||||||
String upload(String id, String toPut);
|
static class FooOnException implements org.jclouds.Fallback<String> {
|
||||||
|
public ListenableFuture<String> create(Throwable t) throws Exception {
|
||||||
|
return immediateFuture("foo");
|
||||||
|
}
|
||||||
|
|
||||||
String post(String id, String toPut);
|
public String createOrPropagate(Throwable t) throws Exception {
|
||||||
|
return "foo";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
String postAsInputStream(String id, String toPut);
|
@GET
|
||||||
|
@Path("/objects/{id}")
|
||||||
|
@Fallback(FooOnException.class)
|
||||||
|
String synchException(@PathParam("id") String id, @HeaderParam("Range") String header);
|
||||||
|
|
||||||
Multimap<String, String> postPayloadAndReturnHeaders(String id, Payload payload);
|
@PUT
|
||||||
|
@Path("/objects/{id}")
|
||||||
|
String upload(@PathParam("id") String id, @BinderParam(BindToStringPayload.class) String toPut);
|
||||||
|
|
||||||
String postJson(String id, String toPut);
|
@POST
|
||||||
|
@Path("/objects/{id}")
|
||||||
|
String post(@PathParam("id") String id, @BinderParam(BindToStringPayload.class) String toPut);
|
||||||
|
|
||||||
String downloadFilter(String id, String header);
|
@POST
|
||||||
|
@Path("/objects/{id}")
|
||||||
|
String postAsInputStream(@PathParam("id") String id,
|
||||||
|
@BinderParam(BindToInputStreamPayload.class) String toPut);
|
||||||
|
|
||||||
String download(String id, String header);
|
static class BindToInputStreamPayload extends BindToStringPayload {
|
||||||
|
@Override
|
||||||
|
public <R extends HttpRequest> R bindToRequest(R request, Object payload) {
|
||||||
|
request.setPayload(Strings2.toInputStream(payload.toString()));
|
||||||
|
request.getPayload().getContentMetadata().setContentLength((long) payload.toString().getBytes().length);
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
String downloadAndParse(String id);
|
@Singleton
|
||||||
|
static class ResponsePayload implements Function<HttpResponse, Multimap<String, String>> {
|
||||||
|
|
||||||
void putNothing(String id);
|
public Multimap<String, String> apply(HttpResponse from) {
|
||||||
|
return from.getHeaders();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("/objects/{id}")
|
||||||
|
@ResponseParser(ResponsePayload.class)
|
||||||
|
Multimap<String, String> postPayloadAndReturnHeaders(@PathParam("id") String id, Payload payload);
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("/objects/{id}")
|
||||||
|
@MapBinder(BindToJsonPayload.class)
|
||||||
|
String postJson(@PathParam("id") String id, @PayloadParam("key") String toPut);
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/objects/{id}")
|
||||||
|
@RequestFilters(Filter.class)
|
||||||
|
String downloadFilter(@PathParam("id") String id, @HeaderParam("filterme") String header);
|
||||||
|
|
||||||
|
static class Filter implements HttpRequestFilter {
|
||||||
|
public HttpRequest filter(HttpRequest request) throws HttpException {
|
||||||
|
if (request.getHeaders().containsKey("filterme")) {
|
||||||
|
request = request.toBuilder().replaceHeader("test", "test").build();
|
||||||
|
}
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/objects/{id}")
|
||||||
|
String download(@PathParam("id") String id, @HeaderParam("test") String header);
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/objects/{id}")
|
||||||
|
@XMLResponseParser(BarHandler.class)
|
||||||
|
String downloadAndParse(@PathParam("id") String id);
|
||||||
|
|
||||||
|
public static class BarHandler extends ParseSax.HandlerWithResult<String> {
|
||||||
|
|
||||||
|
private String bar = null;
|
||||||
|
private StringBuilder currentText = new StringBuilder();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void endElement(String uri, String name, String qName) {
|
||||||
|
if (qName.equals("bar")) {
|
||||||
|
bar = currentText.toString();
|
||||||
|
}
|
||||||
|
currentText = new StringBuilder();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void characters(char ch[], int start, int length) {
|
||||||
|
currentText.append(ch, start, length);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getResult() {
|
||||||
|
return bar;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@PUT
|
||||||
|
@Path("/objects/{id}")
|
||||||
|
void putNothing(@PathParam("id") String id);
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
StringBuilder newStringBuilder();
|
StringBuilder newStringBuilder();
|
||||||
|
|
|
@ -43,7 +43,7 @@ import com.google.common.util.concurrent.ListenableFuture;
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
*/
|
*/
|
||||||
@Test(groups = "unit")
|
@Test(groups = "unit")
|
||||||
public class RestModuleTest {
|
public class MappedHttpInvocationModuleTest {
|
||||||
static interface Sync {
|
static interface Sync {
|
||||||
String get();
|
String get();
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,7 @@ public class RestModuleTest {
|
||||||
|
|
||||||
public void testPutInvokablesWhenInterfacesMatch() {
|
public void testPutInvokablesWhenInterfacesMatch() {
|
||||||
Cache<Invokable<?, ?>, Invokable<?, ?>> cache = CacheBuilder.newBuilder().build();
|
Cache<Invokable<?, ?>, Invokable<?, ?>> cache = CacheBuilder.newBuilder().build();
|
||||||
RestModule.putInvokables(Sync.class, Async.class, cache);
|
MappedHttpInvocationModule.putInvokables(Sync.class, Async.class, cache);
|
||||||
|
|
||||||
assertEquals(cache.size(), 1);
|
assertEquals(cache.size(), 1);
|
||||||
|
|
||||||
|
@ -78,7 +78,7 @@ public class RestModuleTest {
|
||||||
@Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = ".* has different typed exceptions than target .*")
|
@Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = ".* has different typed exceptions than target .*")
|
||||||
public void testPutInvokablesWhenInterfacesMatchExceptExceptions() {
|
public void testPutInvokablesWhenInterfacesMatchExceptExceptions() {
|
||||||
Cache<Invokable<?, ?>, Invokable<?, ?>> cache = CacheBuilder.newBuilder().build();
|
Cache<Invokable<?, ?>, Invokable<?, ?>> cache = CacheBuilder.newBuilder().build();
|
||||||
RestModule.putInvokables(Sync.class, AsyncWithException.class, cache);
|
MappedHttpInvocationModule.putInvokables(Sync.class, AsyncWithException.class, cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static interface AsyncWithMisnamedMethod {
|
private static interface AsyncWithMisnamedMethod {
|
||||||
|
@ -88,7 +88,7 @@ public class RestModuleTest {
|
||||||
@Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "no such method .*")
|
@Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "no such method .*")
|
||||||
public void testPutInvokablesWhenTargetMethodNotFound() {
|
public void testPutInvokablesWhenTargetMethodNotFound() {
|
||||||
Cache<Invokable<?, ?>, Invokable<?, ?>> cache = CacheBuilder.newBuilder().build();
|
Cache<Invokable<?, ?>, Invokable<?, ?>> cache = CacheBuilder.newBuilder().build();
|
||||||
RestModule.putInvokables(Sync.class, AsyncWithMisnamedMethod.class, cache);
|
MappedHttpInvocationModule.putInvokables(Sync.class, AsyncWithMisnamedMethod.class, cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
static final Predicate<Entry<Invokable<?, ?>, Invokable<?, ?>>> isHttpInvokable = new Predicate<Map.Entry<Invokable<?, ?>, Invokable<?, ?>>>() {
|
static final Predicate<Entry<Invokable<?, ?>, Invokable<?, ?>>> isHttpInvokable = new Predicate<Map.Entry<Invokable<?, ?>, Invokable<?, ?>>>() {
|
||||||
|
@ -99,7 +99,7 @@ public class RestModuleTest {
|
||||||
};
|
};
|
||||||
|
|
||||||
public void testSeedKnownSync2AsyncIncludesHttpClientByDefault() {
|
public void testSeedKnownSync2AsyncIncludesHttpClientByDefault() {
|
||||||
Map<Invokable<?, ?>, Invokable<?, ?>> cache = RestModule.seedKnownSync2AsyncInvokables(
|
Map<Invokable<?, ?>, Invokable<?, ?>> cache = MappedHttpInvocationModule.seedKnownSync2AsyncInvokables(
|
||||||
ImmutableMap.<Class<?>, Class<?>> of()).asMap();
|
ImmutableMap.<Class<?>, Class<?>> of()).asMap();
|
||||||
|
|
||||||
assertEquals(cache.size(), 6);
|
assertEquals(cache.size(), 6);
|
||||||
|
@ -107,7 +107,7 @@ public class RestModuleTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testSeedKnownSync2AsyncInvokablesInterfacesMatch() {
|
public void testSeedKnownSync2AsyncInvokablesInterfacesMatch() {
|
||||||
Map<Invokable<?, ?>, Invokable<?, ?>> cache = RestModule.seedKnownSync2AsyncInvokables(
|
Map<Invokable<?, ?>, Invokable<?, ?>> cache = MappedHttpInvocationModule.seedKnownSync2AsyncInvokables(
|
||||||
ImmutableMap.<Class<?>, Class<?>> of(Sync.class, Async.class)).asMap();
|
ImmutableMap.<Class<?>, Class<?>> of(Sync.class, Async.class)).asMap();
|
||||||
|
|
||||||
assertEquals(cache.size(), 7);
|
assertEquals(cache.size(), 7);
|
|
@ -0,0 +1,52 @@
|
||||||
|
/**
|
||||||
|
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. jclouds licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.jclouds.rest.internal;
|
||||||
|
import static org.jclouds.reflect.Reflection2.typeToken;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.jclouds.View;
|
||||||
|
import org.jclouds.apis.ApiMetadata;
|
||||||
|
import org.jclouds.apis.Apis;
|
||||||
|
import org.jclouds.apis.internal.BaseApiMetadataTest;
|
||||||
|
import org.jclouds.rest.ApiContext;
|
||||||
|
import org.jclouds.rest.HttpApiMetadata;
|
||||||
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import com.google.common.reflect.TypeToken;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Adrian Cole
|
||||||
|
*/
|
||||||
|
@Test(groups = "unit")
|
||||||
|
public abstract class BaseHttpApiMetadataTest extends BaseApiMetadataTest {
|
||||||
|
|
||||||
|
public BaseHttpApiMetadataTest(HttpApiMetadata<?> toTest, Set<TypeToken<? extends View>> views) {
|
||||||
|
super(toTest, views);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testContextAssignableFromRestContext() {
|
||||||
|
Set<ApiMetadata> all = ImmutableSet.copyOf(Apis.contextAssignableFrom(typeToken(ApiContext.class)));
|
||||||
|
assert all.contains(toTest) : String.format("%s not found in %s", toTest, all);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -70,7 +70,7 @@ import org.jclouds.io.Payload;
|
||||||
import org.jclouds.io.Payloads;
|
import org.jclouds.io.Payloads;
|
||||||
import org.jclouds.logging.config.NullLoggingModule;
|
import org.jclouds.logging.config.NullLoggingModule;
|
||||||
import org.jclouds.providers.ProviderMetadata;
|
import org.jclouds.providers.ProviderMetadata;
|
||||||
import org.jclouds.rest.RestApiMetadata;
|
import org.jclouds.rest.HttpApiMetadata;
|
||||||
import org.jclouds.rest.config.CredentialStoreModule;
|
import org.jclouds.rest.config.CredentialStoreModule;
|
||||||
import org.jclouds.util.Strings2;
|
import org.jclouds.util.Strings2;
|
||||||
import org.w3c.dom.Node;
|
import org.w3c.dom.Node;
|
||||||
|
@ -557,11 +557,16 @@ public abstract class BaseRestApiExpectTest<S> {
|
||||||
ApiMetadata am = (pm != null) ? pm.getApiMetadata() : checkNotNull(createApiMetadata(),
|
ApiMetadata am = (pm != null) ? pm.getApiMetadata() : checkNotNull(createApiMetadata(),
|
||||||
"either createApiMetadata or createProviderMetadata must be overridden");
|
"either createApiMetadata or createProviderMetadata must be overridden");
|
||||||
|
|
||||||
builder = pm != null ? ContextBuilder.newBuilder(pm) : ContextBuilder.newBuilder(RestApiMetadata.class.cast(am));
|
builder = pm != null ? ContextBuilder.newBuilder(pm) : ContextBuilder.newBuilder(am);
|
||||||
|
}
|
||||||
|
ApiMetadata am = builder.getApiMetadata();
|
||||||
|
if (am instanceof HttpApiMetadata) {
|
||||||
|
this.api = HttpApiMetadata.class.cast(am).getApi();
|
||||||
|
} else if (am instanceof org.jclouds.rest.RestApiMetadata) {
|
||||||
|
this.api = org.jclouds.rest.RestApiMetadata.class.cast(am).getApi();
|
||||||
|
} else {
|
||||||
|
throw new UnsupportedOperationException("unsupported base type: " + am);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.api = RestApiMetadata.class.cast(builder.getApiMetadata()).getApi();
|
|
||||||
|
|
||||||
// isolate tests from eachother, as default credentialStore is static
|
// isolate tests from eachother, as default credentialStore is static
|
||||||
return builder.credentials(identity, credential).modules(
|
return builder.credentials(identity, credential).modules(
|
||||||
ImmutableSet.of(new ExpectModule(fn), new NullLoggingModule(), new CredentialStoreModule(new CopyInputStreamInputSupplierMap(
|
ImmutableSet.of(new ExpectModule(fn), new NullLoggingModule(), new CredentialStoreModule(new CopyInputStreamInputSupplierMap(
|
||||||
|
|
|
@ -18,9 +18,7 @@
|
||||||
*/
|
*/
|
||||||
package org.jclouds.rest.internal;
|
package org.jclouds.rest.internal;
|
||||||
|
|
||||||
import static org.easymock.EasyMock.anyObject;
|
|
||||||
import static org.easymock.EasyMock.createMock;
|
import static org.easymock.EasyMock.createMock;
|
||||||
import static org.easymock.EasyMock.eq;
|
|
||||||
import static org.easymock.EasyMock.expect;
|
import static org.easymock.EasyMock.expect;
|
||||||
import static org.easymock.EasyMock.replay;
|
import static org.easymock.EasyMock.replay;
|
||||||
import static org.easymock.EasyMock.verify;
|
import static org.easymock.EasyMock.verify;
|
||||||
|
@ -48,10 +46,7 @@ import com.google.common.base.Functions;
|
||||||
import com.google.common.base.Optional;
|
import com.google.common.base.Optional;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.common.util.concurrent.Futures;
|
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
import com.google.common.util.concurrent.ListeningExecutorService;
|
|
||||||
import com.google.common.util.concurrent.MoreExecutors;
|
|
||||||
import com.google.common.util.concurrent.TimeLimiter;
|
import com.google.common.util.concurrent.TimeLimiter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -62,17 +57,11 @@ import com.google.common.util.concurrent.TimeLimiter;
|
||||||
public class InvokeHttpMethodTest {
|
public class InvokeHttpMethodTest {
|
||||||
|
|
||||||
public static interface ThingApi {
|
public static interface ThingApi {
|
||||||
|
@Named("ns:get")
|
||||||
HttpResponse get();
|
HttpResponse get();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static interface ThingAsyncApi {
|
|
||||||
@Named("ns:get")
|
|
||||||
ListenableFuture<HttpResponse> get();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Invocation get;
|
private Invocation get;
|
||||||
private Invocation asyncGet;
|
|
||||||
private Function<Invocation, Invocation> sync2async;
|
|
||||||
private HttpRequest getRequest = HttpRequest.builder().method("GET").endpoint("http://get").build();
|
private HttpRequest getRequest = HttpRequest.builder().method("GET").endpoint("http://get").build();
|
||||||
private HttpCommand getCommand = new HttpCommand(getRequest);
|
private HttpCommand getCommand = new HttpCommand(getRequest);
|
||||||
private Function<Invocation, HttpRequest> toRequest;
|
private Function<Invocation, HttpRequest> toRequest;
|
||||||
|
@ -80,15 +69,12 @@ public class InvokeHttpMethodTest {
|
||||||
@BeforeClass
|
@BeforeClass
|
||||||
void setupInvocations() throws SecurityException, NoSuchMethodException {
|
void setupInvocations() throws SecurityException, NoSuchMethodException {
|
||||||
get = Invocation.create(method(ThingApi.class, "get"), ImmutableList.of());
|
get = Invocation.create(method(ThingApi.class, "get"), ImmutableList.of());
|
||||||
asyncGet = Invocation.create(method(ThingAsyncApi.class, "get"), ImmutableList.of());
|
toRequest = Functions.forMap(ImmutableMap.of(get, getRequest));
|
||||||
sync2async = Functions.forMap(ImmutableMap.of(get, asyncGet));
|
|
||||||
toRequest = Functions.forMap(ImmutableMap.of(asyncGet, getRequest));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private Function<HttpRequest, Function<HttpResponse, ?>> transformerForRequest = Function.class.cast(Functions
|
private Function<HttpRequest, Function<HttpResponse, ?>> transformerForRequest = Function.class.cast(Functions
|
||||||
.constant(Functions.identity()));
|
.constant(Functions.identity()));
|
||||||
private ListeningExecutorService userThreads = MoreExecutors.sameThreadExecutor();
|
|
||||||
|
|
||||||
private HttpResponse response = HttpResponse.builder().statusCode(200).payload("foo").build();
|
private HttpResponse response = HttpResponse.builder().statusCode(200).payload("foo").build();
|
||||||
private HttpCommandExecutorService http;
|
private HttpCommandExecutorService http;
|
||||||
|
@ -108,10 +94,9 @@ public class InvokeHttpMethodTest {
|
||||||
fallback = createMock(org.jclouds.Fallback.class);
|
fallback = createMock(org.jclouds.Fallback.class);
|
||||||
config = createMock(InvocationConfig.class);
|
config = createMock(InvocationConfig.class);
|
||||||
future = createMock(ListenableFuture.class);
|
future = createMock(ListenableFuture.class);
|
||||||
invokeHttpMethod = new InvokeHttpMethod(sync2async, toRequest, http, transformerForRequest, timeLimiter, config,
|
invokeHttpMethod = new InvokeHttpMethod(toRequest, http, transformerForRequest, timeLimiter, config);
|
||||||
userThreads);
|
expect(config.getCommandName(get)).andReturn("ns:get");
|
||||||
expect(config.getCommandName(asyncGet)).andReturn("ns:get");
|
expect(config.getFallback(get)).andReturn(fallback);
|
||||||
expect(config.getFallback(asyncGet)).andReturn(fallback);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterMethod
|
@AfterMethod
|
||||||
|
@ -120,7 +105,7 @@ public class InvokeHttpMethodTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testMethodWithTimeoutRunsTimeLimiter() throws Exception {
|
public void testMethodWithTimeoutRunsTimeLimiter() throws Exception {
|
||||||
expect(config.getTimeoutNanos(asyncGet)).andReturn(Optional.of(250000000l));
|
expect(config.getTimeoutNanos(get)).andReturn(Optional.of(250000000l));
|
||||||
InvokeAndTransform invoke = invokeHttpMethod.new InvokeAndTransform("ns:get", getCommand);
|
InvokeAndTransform invoke = invokeHttpMethod.new InvokeAndTransform("ns:get", getCommand);
|
||||||
expect(timeLimiter.callWithTimeout(invoke, 250000000, TimeUnit.NANOSECONDS, true)).andReturn(response);
|
expect(timeLimiter.callWithTimeout(invoke, 250000000, TimeUnit.NANOSECONDS, true)).andReturn(response);
|
||||||
replay(http, timeLimiter, fallback, config, future);
|
replay(http, timeLimiter, fallback, config, future);
|
||||||
|
@ -128,24 +113,17 @@ public class InvokeHttpMethodTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testMethodWithNoTimeoutCallGetDirectly() throws Exception {
|
public void testMethodWithNoTimeoutCallGetDirectly() throws Exception {
|
||||||
expect(config.getTimeoutNanos(asyncGet)).andReturn(Optional.<Long> absent());
|
expect(config.getTimeoutNanos(get)).andReturn(Optional.<Long> absent());
|
||||||
expect(http.invoke(new HttpCommand(getRequest))).andReturn(response);
|
expect(http.invoke(new HttpCommand(getRequest))).andReturn(response);
|
||||||
replay(http, timeLimiter, fallback, config, future);
|
replay(http, timeLimiter, fallback, config, future);
|
||||||
invokeHttpMethod.apply(get);
|
invokeHttpMethod.apply(get);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAsyncMethodSubmitsRequest() throws Exception {
|
|
||||||
expect(http.submit(new HttpCommand(getRequest))).andReturn(future);
|
|
||||||
future.addListener(anyObject(Runnable.class), eq(userThreads));
|
|
||||||
replay(http, timeLimiter, fallback, config, future);
|
|
||||||
invokeHttpMethod.apply(asyncGet);
|
|
||||||
}
|
|
||||||
|
|
||||||
private HttpResponse fallbackResponse = HttpResponse.builder().statusCode(200).payload("bar").build();
|
private HttpResponse fallbackResponse = HttpResponse.builder().statusCode(200).payload("bar").build();
|
||||||
|
|
||||||
public void testDirectCallRunsFallbackCreateOrPropagate() throws Exception {
|
public void testDirectCallRunsFallbackCreateOrPropagate() throws Exception {
|
||||||
IllegalStateException exception = new IllegalStateException();
|
IllegalStateException exception = new IllegalStateException();
|
||||||
expect(config.getTimeoutNanos(asyncGet)).andReturn(Optional.<Long> absent());
|
expect(config.getTimeoutNanos(get)).andReturn(Optional.<Long> absent());
|
||||||
expect(http.invoke(new HttpCommand(getRequest))).andThrow(exception);
|
expect(http.invoke(new HttpCommand(getRequest))).andThrow(exception);
|
||||||
expect(fallback.createOrPropagate(exception)).andReturn(fallbackResponse);
|
expect(fallback.createOrPropagate(exception)).andReturn(fallbackResponse);
|
||||||
replay(http, timeLimiter, fallback, config, future);
|
replay(http, timeLimiter, fallback, config, future);
|
||||||
|
@ -154,24 +132,11 @@ public class InvokeHttpMethodTest {
|
||||||
|
|
||||||
public void testTimeLimitedRunsFallbackCreateOrPropagate() throws Exception {
|
public void testTimeLimitedRunsFallbackCreateOrPropagate() throws Exception {
|
||||||
IllegalStateException exception = new IllegalStateException();
|
IllegalStateException exception = new IllegalStateException();
|
||||||
expect(config.getTimeoutNanos(asyncGet)).andReturn(Optional.of(250000000l));
|
expect(config.getTimeoutNanos(get)).andReturn(Optional.of(250000000l));
|
||||||
InvokeAndTransform invoke = invokeHttpMethod.new InvokeAndTransform("ns:get", getCommand);
|
InvokeAndTransform invoke = invokeHttpMethod.new InvokeAndTransform("ns:get", getCommand);
|
||||||
expect(timeLimiter.callWithTimeout(invoke, 250000000, TimeUnit.NANOSECONDS, true)).andThrow(exception);
|
expect(timeLimiter.callWithTimeout(invoke, 250000000, TimeUnit.NANOSECONDS, true)).andThrow(exception);
|
||||||
expect(fallback.createOrPropagate(exception)).andReturn(fallbackResponse);
|
expect(fallback.createOrPropagate(exception)).andReturn(fallbackResponse);
|
||||||
replay(http, timeLimiter, fallback, config, future);
|
replay(http, timeLimiter, fallback, config, future);
|
||||||
assertEquals(invokeHttpMethod.apply(get), fallbackResponse);
|
assertEquals(invokeHttpMethod.apply(get), fallbackResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public void testSubmitRunsFallbackCreateOnGet() throws Exception {
|
|
||||||
IllegalStateException exception = new IllegalStateException();
|
|
||||||
expect(http.submit(new HttpCommand(getRequest))).andReturn(
|
|
||||||
Futures.<HttpResponse> immediateFailedFuture(exception));
|
|
||||||
expect(fallback.create(exception)).andReturn(Futures.<HttpResponse> immediateFuture(fallbackResponse));
|
|
||||||
// not using the field, as you can see above we are making an immediate
|
|
||||||
// failed future instead.
|
|
||||||
future = createMock(ListenableFuture.class);
|
|
||||||
replay(http, timeLimiter, fallback, config, future);
|
|
||||||
assertEquals(ListenableFuture.class.cast(invokeHttpMethod.apply(asyncGet)).get(), fallbackResponse);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,177 @@
|
||||||
|
/**
|
||||||
|
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. jclouds licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.jclouds.rest.internal;
|
||||||
|
|
||||||
|
import static org.easymock.EasyMock.anyObject;
|
||||||
|
import static org.easymock.EasyMock.createMock;
|
||||||
|
import static org.easymock.EasyMock.eq;
|
||||||
|
import static org.easymock.EasyMock.expect;
|
||||||
|
import static org.easymock.EasyMock.replay;
|
||||||
|
import static org.easymock.EasyMock.verify;
|
||||||
|
import static org.jclouds.reflect.Reflection2.method;
|
||||||
|
import static org.testng.Assert.assertEquals;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import javax.inject.Named;
|
||||||
|
|
||||||
|
import org.jclouds.http.HttpCommand;
|
||||||
|
import org.jclouds.http.HttpCommandExecutorService;
|
||||||
|
import org.jclouds.http.HttpRequest;
|
||||||
|
import org.jclouds.http.HttpResponse;
|
||||||
|
import org.jclouds.reflect.Invocation;
|
||||||
|
import org.jclouds.rest.config.InvocationConfig;
|
||||||
|
import org.jclouds.rest.internal.InvokeMappedHttpMethod.InvokeAndTransform;
|
||||||
|
import org.testng.annotations.AfterMethod;
|
||||||
|
import org.testng.annotations.BeforeClass;
|
||||||
|
import org.testng.annotations.BeforeMethod;
|
||||||
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
|
import com.google.common.base.Function;
|
||||||
|
import com.google.common.base.Functions;
|
||||||
|
import com.google.common.base.Optional;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.google.common.util.concurrent.Futures;
|
||||||
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
|
import com.google.common.util.concurrent.ListeningExecutorService;
|
||||||
|
import com.google.common.util.concurrent.MoreExecutors;
|
||||||
|
import com.google.common.util.concurrent.TimeLimiter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Adrian Cole
|
||||||
|
*/
|
||||||
|
@Test(groups = "unit", singleThreaded = true)
|
||||||
|
public class InvokeMappedHttpMethodTest {
|
||||||
|
|
||||||
|
public static interface ThingApi {
|
||||||
|
HttpResponse get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static interface ThingAsyncApi {
|
||||||
|
@Named("ns:get")
|
||||||
|
ListenableFuture<HttpResponse> get();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Invocation get;
|
||||||
|
private Invocation asyncGet;
|
||||||
|
private Function<Invocation, Invocation> sync2async;
|
||||||
|
private HttpRequest getRequest = HttpRequest.builder().method("GET").endpoint("http://get").build();
|
||||||
|
private HttpCommand getCommand = new HttpCommand(getRequest);
|
||||||
|
private Function<Invocation, HttpRequest> toRequest;
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
void setupInvocations() throws SecurityException, NoSuchMethodException {
|
||||||
|
get = Invocation.create(method(ThingApi.class, "get"), ImmutableList.of());
|
||||||
|
asyncGet = Invocation.create(method(ThingAsyncApi.class, "get"), ImmutableList.of());
|
||||||
|
sync2async = Functions.forMap(ImmutableMap.of(get, asyncGet));
|
||||||
|
toRequest = Functions.forMap(ImmutableMap.of(asyncGet, getRequest));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private Function<HttpRequest, Function<HttpResponse, ?>> transformerForRequest = Function.class.cast(Functions
|
||||||
|
.constant(Functions.identity()));
|
||||||
|
private ListeningExecutorService userThreads = MoreExecutors.sameThreadExecutor();
|
||||||
|
|
||||||
|
private HttpResponse response = HttpResponse.builder().statusCode(200).payload("foo").build();
|
||||||
|
private HttpCommandExecutorService http;
|
||||||
|
private TimeLimiter timeLimiter;
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
private org.jclouds.Fallback fallback;
|
||||||
|
private InvocationConfig config;
|
||||||
|
private InvokeMappedHttpMethod invokeHttpMethod;
|
||||||
|
|
||||||
|
private ListenableFuture<HttpResponse> future;
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@BeforeMethod
|
||||||
|
void createMocks() {
|
||||||
|
http = createMock(HttpCommandExecutorService.class);
|
||||||
|
timeLimiter = createMock(TimeLimiter.class);
|
||||||
|
fallback = createMock(org.jclouds.Fallback.class);
|
||||||
|
config = createMock(InvocationConfig.class);
|
||||||
|
future = createMock(ListenableFuture.class);
|
||||||
|
invokeHttpMethod = new InvokeMappedHttpMethod(sync2async, toRequest, http, transformerForRequest, timeLimiter, config,
|
||||||
|
userThreads);
|
||||||
|
expect(config.getCommandName(asyncGet)).andReturn("ns:get");
|
||||||
|
expect(config.getFallback(asyncGet)).andReturn(fallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterMethod
|
||||||
|
void verifyMocks() {
|
||||||
|
verify(http, timeLimiter, fallback, config, future);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testMethodWithTimeoutRunsTimeLimiter() throws Exception {
|
||||||
|
expect(config.getTimeoutNanos(asyncGet)).andReturn(Optional.of(250000000l));
|
||||||
|
InvokeAndTransform invoke = invokeHttpMethod.new InvokeAndTransform("ns:get", getCommand);
|
||||||
|
expect(timeLimiter.callWithTimeout(invoke, 250000000, TimeUnit.NANOSECONDS, true)).andReturn(response);
|
||||||
|
replay(http, timeLimiter, fallback, config, future);
|
||||||
|
invokeHttpMethod.apply(get);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testMethodWithNoTimeoutCallGetDirectly() throws Exception {
|
||||||
|
expect(config.getTimeoutNanos(asyncGet)).andReturn(Optional.<Long> absent());
|
||||||
|
expect(http.invoke(new HttpCommand(getRequest))).andReturn(response);
|
||||||
|
replay(http, timeLimiter, fallback, config, future);
|
||||||
|
invokeHttpMethod.apply(get);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAsyncMethodSubmitsRequest() throws Exception {
|
||||||
|
expect(http.submit(new HttpCommand(getRequest))).andReturn(future);
|
||||||
|
future.addListener(anyObject(Runnable.class), eq(userThreads));
|
||||||
|
replay(http, timeLimiter, fallback, config, future);
|
||||||
|
invokeHttpMethod.apply(asyncGet);
|
||||||
|
}
|
||||||
|
|
||||||
|
private HttpResponse fallbackResponse = HttpResponse.builder().statusCode(200).payload("bar").build();
|
||||||
|
|
||||||
|
public void testDirectCallRunsFallbackCreateOrPropagate() throws Exception {
|
||||||
|
IllegalStateException exception = new IllegalStateException();
|
||||||
|
expect(config.getTimeoutNanos(asyncGet)).andReturn(Optional.<Long> absent());
|
||||||
|
expect(http.invoke(new HttpCommand(getRequest))).andThrow(exception);
|
||||||
|
expect(fallback.createOrPropagate(exception)).andReturn(fallbackResponse);
|
||||||
|
replay(http, timeLimiter, fallback, config, future);
|
||||||
|
assertEquals(invokeHttpMethod.apply(get), fallbackResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testTimeLimitedRunsFallbackCreateOrPropagate() throws Exception {
|
||||||
|
IllegalStateException exception = new IllegalStateException();
|
||||||
|
expect(config.getTimeoutNanos(asyncGet)).andReturn(Optional.of(250000000l));
|
||||||
|
InvokeAndTransform invoke = invokeHttpMethod.new InvokeAndTransform("ns:get", getCommand);
|
||||||
|
expect(timeLimiter.callWithTimeout(invoke, 250000000, TimeUnit.NANOSECONDS, true)).andThrow(exception);
|
||||||
|
expect(fallback.createOrPropagate(exception)).andReturn(fallbackResponse);
|
||||||
|
replay(http, timeLimiter, fallback, config, future);
|
||||||
|
assertEquals(invokeHttpMethod.apply(get), fallbackResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void testSubmitRunsFallbackCreateOnGet() throws Exception {
|
||||||
|
IllegalStateException exception = new IllegalStateException();
|
||||||
|
expect(http.submit(new HttpCommand(getRequest))).andReturn(
|
||||||
|
Futures.<HttpResponse> immediateFailedFuture(exception));
|
||||||
|
expect(fallback.create(exception)).andReturn(Futures.<HttpResponse> immediateFuture(fallbackResponse));
|
||||||
|
// not using the field, as you can see above we are making an immediate
|
||||||
|
// failed future instead.
|
||||||
|
future = createMock(ListenableFuture.class);
|
||||||
|
replay(http, timeLimiter, fallback, config, future);
|
||||||
|
assertEquals(ListenableFuture.class.cast(invokeHttpMethod.apply(asyncGet)).get(), fallbackResponse);
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,7 +19,7 @@
|
||||||
package org.jclouds.dynect.v3.config;
|
package org.jclouds.dynect.v3.config;
|
||||||
|
|
||||||
import static org.jclouds.http.HttpUtils.closeClientButKeepContentStream;
|
import static org.jclouds.http.HttpUtils.closeClientButKeepContentStream;
|
||||||
import static org.jclouds.rest.config.BinderUtils.bindHttpApi;
|
import static org.jclouds.rest.config.BinderUtils.bindMappedHttpApi;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.HttpURLConnection;
|
import java.net.HttpURLConnection;
|
||||||
|
@ -106,7 +106,7 @@ public class DynECTRestClientModule extends RestClientModule<DynECTApi, DynECTAs
|
||||||
bind(RedirectionRetryHandler.class).to(GetJobRedirectionRetryHandler.class);
|
bind(RedirectionRetryHandler.class).to(GetJobRedirectionRetryHandler.class);
|
||||||
super.configure();
|
super.configure();
|
||||||
// Bind apis that are used directly vs via DynECTApi
|
// Bind apis that are used directly vs via DynECTApi
|
||||||
bindHttpApi(binder(), SessionApi.class, SessionAsyncApi.class);
|
bindMappedHttpApi(binder(), SessionApi.class, SessionAsyncApi.class);
|
||||||
|
|
||||||
// dynect returns the following as a 200.
|
// dynect returns the following as a 200.
|
||||||
// {"status": "failure", "data": {}, "job_id": 274509427, "msgs":
|
// {"status": "failure", "data": {}, "job_id": 274509427, "msgs":
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
*/
|
*/
|
||||||
package org.jclouds.hpcloud.objectstorage;
|
package org.jclouds.hpcloud.objectstorage;
|
||||||
|
|
||||||
import static org.jclouds.rest.config.BinderUtils.bindHttpApi;
|
import static org.jclouds.rest.config.BinderUtils.bindMappedHttpApi;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
@ -112,7 +112,7 @@ public class HPCloudObjectStorageApiMetadata extends SwiftKeystoneApiMetadata {
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
protected void bindTemporaryUrlKeyApi() {
|
protected void bindTemporaryUrlKeyApi() {
|
||||||
bindHttpApi(binder(), TemporaryUrlKeyApi.class, KeystoneTemporaryUrlKeyAsyncApi.class);
|
bindMappedHttpApi(binder(), TemporaryUrlKeyApi.class, KeystoneTemporaryUrlKeyAsyncApi.class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue