Cleaned up scoping of delegated methods

This commit is contained in:
Adrian Cole 2010-07-13 02:39:52 -05:00
parent 436037c7be
commit 891484a2aa
4 changed files with 502 additions and 304 deletions

View File

@ -0,0 +1,38 @@
/**
*
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ====================================================================
*/
package org.jclouds.rest.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.inject.Qualifier;
/**
* Apply this to implementation classes when you want Access to items from the
* scope of a delegate.
*
* @author Adrian Cole
*/
@Target( { ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER , ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Caller {
}

View File

@ -51,15 +51,15 @@ public class RestClientModule<S, A> extends AbstractModule {
protected final Map<Class<?>, Class<?>> delegates; protected final Map<Class<?>, Class<?>> delegates;
public RestClientModule(Class<S> syncClientType, Class<A> asyncClientType, public RestClientModule(Class<S> syncClientType, Class<A> asyncClientType,
Map<Class<?>, Class<?>> delegates) { Map<Class<?>, Class<?>> delegates) {
this.asyncClientType = asyncClientType; this.asyncClientType = asyncClientType;
this.syncClientType = syncClientType; this.syncClientType = syncClientType;
this.delegates = delegates; this.delegates = delegates;
} }
public RestClientModule(Class<S> syncClientType, Class<A> asyncClientType) { public RestClientModule(Class<S> syncClientType, Class<A> asyncClientType) {
this(syncClientType, asyncClientType, ImmutableMap.<Class<?>, Class<?>> of(syncClientType, this(syncClientType, asyncClientType, ImmutableMap
asyncClientType)); .<Class<?>, Class<?>> of(syncClientType, asyncClientType));
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -68,8 +68,9 @@ public class RestClientModule<S, A> extends AbstractModule {
// Ensures the restcontext can be looked up without generic types. // Ensures the restcontext can be looked up without generic types.
bind(new TypeLiteral<RestContext>() { bind(new TypeLiteral<RestContext>() {
}).to( }).to(
(TypeLiteral) TypeLiteral.get(Types.newParameterizedType(RestContextImpl.class, (TypeLiteral) TypeLiteral.get(Types.newParameterizedType(
syncClientType, asyncClientType))).in(Scopes.SINGLETON); RestContextImpl.class, syncClientType, asyncClientType))).in(
Scopes.SINGLETON);
bindAsyncClient(); bindAsyncClient();
bindClient(); bindClient();
bindErrorHandlers(); bindErrorHandlers();
@ -82,8 +83,10 @@ public class RestClientModule<S, A> extends AbstractModule {
* ex. * ex.
* *
* <pre> * <pre>
* bind(HttpRetryHandler.class).annotatedWith(Redirection.class).to(AWSRedirectionRetryHandler.class); * bind(HttpRetryHandler.class).annotatedWith(Redirection.class).to(
* bind(HttpRetryHandler.class).annotatedWith(ClientError.class).to(AWSClientErrorRetryHandler.class); * AWSRedirectionRetryHandler.class);
* bind(HttpRetryHandler.class).annotatedWith(ClientError.class).to(
* AWSClientErrorRetryHandler.class);
* </pre> * </pre>
* *
*/ */
@ -96,9 +99,12 @@ public class RestClientModule<S, A> extends AbstractModule {
* ex. * ex.
* *
* <pre> * <pre>
* bind(HttpErrorHandler.class).annotatedWith(Redirection.class).to(ParseAWSErrorFromXmlContent.class); * bind(HttpErrorHandler.class).annotatedWith(Redirection.class).to(
* bind(HttpErrorHandler.class).annotatedWith(ClientError.class).to(ParseAWSErrorFromXmlContent.class); * ParseAWSErrorFromXmlContent.class);
* bind(HttpErrorHandler.class).annotatedWith(ServerError.class).to(ParseAWSErrorFromXmlContent.class); * bind(HttpErrorHandler.class).annotatedWith(ClientError.class).to(
* ParseAWSErrorFromXmlContent.class);
* bind(HttpErrorHandler.class).annotatedWith(ServerError.class).to(
* ParseAWSErrorFromXmlContent.class);
* </pre> * </pre>
* *
* *
@ -111,14 +117,16 @@ public class RestClientModule<S, A> extends AbstractModule {
} }
protected void bindClient() { protected void bindClient() {
BinderUtils.bindClient(binder(), syncClientType, asyncClientType, delegates); BinderUtils.bindClient(binder(), syncClientType, asyncClientType,
delegates);
} }
@Provides @Provides
@Singleton @Singleton
@Named("sync") @Named("sync")
ConcurrentMap<ClassMethodArgs, Object> provideSyncDelegateMap( ConcurrentMap<ClassMethodArgs, Object> provideSyncDelegateMap(
CreateClientForCaller createClientForCaller) { CreateClientForCaller createClientForCaller) {
createClientForCaller.sync2Async = delegates; createClientForCaller.sync2Async = delegates;
return new MapMaker().makeComputingMap(createClientForCaller); return new MapMaker().makeComputingMap(createClientForCaller);
} }

View File

@ -18,9 +18,9 @@
*/ */
package org.jclouds.rest.config; package org.jclouds.rest.config;
import java.net.URI;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
import javax.inject.Singleton; import javax.inject.Singleton;
import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriBuilder;
@ -36,6 +36,7 @@ import org.jclouds.internal.ClassMethodArgs;
import org.jclouds.rest.AsyncClientFactory; import org.jclouds.rest.AsyncClientFactory;
import org.jclouds.rest.HttpAsyncClient; import org.jclouds.rest.HttpAsyncClient;
import org.jclouds.rest.HttpClient; import org.jclouds.rest.HttpClient;
import org.jclouds.rest.annotations.Caller;
import org.jclouds.rest.internal.AsyncRestClientProxy; import org.jclouds.rest.internal.AsyncRestClientProxy;
import org.jclouds.rest.internal.RestAnnotationProcessor; import org.jclouds.rest.internal.RestAnnotationProcessor;
@ -43,8 +44,10 @@ import com.google.common.base.Function;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.MapMaker; import com.google.common.collect.MapMaker;
import com.google.inject.AbstractModule; import com.google.inject.AbstractModule;
import com.google.inject.Inject;
import com.google.inject.Injector; import com.google.inject.Injector;
import com.google.inject.Key; import com.google.inject.Key;
import com.google.inject.Module;
import com.google.inject.Provides; import com.google.inject.Provides;
import com.google.inject.Scopes; import com.google.inject.Scopes;
import com.google.inject.TypeLiteral; import com.google.inject.TypeLiteral;
@ -61,58 +64,99 @@ public class RestModule extends AbstractModule {
protected void configure() { protected void configure() {
install(new ParserModule()); install(new ParserModule());
bind(UriBuilder.class).to(UriBuilderImpl.class); bind(UriBuilder.class).to(UriBuilderImpl.class);
bind(AsyncRestClientProxy.Factory.class).to(Factory.class).in(Scopes.SINGLETON); bind(AsyncRestClientProxy.Factory.class).to(Factory.class).in(
Scopes.SINGLETON);
BinderUtils.bindAsyncClient(binder(), HttpAsyncClient.class); BinderUtils.bindAsyncClient(binder(), HttpAsyncClient.class);
BinderUtils.bindClient(binder(), HttpClient.class, HttpAsyncClient.class, ImmutableMap BinderUtils.bindClient(binder(), HttpClient.class, HttpAsyncClient.class,
.<Class<?>, Class<?>> of(HttpClient.class, HttpAsyncClient.class)); ImmutableMap.<Class<?>, Class<?>> of(HttpClient.class,
HttpAsyncClient.class));
} }
@Provides @Provides
@Singleton @Singleton
@Named("async") @Named("async")
ConcurrentMap<ClassMethodArgs, Object> provideAsyncDelegateMap( ConcurrentMap<ClassMethodArgs, Object> provideAsyncDelegateMap(
CreateAsyncClientForCaller createAsyncClientForCaller) { CreateAsyncClientForCaller createAsyncClientForCaller) {
return new MapMaker().makeComputingMap(createAsyncClientForCaller); return new MapMaker().makeComputingMap(createAsyncClientForCaller);
} }
static class CreateAsyncClientForCaller implements Function<ClassMethodArgs, Object> { static class CreateAsyncClientForCaller implements
Function<ClassMethodArgs, Object> {
private final Injector injector; private final Injector injector;
private final AsyncRestClientProxy.Factory factory; private final AsyncRestClientProxy.Factory factory;
@Caller
@Inject(optional = true)
Module callerModule = new CallerScopedBindingModule();
@Inject @Inject
CreateAsyncClientForCaller(Injector injector, AsyncRestClientProxy.Factory factory) { CreateAsyncClientForCaller(Injector injector,
AsyncRestClientProxy.Factory factory) {
this.injector = injector; this.injector = injector;
this.factory = factory; this.factory = factory;
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public Object apply(ClassMethodArgs from) { public Object apply(final ClassMethodArgs from) {
Class clazz = from.getAsyncClass(); Class clazz = from.getAsyncClass();
TypeLiteral typeLiteral = TypeLiteral.get(clazz); TypeLiteral typeLiteral = TypeLiteral.get(clazz);
RestAnnotationProcessor util = (RestAnnotationProcessor) injector.getInstance(Key Injector injector = this.injector.createChildInjector(callerModule,
.get(TypeLiteral.get(Types.newParameterizedType(RestAnnotationProcessor.class, new AbstractModule() {
clazz))));
util.setCaller(from);
ConcurrentMap<ClassMethodArgs, Object> delegateMap = injector.getInstance(Key.get( @Override
new TypeLiteral<ConcurrentMap<ClassMethodArgs, Object>>() { protected void configure() {
}, Names.named("async"))); bind(ClassMethodArgs.class).annotatedWith(Caller.class)
AsyncRestClientProxy proxy = new AsyncRestClientProxy(injector, factory, util, .toInstance(from);
typeLiteral, delegateMap); install(callerModule);
}
});
RestAnnotationProcessor util = (RestAnnotationProcessor) injector
.getInstance(Key.get(TypeLiteral.get(Types.newParameterizedType(
RestAnnotationProcessor.class, clazz))));
// not sure why we have to go back and re-inject this...
injector.injectMembers(util);
ConcurrentMap<ClassMethodArgs, Object> delegateMap = injector
.getInstance(Key.get(
new TypeLiteral<ConcurrentMap<ClassMethodArgs, Object>>() {
}, Names.named("async")));
AsyncRestClientProxy proxy = new AsyncRestClientProxy(injector,
factory, util, typeLiteral, delegateMap);
injector.injectMembers(proxy); injector.injectMembers(proxy);
return AsyncClientFactory.create(clazz, proxy); return AsyncClientFactory.create(clazz, proxy);
} }
} }
@Singleton
public static class CallerScopedBindingModule extends AbstractModule {
@Provides
@Caller
URI provideCallerScopedURI(Injector injector, @Caller ClassMethodArgs args) {
try {
return RestAnnotationProcessor.getEndpointFor(args.getMethod(),
args.getArgs(), injector);
} catch (IllegalStateException e) {
return null;
}
}
@Override
protected void configure() {
}
}
private static class Factory implements AsyncRestClientProxy.Factory { private static class Factory implements AsyncRestClientProxy.Factory {
@Inject @Inject
private TransformingHttpCommandExecutorService executorService; private TransformingHttpCommandExecutorService executorService;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public TransformingHttpCommand<?> create(HttpRequest request, public TransformingHttpCommand<?> create(HttpRequest request,
Function<HttpResponse, ?> transformer) { Function<HttpResponse, ?> transformer) {
return new TransformingHttpCommandImpl(executorService, request, transformer); return new TransformingHttpCommandImpl(executorService, request,
transformer);
} }
} }