Deprecated remaining code related to RestContext, RestApiMetadata, and RestClientModule for ApiContext, HttpApiMetadata, HttpApiModule

This commit is contained in:
adriancole 2013-04-09 14:01:44 -07:00
parent b4819b6c17
commit 61067d3683
53 changed files with 1927 additions and 372 deletions

View File

@ -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();

View File

@ -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());

View File

@ -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);
} }
/** /**

View File

@ -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);

View File

@ -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> {

View File

@ -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);
} }
} }

View File

@ -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() {

View File

@ -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);

View File

@ -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);
} }

View File

@ -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)

View File

@ -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

View File

@ -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) {

View File

@ -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());
} }
} }

View File

@ -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()))));
}
}

View File

@ -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;

View File

@ -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());

View File

@ -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;
}
}

View File

@ -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;
}
}
}

View File

@ -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 {

View File

@ -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();
}

View File

@ -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 {
} }

View File

@ -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();
}

View File

@ -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

View File

@ -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);
} }

View File

@ -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 {

View File

@ -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();

View File

@ -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);
} }
} }

View File

@ -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);
}
}

View File

@ -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>>() {

View File

@ -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")

View File

@ -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() {
}
}

View File

@ -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")

View File

@ -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);
}
}

View File

@ -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();
} }

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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());
}
}

View File

@ -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>() {

View File

@ -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");
} }

View File

@ -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);
}
}

View File

@ -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

View File

@ -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)

View File

@ -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();
}
}

View File

@ -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;
} }

View File

@ -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);
}
}

View File

@ -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();

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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(

View File

@ -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);
}
} }

View File

@ -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);
}
}

View File

@ -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":

View File

@ -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);
} }
} }
} }