Merge pull request #1121 from jclouds/no-static

removed use of static fields to share type mapping info
This commit is contained in:
Adrian Cole 2012-12-30 18:36:57 -08:00
commit e4a8b84da6
21 changed files with 170 additions and 263 deletions

View File

@ -131,7 +131,6 @@ import org.jclouds.location.suppliers.ImplicitLocationSupplier;
import org.jclouds.location.suppliers.implicit.OnlyLocationOrFirstZone;
import org.jclouds.rest.ConfiguresRestClient;
import org.jclouds.rest.RestContext;
import org.jclouds.rest.config.BinderUtils;
import org.jclouds.rest.config.RestClientModule;
import org.jclouds.rest.internal.RestContextImpl;
@ -199,26 +198,6 @@ public class CloudStackRestClientModule extends RestClientModule<CloudStackClien
.put(SessionClient.class, SessionAsyncClient.class)//
.build();
@Override
protected void bindAsyncClient() {
// bind the user client (default)
super.bindAsyncClient();
// bind the domain admin client
BinderUtils.bindAsyncClient(binder(), CloudStackDomainAsyncClient.class);
// bind the global admin client
BinderUtils.bindAsyncClient(binder(), CloudStackGlobalAsyncClient.class);
}
@Override
protected void bindClient() {
// bind the user client (default)
super.bindClient();
// bind the domain admin client
BinderUtils.bindClient(binder(), CloudStackDomainClient.class, CloudStackDomainAsyncClient.class, DELEGATE_MAP);
// bind the domain admin client
BinderUtils.bindClient(binder(), CloudStackGlobalClient.class, CloudStackGlobalAsyncClient.class, DELEGATE_MAP);
}
public CloudStackRestClientModule() {
super(DELEGATE_MAP);
}
@ -232,9 +211,10 @@ public class CloudStackRestClientModule extends RestClientModule<CloudStackClien
}).to(new TypeLiteral<RestContextImpl<CloudStackGlobalClient, CloudStackGlobalAsyncClient>>() {
});
bind(CredentialType.class).toProvider(CredentialTypeFromPropertyOrDefault.class);
// session client is used directly for filters and retry handlers, so let's bind it explicitly
bindClientAndAsyncClient(binder(), SessionClient.class, SessionAsyncClient.class);
bindClientAndAsyncClient(binder(), CloudStackDomainClient.class, CloudStackDomainAsyncClient.class);
bindClientAndAsyncClient(binder(), CloudStackGlobalClient.class, CloudStackGlobalAsyncClient.class);
bind(HttpRetryHandler.class).annotatedWith(ClientError.class).to(InvalidateSessionAndRetryOn401AndLogoutOnClose.class);
super.configure();

View File

