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

View File

@ -18,9 +18,9 @@
*/
package org.jclouds.rest.config;
import java.net.URI;
import java.util.concurrent.ConcurrentMap;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import javax.ws.rs.core.UriBuilder;
@ -36,6 +36,7 @@ import org.jclouds.internal.ClassMethodArgs;
import org.jclouds.rest.AsyncClientFactory;
import org.jclouds.rest.HttpAsyncClient;
import org.jclouds.rest.HttpClient;
import org.jclouds.rest.annotations.Caller;
import org.jclouds.rest.internal.AsyncRestClientProxy;
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.MapMaker;
import com.google.inject.AbstractModule;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Module;
import com.google.inject.Provides;
import com.google.inject.Scopes;
import com.google.inject.TypeLiteral;
@ -61,58 +64,99 @@ public class RestModule extends AbstractModule {
protected void configure() {
install(new ParserModule());
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.bindClient(binder(), HttpClient.class, HttpAsyncClient.class, ImmutableMap
.<Class<?>, Class<?>> of(HttpClient.class, HttpAsyncClient.class));
BinderUtils.bindClient(binder(), HttpClient.class, HttpAsyncClient.class,
ImmutableMap.<Class<?>, Class<?>> of(HttpClient.class,
HttpAsyncClient.class));
}
@Provides
@Singleton
@Named("async")
ConcurrentMap<ClassMethodArgs, Object> provideAsyncDelegateMap(
CreateAsyncClientForCaller createAsyncClientForCaller) {
CreateAsyncClientForCaller 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 AsyncRestClientProxy.Factory factory;
@Caller
@Inject(optional = true)
Module callerModule = new CallerScopedBindingModule();
@Inject
CreateAsyncClientForCaller(Injector injector, AsyncRestClientProxy.Factory factory) {
CreateAsyncClientForCaller(Injector injector,
AsyncRestClientProxy.Factory factory) {
this.injector = injector;
this.factory = factory;
}
@SuppressWarnings("unchecked")
@Override
public Object apply(ClassMethodArgs from) {
public Object apply(final ClassMethodArgs from) {
Class clazz = from.getAsyncClass();
TypeLiteral typeLiteral = TypeLiteral.get(clazz);
RestAnnotationProcessor util = (RestAnnotationProcessor) injector.getInstance(Key
.get(TypeLiteral.get(Types.newParameterizedType(RestAnnotationProcessor.class,
clazz))));
util.setCaller(from);
Injector injector = this.injector.createChildInjector(callerModule,
new AbstractModule() {
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);
@Override
protected void configure() {
bind(ClassMethodArgs.class).annotatedWith(Caller.class)
.toInstance(from);
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);
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 {
@Inject
private TransformingHttpCommandExecutorService executorService;
@SuppressWarnings("unchecked")
public TransformingHttpCommand<?> create(HttpRequest request,
Function<HttpResponse, ?> transformer) {
return new TransformingHttpCommandImpl(executorService, request, transformer);
Function<HttpResponse, ?> transformer) {
return new TransformingHttpCommandImpl(executorService, request,
transformer);
}
}