@ -18,6 +18,8 @@
*/
package org.jclouds.filesystem.config;
import static org.jclouds.rest.config.BinderUtils.bindClient;
import org.jclouds.blobstore.AsyncBlobStore;
import org.jclouds.blobstore.BlobRequestSigner;
import org.jclouds.blobstore.BlobStore;
@ -35,9 +37,7 @@ import org.jclouds.filesystem.predicates.validators.internal.FilesystemBlobKeyVa
import org.jclouds.filesystem.predicates.validators.internal.FilesystemContainerNameValidatorImpl;
import org.jclouds.filesystem.strategy.internal.FilesystemStorageStrategyImpl;
import org.jclouds.filesystem.util.internal.FileSystemBlobUtilsImpl;
import org.jclouds.rest.config.BinderUtils;
import com.google.common.collect.ImmutableMap;
import com.google.inject.AbstractModule;
/**
@ -50,7 +50,7 @@ public class FilesystemBlobStoreContextModule extends AbstractModule {
protected void configure() {
bind(AsyncBlobStore.class).to(LocalAsyncBlobStore.class).asEagerSingleton();
// forward all requests from TransientBlobStore to TransientAsyncBlobStore. needs above binding as cannot proxy a class
BinderUtils.bindClient(binder(), LocalBlobStore.class, AsyncBlobStore.class, ImmutableMap.<Class<?>, Class<?>>of());
bindClient(binder(), LocalBlobStore.class, AsyncBlobStore.class);
bind(BlobStore.class).to(LocalBlobStore.class);
install(new BlobStoreObjectModule());

View File

@ -144,8 +144,7 @@ public class KeystoneAuthenticationModule extends AbstractModule {
}
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
bindClientAndAsyncClient(binder(), AuthenticationApi.class, AuthenticationAsyncApi.class);
}

View File

@ -47,10 +47,9 @@ public class CloudIdentityAuthenticationModule extends KeystoneAuthenticationMod
@Override
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
bindClientAndAsyncClient(binder(), CloudIdentityAuthenticationApi.class,
CloudIdentityAuthenticationAsyncApi.class);
CloudIdentityAuthenticationAsyncApi.class);
bind(AuthenticationApi.class).to(CloudIdentityAuthenticationApi.class).in(Scopes.SINGLETON);
bind(AuthenticationAsyncApi.class).to(CloudIdentityAuthenticationAsyncApi.class).in(Scopes.SINGLETON);
}

View File

@ -18,6 +18,8 @@
*/
package org.jclouds.blobstore.config;
import static org.jclouds.rest.config.BinderUtils.bindClient;
import org.jclouds.blobstore.AsyncBlobStore;
import org.jclouds.blobstore.BlobRequestSigner;
import org.jclouds.blobstore.BlobStore;
@ -26,9 +28,7 @@ import org.jclouds.blobstore.LocalBlobRequestSigner;
import org.jclouds.blobstore.LocalStorageStrategy;
import org.jclouds.blobstore.TransientStorageStrategy;
import org.jclouds.blobstore.attr.ConsistencyModel;
import org.jclouds.rest.config.BinderUtils;
import com.google.common.collect.ImmutableMap;
import com.google.inject.AbstractModule;
/**
@ -41,7 +41,7 @@ public class TransientBlobStoreContextModule extends AbstractModule {
protected void configure() {
bind(AsyncBlobStore.class).to(LocalAsyncBlobStore.class).asEagerSingleton();
// forward all requests from TransientBlobStore to TransientAsyncBlobStore. needs above binding as cannot proxy a class
BinderUtils.bindClient(binder(), LocalBlobStore.class, AsyncBlobStore.class, ImmutableMap.<Class<?>, Class<?>>of());
bindClient(binder(), LocalBlobStore.class, AsyncBlobStore.class);
install(new BlobStoreObjectModule());
install(new BlobStoreMapModule());
bind(BlobStore.class).to(LocalBlobStore.class);

View File

@ -63,8 +63,7 @@ public class OpenStackAuthenticationModule extends AbstractModule {
protected void configure() {
bind(new TypeLiteral<Function<Credentials, AuthenticationResponse>>() {
}).to(GetAuthenticationResponse.class);
// 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
bindClientAndAsyncClient(binder(), OpenStackAuthClient.class, OpenStackAuthAsyncClient.class);
install(new FactoryModuleBuilder().build(URIFromAuthenticationResponseForService.Factory.class));
bind(HttpRetryHandler.class).annotatedWith(ClientError.class).to(RetryOnRenew.class);

View File

@ -63,8 +63,7 @@ public class AuthenticationServiceModule extends AbstractModule {
protected void configure() {
bind(new TypeLiteral<Function<Credentials, Auth>>() {
}).to(GetAuth.class);
// 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
bindClientAndAsyncClient(binder(), AuthenticationClient.class, AuthenticationAsyncClient.class);
install(new FactoryModuleBuilder().implement(RegionIdToURISupplier.class,
RegionIdToURIFromAuthForServiceSupplier.class).build(RegionIdToURISupplier.Factory.class));

View File

@ -20,7 +20,6 @@ package org.jclouds.concurrent.internal;
import static com.google.common.base.Optional.fromNullable;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.reflect.Reflection.newProxy;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@ -30,15 +29,18 @@ import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import javax.annotation.Resource;
import javax.inject.Inject;
import javax.inject.Named;
import org.jclouds.internal.ClassMethodArgs;
import org.jclouds.internal.ClassMethodArgsAndReturnVal;
import org.jclouds.logging.Logger;
import org.jclouds.rest.annotations.Delegate;
import org.jclouds.util.Optionals2;
import org.jclouds.util.Throwables2;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Throwables;
@ -48,21 +50,30 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.reflect.AbstractInvocationHandler;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.inject.ProvisionException;
import com.google.inject.assistedinject.Assisted;
/**
* Generates RESTful clients from appropriately annotated interfaces.
*
* @author Adrian Cole
*/
public class SyncProxy extends AbstractInvocationHandler {
public final class SyncProxy extends AbstractInvocationHandler {
public static <T> T proxy(Function<ClassMethodArgsAndReturnVal, Optional<Object>> optionalConverter, Class<T> clazz, Object async,
@Named("sync") LoadingCache<ClassMethodArgs, Object> delegateMap,
Map<Class<?>, Class<?>> sync2Async, Map<String, Long> timeouts) throws IllegalArgumentException, SecurityException,
NoSuchMethodException {
return newProxy(clazz, new SyncProxy(optionalConverter, clazz, async, delegateMap, sync2Async, timeouts));
public static interface Factory {
/**
* @param declaring
* type of the interface where all methods match those of {@code async} except the return values are
* dereferenced
* @param async
* object whose interface matched {@code declaring} except all methods return {@link ListenableFuture}
* @return blocking invocation handler
*/
SyncProxy create(Class<?> declaring, Object async);
}
@Resource
private Logger logger = Logger.NULL;
private final Function<ClassMethodArgsAndReturnVal, Optional<Object>> optionalConverter;
private final Object delegate;
private final Class<?> declaring;
@ -74,9 +85,10 @@ public class SyncProxy extends AbstractInvocationHandler {
private static final Set<Method> objectMethods = ImmutableSet.copyOf(Object.class.getMethods());
@Inject
private SyncProxy(Function<ClassMethodArgsAndReturnVal, Optional<Object>> optionalConverter, Class<?> declaring, Object async,
@Named("sync") LoadingCache<ClassMethodArgs, Object> delegateMap, Map<Class<?>,
Class<?>> sync2Async, final Map<String, Long> timeouts)
@VisibleForTesting
SyncProxy(Function<ClassMethodArgsAndReturnVal, Optional<Object>> optionalConverter,
@Named("sync") LoadingCache<ClassMethodArgs, Object> delegateMap, Map<Class<?>, Class<?>> sync2Async,
@Named("TIMEOUTS") Map<String, Long> timeouts, @Assisted Class<?> declaring, @Assisted Object async)
throws SecurityException, NoSuchMethodException {
this.optionalConverter = optionalConverter;
this.delegateMap = delegateMap;
@ -86,7 +98,6 @@ public class SyncProxy extends AbstractInvocationHandler {
ImmutableMap.Builder<Method, Method> methodMapBuilder = ImmutableMap.builder();
ImmutableMap.Builder<Method, Method> syncMethodMapBuilder = ImmutableMap.builder();
ImmutableMap.Builder<Method, Optional<Long>> timeoutMapBuilder = ImmutableMap.builder();
for (Method method : declaring.getMethods()) {
if (!objectMethods.contains(method)) {
@ -95,16 +106,19 @@ public class SyncProxy extends AbstractInvocationHandler {
throw new IllegalArgumentException(String.format(
"method %s has different typed exceptions than delegated method %s", method, delegatedMethod));
if (delegatedMethod.getReturnType().isAssignableFrom(ListenableFuture.class)) {
timeoutMapBuilder.put(method, timeoutInMillis(method, timeouts));
methodMapBuilder.put(method, delegatedMethod);
} else {
syncMethodMapBuilder.put(method, delegatedMethod);
}
}
}
methodMap = methodMapBuilder.build();
syncMethodMap = syncMethodMapBuilder.build();
ImmutableMap.Builder<Method, Optional<Long>> timeoutMapBuilder = ImmutableMap.builder();
for (Method method : methodMap.keySet()) {
timeoutMapBuilder.put(method, timeoutInNanos(method, timeouts));
}
timeoutMap = timeoutMapBuilder.build();
}
@ -140,10 +154,15 @@ public class SyncProxy extends AbstractInvocationHandler {
}
} else {
try {
ListenableFuture<?> future = ((ListenableFuture<?>) methodMap.get(method).invoke(delegate, args));
Optional<Long> timeoutNanos = timeoutMap.get(method);
if (timeoutNanos.isPresent())
Method asyncMethod = methodMap.get(method);
String name = asyncMethod.getDeclaringClass().getSimpleName() + "." + asyncMethod.getName();
ListenableFuture<?> future = ((ListenableFuture<?>) asyncMethod.invoke(delegate, args));
if (timeoutNanos.isPresent()) {
logger.debug(">> blocking on %s for %s", name, timeoutNanos);
return future.get(timeoutNanos.get(), TimeUnit.NANOSECONDS);
}
logger.debug(">> blocking on %s", name);
return future.get();
} catch (ProvisionException e) {
throw Throwables2.returnFirstExceptionIfInListOrThrowStandardExceptionOrCause(method.getExceptionTypes(), e);
@ -156,7 +175,7 @@ public class SyncProxy extends AbstractInvocationHandler {
}
// override timeout by values configured in properties(in ms)
private Optional<Long> timeoutInMillis(Method method, Map<String, Long> timeouts) {
private Optional<Long> timeoutInNanos(Method method, Map<String, Long> timeouts) {
String className = declaring.getSimpleName();
Optional<Long> timeoutMillis = fromNullable(timeouts.get(className + "." + method.getName()))
.or(fromNullable(timeouts.get(className)))
@ -168,6 +187,6 @@ public class SyncProxy extends AbstractInvocationHandler {
@Override
public String toString() {
return "Sync Proxy for: " + delegate.getClass().getSimpleName();
return "blocking invocation handler for: " + delegate.getClass().getSimpleName();
}
}

View File

@ -49,21 +49,21 @@ public class BindRestContextWithWildcardExtendsExplicitAndRawType extends Abstra
@SuppressWarnings("unchecked")
@Override
protected void configure() {
TypeToken concreteType = BaseRestApiMetadata.contextToken(TypeToken.of(restApiMetadata.getApi()), TypeToken
TypeToken<?> concreteType = BaseRestApiMetadata.contextToken(TypeToken.of(restApiMetadata.getApi()), TypeToken
.of(restApiMetadata.getAsyncApi()));
// bind explicit type
bind(TypeLiteral.get(concreteType.getType())).to(
(TypeLiteral) TypeLiteral.get(Types.newParameterizedType(RestContextImpl.class,
restApiMetadata.getApi(), restApiMetadata.getAsyncApi())));
TypeLiteral.class.cast(TypeLiteral.get(Types.newParameterizedType(RestContextImpl.class,
restApiMetadata.getApi(), restApiMetadata.getAsyncApi()))));
// bind potentially wildcard type
if (!concreteType.equals(restApiMetadata.getContext())) {
bind(TypeLiteral.get(restApiMetadata.getContext().getType())).to(
(TypeLiteral) TypeLiteral.get(Types.newParameterizedType(RestContextImpl.class, restApiMetadata
.getApi(), restApiMetadata.getAsyncApi())));
TypeLiteral.class.cast(TypeLiteral.get(Types.newParameterizedType(RestContextImpl.class,
restApiMetadata.getApi(), restApiMetadata.getAsyncApi()))));
}
// bind w/o types
bind(TypeLiteral.get(RestContext.class)).to(
(TypeLiteral) TypeLiteral.get(Types.newParameterizedType(RestContextImpl.class,
restApiMetadata.getApi(), restApiMetadata.getAsyncApi())));
TypeLiteral.class.cast(TypeLiteral.get(Types.newParameterizedType(RestContextImpl.class,
restApiMetadata.getApi(), restApiMetadata.getAsyncApi()))));
}
}

View File

@ -39,7 +39,7 @@ public class AsyncClientFactory {
private final Injector injector;
@Inject
public AsyncClientFactory(Injector injector) {
private AsyncClientFactory(Injector injector) {
this.injector = injector;
}

View File

@ -23,7 +23,6 @@ import javax.inject.Singleton;
import org.jclouds.rest.AsyncClientFactory;
import com.google.inject.Injector;
import com.google.inject.Provider;
/**
@ -32,19 +31,19 @@ import com.google.inject.Provider;
*/
@Singleton
public class AsyncClientProvider<A> implements Provider<A> {
@Inject
Injector injector;
private final Class<?> asyncClientType;
private final Class<A> asyncClientType;
private final AsyncClientFactory factory;
@Inject
AsyncClientProvider(Class<?> asyncClientType) {
private AsyncClientProvider(AsyncClientFactory factory, Class<A> asyncClientType) {
this.factory = factory;
this.asyncClientType = asyncClientType;
}
@Override
@Singleton
public A get() {
return (A) injector.getInstance(AsyncClientFactory.class).create(asyncClientType);
return factory.create(asyncClientType);
}
}

View File

@ -18,14 +18,11 @@
*/
package org.jclouds.rest.config;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;
import com.google.common.collect.ImmutableMap;
import com.google.common.reflect.TypeParameter;
import com.google.common.reflect.TypeToken;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.inject.Binder;
import com.google.inject.Provider;
import com.google.inject.TypeLiteral;
/**
*
@ -34,64 +31,66 @@ import com.google.inject.Provider;
public class BinderUtils {
/**
* adds an explicit binding for a rest client, after which you can inject either the sync or
* async client class.
*
* <h3>note</h3> This client cannot have @Delegate methods, so if you have them, use the
* {@link #bindClientAndAsyncClient(Binder, Class, Class, Map) overloaded method}.
* 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.
*
* @param <S>
* sync client type
* sync interface that blocks
* @param <A>
* async client type (all methods have same args as client, but return
* listenablefuture)
* async type where all methods have same args as {@code sync}, but returns {@link ListenableFuture}
* @param binder
* guice binder
* @param syncClientType
* interface for the sync client
* @param asyncClientType
* interface for the async client
* @param sync
* type interface that blocks
* @param async
* type type that returns {@link ListenableFuture}
*/
public static <S, A> void bindClientAndAsyncClient(Binder binder, Class<?> syncClientType, Class<?> asyncClientType) {
bindClientAndAsyncClient(binder, syncClientType, asyncClientType, ImmutableMap.<Class<?>, Class<?>> of());
public static <S, A> void bindClientAndAsyncClient(Binder binder, Class<S> sync, Class<A> async) {
bindAsyncClient(binder, async);
bindClient(binder, sync, async);
}
/**
* adds an explicit binding for a rest client, after which you can inject either the sync or
* async client class.
* adds an explicit binding for an interface which synchronously blocks on similar calls to an {@code async} type.
*
* @param <S>
* sync client type
* sync interface that blocks
* @param <A>
* async client type (all methods have same args as client, but return
* listenablefuture)
* async type where all methods have same args as {@code sync}, but returns {@link ListenableFuture}
* @param binder
* guice binder
* @param syncClientType
* interface for the sync client (ex. LoginClient)
* @param asyncClientType
* interface for the async client (ex. LoginAsyncClient)
* @param sync2Async
* presuming your clients are annotated with @Delegate, contains the sync to async
* classes relating to these methods
* @param sync
* type interface that blocks
* @param async
* type type that returns {@link ListenableFuture}
*/
public static <S, A> void bindClientAndAsyncClient(Binder binder, Class<?> syncClientType, Class<?> asyncClientType,
Map<Class<?>, Class<?>> sync2Async) {
bindClient(binder, syncClientType, asyncClientType, sync2Async);
bindAsyncClient(binder, asyncClientType);
@SuppressWarnings("unchecked")
public static <S, A> void bindClient(Binder binder, Class<S> sync, Class<A> async) {
bindClass(binder, sync);
TypeToken<ClientProvider<S, A>> token = new TypeToken<ClientProvider<S, A>>() {
private static final long serialVersionUID = 1L;
}.where(new TypeParameter<S>() {
}, sync).where(new TypeParameter<A>() {
}, async);
binder.bind(sync).toProvider(TypeLiteral.class.cast(TypeLiteral.get(token.getType())));
}
public static <K, V> void bindClient(Binder binder, Class<K> syncClientType, Class<V> asyncClientType,
Map<Class<?>, Class<?>> sync2Async) {
Provider<K> asyncProvider = new ClientProvider<K, V>(syncClientType, asyncClientType, sync2Async);
binder.requestInjection(asyncProvider);
binder.bind(syncClientType).toProvider(asyncProvider);
@SuppressWarnings("unchecked")
private static <K> void bindClass(Binder binder, Class<K> sync) {
binder.bind(TypeLiteral.class.cast(TypeLiteral.get(new TypeToken<Class<K>>() {
private static final long serialVersionUID = 1L;
}.where(new TypeParameter<K>() {
}, sync).getType()))).toInstance(sync);
}
public static <T> void bindAsyncClient(Binder binder, Class<T> asyncClientType) {
Provider<T> asyncProvider = new AsyncClientProvider<T>(asyncClientType);
binder.requestInjection(asyncProvider);
binder.bind(asyncClientType).toProvider(asyncProvider);
@SuppressWarnings("unchecked")
private static <T> void bindAsyncClient(Binder binder, Class<T> async) {
bindClass(binder, async);
TypeToken<AsyncClientProvider<T>> token = new TypeToken<AsyncClientProvider<T>>() {
private static final long serialVersionUID = 1L;
}.where(new TypeParameter<T>() {
}, async);
binder.bind(async).toProvider(TypeLiteral.class.cast(TypeLiteral.get(token.getType())));
}
}

View File

@ -18,62 +18,38 @@
*/
package org.jclouds.rest.config;
import java.util.Map;
import static com.google.common.reflect.Reflection.newProxy;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.jclouds.concurrent.internal.SyncProxy;
import org.jclouds.internal.ClassMethodArgs;
import org.jclouds.internal.ClassMethodArgsAndReturnVal;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Throwables;
import com.google.common.cache.LoadingCache;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Provider;
import com.google.inject.TypeLiteral;
import com.google.inject.name.Names;
/**
* ClientProvider makes the primary interface for the provider context. ex. {@code
* context.getProviderSpecificContext().getApi()} is created by ClientProvider, which is a singleton
* ClientProvider makes the primary interface for the provider context. ex.
* {@code context.getProviderSpecificContext().getApi()} is created by ClientProvider, which is a singleton
*
* @author Adrian Cole
*/
@Singleton
public class ClientProvider<S, A> implements Provider<S> {
@Inject
Injector injector;
private final Class<?> syncClientType;
private final Class<?> asyncClientType;
private final Map<Class<?>, Class<?>> sync2Async;
private final SyncProxy.Factory factory;
private final Class<S> syncClientType;
private final A asyncClient;
@Inject
ClientProvider(Class<?> syncClientType, Class<?> asyncClientType, Map<Class<?>, Class<?>> sync2Async) {
this.asyncClientType = asyncClientType;
private ClientProvider(SyncProxy.Factory factory, Class<S> syncClientType, A asyncClient) {
this.factory = factory;
this.asyncClient = asyncClient;
this.syncClientType = syncClientType;
this.sync2Async = sync2Async;
}
@Override
@Singleton
public S get() {
A client = (A) injector.getInstance(Key.get(asyncClientType));
Function<ClassMethodArgsAndReturnVal, Optional<Object>> optionalConverter = injector.getInstance(Key.get(new TypeLiteral<Function<ClassMethodArgsAndReturnVal, Optional<Object>>>() {
}));
LoadingCache<ClassMethodArgs, Object> delegateMap = injector.getInstance(Key.get(
new TypeLiteral<LoadingCache<ClassMethodArgs, Object>>() {
}, Names.named("sync")));
Map<String, Long> timeoutsMap = injector.getInstance(Key.get(new TypeLiteral<Map<String, Long>>() {
}, Names.named("TIMEOUTS")));
try {
return (S) SyncProxy.proxy(optionalConverter, syncClientType, client, delegateMap, sync2Async,
timeoutsMap);
} catch (Exception e) {
throw Throwables.propagate(e);
}
return newProxy(syncClientType, factory.create(syncClientType, asyncClient));
}
}

View File

@ -19,48 +19,37 @@
package org.jclouds.rest.config;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.reflect.Reflection.newProxy;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Provider;
import org.jclouds.concurrent.internal.SyncProxy;
import org.jclouds.internal.ClassMethodArgs;
import org.jclouds.internal.ClassMethodArgsAndReturnVal;
import org.jclouds.util.Optionals2;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Throwables;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.TypeLiteral;
import com.google.inject.name.Names;
/**
* CreateClientForCaller is parameterized, so clients it creates aren't singletons. For example,
* CreateClientForCaller satisfies a call like this:
* {@code context.getProviderSpecificContext().getApi().getOrgClientForName(name)}
* CreateClientForCaller is parameterized, so clients it creates aren't singletons. For example, CreateClientForCaller
* satisfies a call like this: {@code context.getProviderSpecificContext().getApi().getOrgClientForName(name)}
*
* @author Adrian Cole
*/
public class CreateClientForCaller extends CacheLoader<ClassMethodArgs, Object> {
@Inject
Injector injector;
private final SyncProxy.Factory factory;
private final LoadingCache<ClassMethodArgs, Object> asyncMap;
private final Provider<LoadingCache<ClassMethodArgs, Object>> delegateMap;
Map<Class<?>, Class<?>> sync2Async;
private final Map<Class<?>, Class<?>> sync2Async;
@Inject
CreateClientForCaller(@Named("async") LoadingCache<ClassMethodArgs, Object> asyncMap,
@Named("sync") Provider<LoadingCache<ClassMethodArgs, Object>> delegateMap) {
private CreateClientForCaller(SyncProxy.Factory factory,
@Named("async") LoadingCache<ClassMethodArgs, Object> asyncMap, Map<Class<?>, Class<?>> sync2Async) {
this.factory = factory;
this.asyncMap = asyncMap;
this.delegateMap = delegateMap;
this.sync2Async = sync2Async;
}
@Override
@ -70,15 +59,6 @@ public class CreateClientForCaller extends CacheLoader<ClassMethodArgs, Object>
checkState(asyncClass != null, "configuration error, sync class " + syncClass + " not mapped to an async class");
Object asyncClient = asyncMap.getUnchecked(from);
checkState(asyncClient != null, "configuration error, sync client for " + from + " not found");
Function<ClassMethodArgsAndReturnVal, Optional<Object>> optionalConverter = injector.getInstance(Key.get(new TypeLiteral<Function<ClassMethodArgsAndReturnVal, Optional<Object>>>() {
}));
Map<String, Long> timeoutsMap = injector.getInstance(Key.get(new TypeLiteral<Map<String, Long>>() {
}, Names.named("TIMEOUTS")));
try {
return SyncProxy.proxy(optionalConverter, syncClass, asyncClient, delegateMap.get(), sync2Async, timeoutsMap);
} catch (Exception e) {
throw Throwables.propagate(e);
}
return newProxy(syncClass, factory.create(syncClass, asyncClient));
}
}

View File

@ -19,7 +19,7 @@
package org.jclouds.rest.config;
import java.util.Map;
import static org.jclouds.rest.config.BinderUtils.*;
import org.jclouds.rest.ConfiguresRestClient;
import org.jclouds.util.TypeTokens2;
@ -42,8 +42,10 @@ public class RestClientModule<S, A> extends RestModule {
protected RestClientModule(Map<Class<?>, Class<?>> sync2Async) {
super(sync2Async);
this.syncClientType = TypeTokens2.checkBound(new TypeToken<S>(getClass()) {
private static final long serialVersionUID = 1L;
});
this.asyncClientType = TypeTokens2.checkBound(new TypeToken<A>(getClass()) {
private static final long serialVersionUID = 1L;
});
}
@ -73,8 +75,7 @@ public class RestClientModule<S, A> extends RestModule {
@Override
protected void configure() {
super.configure();
bindAsyncClient();
bindClient();
bindClientAndAsyncClient(binder(), syncClientType.getRawType(), asyncClientType.getRawType());
bindErrorHandlers();
bindRetryHandlers();
}
@ -109,14 +110,7 @@ public class RestClientModule<S, A> extends RestModule {
*
*/
protected void bindErrorHandlers() {
}
protected void bindAsyncClient() {
BinderUtils.bindAsyncClient(binder(), asyncClientType.getRawType());
}
protected void bindClient() {
BinderUtils.bindClient(binder(), syncClientType.getRawType(), asyncClientType.getRawType(), sync2Async);
}
}

View File

@ -20,6 +20,7 @@ package org.jclouds.rest.config;
import static com.google.common.reflect.Reflection.newProxy;
import static org.jclouds.Constants.PROPERTY_TIMEOUTS_PREFIX;
import static org.jclouds.rest.config.BinderUtils.bindClientAndAsyncClient;
import java.lang.reflect.Method;
import java.net.URI;
@ -29,6 +30,7 @@ import java.util.concurrent.atomic.AtomicReference;
import javax.inject.Named;
import javax.inject.Singleton;
import org.jclouds.concurrent.internal.SyncProxy;
import org.jclouds.functions.IdentityFunction;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
@ -76,7 +78,7 @@ public class RestModule extends AbstractModule {
public static final TypeLiteral<Supplier<URI>> URI_SUPPLIER_TYPE = new TypeLiteral<Supplier<URI>>() {
};
protected final Map<Class<?>, Class<?>> sync2Async;
private final Map<Class<?>, Class<?>> sync2Async;
protected final AtomicReference<AuthorizationException> authException = Atomics.newReference();
public RestModule() {
@ -93,14 +95,14 @@ public class RestModule extends AbstractModule {
@Override
protected void configure() {
bind(new TypeLiteral<Map<Class<?>, Class<?>>>(){}).toInstance(sync2Async);
install(new SaxParserModule());
install(new GsonModule());
install(new FactoryModuleBuilder().build(BindToJsonPayloadWrappedWith.Factory.class));
bind(IdentityFunction.class).toInstance(IdentityFunction.INSTANCE);
bind(AsyncRestClientProxy.Factory.class).to(Factory.class).in(Scopes.SINGLETON);
BinderUtils.bindAsyncClient(binder(), HttpAsyncClient.class);
BinderUtils.bindClient(binder(), HttpClient.class, HttpAsyncClient.class, ImmutableMap.<Class<?>, Class<?>> of(
HttpClient.class, HttpAsyncClient.class));
install(new FactoryModuleBuilder().build(SyncProxy.Factory.class));
bindClientAndAsyncClient(binder(), HttpClient.class, HttpAsyncClient.class);
// this will help short circuit scenarios that can otherwise lock out users
bind(new TypeLiteral<AtomicReference<AuthorizationException>>() {
}).toInstance(authException);
@ -195,14 +197,12 @@ public class RestModule extends AbstractModule {
public TransformingHttpCommand<?> create(HttpRequest request, Function<HttpResponse, ?> transformer) {
return new TransformingHttpCommandImpl(executorService, request, transformer);
}
}
@Provides
@Singleton
@Named("sync")
LoadingCache<ClassMethodArgs, Object> provideSyncDelegateMap(CreateClientForCaller createClientForCaller) {
createClientForCaller.sync2Async = sync2Async;
return CacheBuilder.newBuilder().build(createClientForCaller);
}

View File

@ -226,14 +226,14 @@ public class AsyncRestClientProxy<T> extends AbstractInvocationHandler {
// else try to create an instance
return injector.getInstance(Key.get(genericReturnType, qualifier));
}
private ListenableFuture<?> createListenableFutureForHttpRequestMappedToMethodAndArgs(Method method, Object[] args)
throws ExecutionException {
method = annotationProcessor.getDelegateOrNull(method);
logger.trace("Converting %s.%s", declaring.getSimpleName(), method.getName());
String name = method.getDeclaringClass().getSimpleName() + "." + method.getName();
logger.trace(">> converting %s", name);
FutureFallback<?> fallback = fallbacks.getUnchecked(method);
// in case there is an exception creating the request, we should at least
// pass in args
// in case there is an exception creating the request, we should at least pass in args
if (fallback instanceof InvocationContext) {
InvocationContext.class.cast(fallback).setContext((HttpRequest) null);
}
@ -243,13 +243,12 @@ public class AsyncRestClientProxy<T> extends AbstractInvocationHandler {
if (fallback instanceof InvocationContext) {
InvocationContext.class.cast(fallback).setContext(request);
}
logger.trace("Converted %s.%s to %s", declaring.getSimpleName(), method.getName(), request.getRequestLine());
logger.trace("<< converted %s to %s", name, request.getRequestLine());
Function<HttpResponse, ?> transformer = annotationProcessor.createResponseParser(method, request);
logger.trace("Response from %s.%s is parsed by %s", declaring.getSimpleName(), method.getName(), transformer
.getClass().getSimpleName());
logger.trace("<< response from %s is parsed by %s", name, transformer.getClass().getSimpleName());
logger.debug("Invoking %s.%s", declaring.getSimpleName(), method.getName());
logger.debug(">> invoking %s", name);
result = commandFactory.create(request, transformer).execute();
} catch (RuntimeException e) {
AuthorizationException aex = Throwables2.getFirstThrowableOfType(e, AuthorizationException.class);
@ -261,8 +260,7 @@ public class AsyncRestClientProxy<T> extends AbstractInvocationHandler {
return immediateFailedFuture(ex);
}
}
logger.trace("Exceptions from %s.%s are parsed by %s", declaring.getSimpleName(), method.getName(),
fallback.getClass().getSimpleName());
logger.trace("<< exceptions from %s are parsed by %s", name, fallback.getClass().getSimpleName());
return withFallback(result, fallback);
}

View File

@ -18,6 +18,7 @@
*/
package org.jclouds.concurrent.internal;
import static com.google.common.reflect.Reflection.newProxy;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;
@ -69,53 +70,45 @@ public class SyncProxyTest {
}
public void testWithDefaultPropTimeout() throws Exception {
LoadingCache<ClassMethodArgs, Object> cache = CacheBuilder.newBuilder().build(
CacheLoader.from(Functions.<Object> constant(null)));
Sync withOverride = SyncProxy.proxy(new AlwaysPresentImplicitOptionalConverter(), Sync.class, new Async(), cache,
ImmutableMap.<Class<?>, Class<?>> of(), ImmutableMap.<String, Long> of("default", 250L));
Sync withOverride = syncProxyForTimeouts(ImmutableMap.of("default", 250L));
assertEquals(withOverride.get(), "foo");
verify(future);
}
public void testWithClassPropTimeout() throws Exception {
LoadingCache<ClassMethodArgs, Object> cache = CacheBuilder.newBuilder().build(
CacheLoader.from(Functions.<Object> constant(null)));
Sync withOverride = SyncProxy.proxy(new AlwaysPresentImplicitOptionalConverter(), Sync.class, new Async(), cache,
ImmutableMap.<Class<?>, Class<?>> of(), ImmutableMap.<String, Long> of("default", 50L, "Sync", 250L));
Sync withOverride = syncProxyForTimeouts(ImmutableMap.of("default", 50L, "Sync", 250L));
assertEquals(withOverride.get(), "foo");
verify(future);
}
public void testWithMethodPropTimeout() throws Exception {
LoadingCache<ClassMethodArgs, Object> cache = CacheBuilder.newBuilder().build(
CacheLoader.from(Functions.<Object> constant(null)));
Sync withOverride = SyncProxy.proxy(new AlwaysPresentImplicitOptionalConverter(), Sync.class, new Async(), cache,
ImmutableMap.<Class<?>, Class<?>> of(),
ImmutableMap.<String, Long> of("default", 50L, "Sync", 100L, "Sync.get", 250L));
Sync withOverride = syncProxyForTimeouts(ImmutableMap.of("default", 50L, "Sync", 100L, "Sync.get", 250L));
assertEquals(withOverride.get(), "foo");
verify(future);
}
@SuppressWarnings("unchecked")
public void testWithMethodWithNoTimeoutsCallGetDirectly() throws Exception {
LoadingCache<ClassMethodArgs, Object> cache = CacheBuilder.newBuilder().build(
CacheLoader.from(Functions.<Object> constant(null)));
Sync withOverride = SyncProxy.proxy(new AlwaysPresentImplicitOptionalConverter(), Sync.class, new Async(), cache,
ImmutableMap.<Class<?>, Class<?>> of(),
ImmutableMap.<String, Long> of());
future = createMock(ListenableFuture.class);
expect(future.get()).andReturn("foo");
replay(future);
assertEquals(withOverride.get(), "foo");
Sync noOverrides = syncProxyForTimeouts(ImmutableMap.<String, Long> of());
assertEquals(noOverrides.get(), "foo");
verify(future);
}
private Sync syncProxyForTimeouts(ImmutableMap<String, Long> timeouts) throws NoSuchMethodException {
LoadingCache<ClassMethodArgs, Object> cache = CacheBuilder.newBuilder().build(
CacheLoader.from(Functions.<Object> constant(null)));
return newProxy(
Sync.class,
new SyncProxy(new AlwaysPresentImplicitOptionalConverter(), cache, ImmutableMap.<Class<?>, Class<?>> of(
Sync.class, Async.class), timeouts, Sync.class, new Async()));
}
}

View File

@ -25,7 +25,7 @@ import static org.testng.Assert.assertTrue;
import org.jclouds.Constants;
import org.jclouds.concurrent.config.ExecutorServiceModule;
import org.jclouds.events.config.annotations.AsyncBus;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import com.google.common.eventbus.AsyncEventBus;
@ -44,7 +44,7 @@ import com.google.inject.name.Names;
public class EventBusModuleTest {
private Injector injector;
@BeforeMethod
@BeforeClass
public void setup() {
ExecutorServiceModule executorServiceModule = new ExecutorServiceModule() {
@Override

View File

@ -21,6 +21,7 @@ package org.jclouds.abiquo.config;
import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
import static org.jclouds.abiquo.domain.DomainWrapper.wrap;
import static org.jclouds.rest.config.BinderUtils.bindClientAndAsyncClient;
import java.util.List;
import java.util.Map;
@ -65,7 +66,6 @@ import org.jclouds.rest.AuthorizationException;
import org.jclouds.rest.ConfiguresRestClient;
import org.jclouds.rest.RestContext;
import org.jclouds.rest.Utils;
import org.jclouds.rest.config.BinderUtils;
import org.jclouds.rest.config.RestClientModule;
import org.jclouds.rest.suppliers.MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier;
import org.jclouds.util.Suppliers2;
@ -100,22 +100,10 @@ public class AbiquoRestClientModule extends RestClientModule<AbiquoApi, AbiquoAs
super(DELEGATE_MAP);
}
@Override
protected void bindAsyncClient() {
super.bindAsyncClient();
BinderUtils.bindAsyncClient(binder(), AbiquoHttpAsyncClient.class);
}
@Override
protected void bindClient() {
super.bindClient();
BinderUtils.bindClient(binder(), AbiquoHttpClient.class, AbiquoHttpAsyncClient.class,
ImmutableMap.<Class<?>, Class<?>> of(AbiquoHttpClient.class, AbiquoHttpAsyncClient.class));
}
@Override
protected void configure() {
super.configure();
bindClientAndAsyncClient(binder(), AbiquoHttpClient.class, AbiquoHttpAsyncClient.class);
bind(Utils.class).to(ExtendedUtils.class);
}

View File

@ -37,7 +37,6 @@ import org.jclouds.http.annotation.ServerError;
import org.jclouds.location.Provider;
import org.jclouds.rest.ConfiguresRestClient;
import org.jclouds.rest.RestContext;
import org.jclouds.rest.config.BinderUtils;
import org.jclouds.rest.config.RestClientModule;
import org.jclouds.rest.internal.RestContextImpl;
import org.jclouds.util.Suppliers2;
@ -140,22 +139,6 @@ public class VCloudDirectorRestClientModule extends RestClientModule<VCloudDirec
.put(UserApi.class, UserAsyncApi.class)
.build();
@Override
protected void bindAsyncClient() {
// bind the user api (default)
super.bindAsyncClient();
// bind the admin api
BinderUtils.bindAsyncClient(binder(), VCloudDirectorAdminAsyncApi.class);
}
@Override
protected void bindClient() {
// bind the user api (default)
super.bindClient();
// bind the admin api
BinderUtils.bindClient(binder(), VCloudDirectorAdminApi.class, VCloudDirectorAdminAsyncApi.class, ADMIN_DELEGATE_MAP);
}
public VCloudDirectorRestClientModule() {
super(ADMIN_DELEGATE_MAP);
}
@ -176,6 +159,8 @@ public class VCloudDirectorRestClientModule extends RestClientModule<VCloudDirec
bind(HttpRetryHandler.class).annotatedWith(ClientError.class).to(InvalidateSessionAndRetryOn401AndLogoutOnClose.class);
super.configure();
bindClientAndAsyncClient(binder(), VCloudDirectorAdminApi.class, VCloudDirectorAdminAsyncApi.class);
}
@Override