diff --git a/core/src/main/java/org/jclouds/concurrent/internal/SyncProxy.java b/core/src/main/java/org/jclouds/concurrent/internal/SyncProxy.java index eb3eb3e6d5..fe3e14ea37 100644 --- a/core/src/main/java/org/jclouds/concurrent/internal/SyncProxy.java +++ b/core/src/main/java/org/jclouds/concurrent/internal/SyncProxy.java @@ -34,6 +34,7 @@ import javax.inject.Named; import org.jclouds.concurrent.Timeout; import org.jclouds.internal.ClassMethodArgs; +import org.jclouds.internal.ClassMethodArgsAndReturnVal; import org.jclouds.rest.annotations.Delegate; import org.jclouds.util.Optionals2; import org.jclouds.util.Throwables2; @@ -54,7 +55,7 @@ import com.google.inject.ProvisionException; public class SyncProxy implements InvocationHandler { @SuppressWarnings("unchecked") - public static T proxy(Function> optionalConverter, Class clazz, Object async, + public static T proxy(Function> optionalConverter, Class clazz, Object async, @Named("sync") LoadingCache delegateMap, Map, Class> sync2Async, Map timeouts) throws IllegalArgumentException, SecurityException, NoSuchMethodException { @@ -62,7 +63,7 @@ public class SyncProxy implements InvocationHandler { new SyncProxy(optionalConverter, clazz, async, delegateMap, sync2Async, timeouts)); } - private final Function> optionalConverter; + private final Function> optionalConverter; private final Object delegate; private final Class declaring; private final Map methodMap; @@ -73,7 +74,7 @@ public class SyncProxy implements InvocationHandler { private static final Set objectMethods = ImmutableSet.copyOf(Object.class.getMethods()); @Inject - private SyncProxy(Function> optionalConverter, Class declaring, Object async, + private SyncProxy(Function> optionalConverter, Class declaring, Object async, @Named("sync") LoadingCache delegateMap, Map, Class> sync2Async, final Map timeouts) throws SecurityException, NoSuchMethodException { @@ -107,6 +108,10 @@ public class SyncProxy implements InvocationHandler { } } + public Class getDeclaring() { + return declaring; + } + private Long getTimeout(Method method, long typeNanos, final Map timeouts) { Long timeout = overrideTimeout(method, timeouts); if (timeout == null && method.isAnnotationPresent(Timeout.class)) { @@ -139,9 +144,12 @@ public class SyncProxy implements InvocationHandler { // pass any parameters necessary to get a relevant instance of that async class // ex. getClientForRegion("north") might return an instance whose endpoint is // different that "south" - Object returnVal = delegateMap.get(new ClassMethodArgs(asyncClass, method, args)); + ClassMethodArgs cma = new ClassMethodArgs(asyncClass, method, args); + Object returnVal = delegateMap.get(cma); if (Optionals2.isReturnTypeOptional(method)){ - return optionalConverter.apply(returnVal); + ClassMethodArgsAndReturnVal cmar = ClassMethodArgsAndReturnVal.builder().fromClassMethodArgs(cma) + .returnVal(returnVal).build(); + return optionalConverter.apply(cmar); } return returnVal; } else if (syncMethodMap.containsKey(method)) { diff --git a/core/src/main/java/org/jclouds/internal/ClassMethodArgs.java b/core/src/main/java/org/jclouds/internal/ClassMethodArgs.java index 2a83622fae..e16b25c679 100644 --- a/core/src/main/java/org/jclouds/internal/ClassMethodArgs.java +++ b/core/src/main/java/org/jclouds/internal/ClassMethodArgs.java @@ -18,32 +18,91 @@ */ package org.jclouds.internal; +import static com.google.common.base.Objects.equal; import static com.google.common.base.Preconditions.checkNotNull; import java.lang.reflect.Method; -import java.util.Arrays; import org.jclouds.javax.annotation.Nullable; +import com.google.common.base.Objects; +import com.google.common.base.Objects.ToStringHelper; + /** * * @author Adrian Cole */ public class ClassMethodArgs { + public static Builder builder() { + return new ConcreteBuilder(); + } + + public Builder toBuilder() { + return builder().fromClassMethodArgs(this); + } + + private static class ConcreteBuilder extends Builder { + } + + public static abstract class Builder> { + private Class clazz; + private Method method; + private Object[] args; + + @SuppressWarnings("unchecked") + protected B self() { + return (B) this; + } + + /** + * @see ClassMethodArgs#getClazz() + */ + public B clazz(Class clazz) { + this.clazz = clazz; + return self(); + } + + /** + * @see ClassMethodArgs#getMethod() + */ + public B method(Method method) { + this.method = method; + return self(); + } + + /** + * @see ClassMethodArgs#getArgs() + */ + public B args(Object[] args) { + this.args = args; + return self(); + } + + public ClassMethodArgs build() { + return new ClassMethodArgs(this); + } + + public B fromClassMethodArgs(ClassMethodArgs in) { + return clazz(in.getClazz()).method(in.getMethod()).args(in.getArgs()); + } + } + + private final Class clazz; private final Method method; private final Object[] args; - private final Class asyncClass; - public ClassMethodArgs(Class asyncClass, Method method, @Nullable Object[] args) { - this.asyncClass = checkNotNull(asyncClass, "asyncClass"); + public ClassMethodArgs(Builder builder) { + this(builder.clazz, builder.method, builder.args); + } + + public ClassMethodArgs(Class clazz, Method method, @Nullable Object[] args) { + this.clazz = checkNotNull(clazz, "clazz"); this.method = checkNotNull(method, "method"); this.args = args; } - @Override - public String toString() { - return "[class=" + asyncClass.getSimpleName() + ", method=" + method.getName() + ", args=" - + Arrays.toString(args) + "]"; + public Class getClazz() { + return clazz; } public Method getMethod() { @@ -55,40 +114,26 @@ public class ClassMethodArgs { } @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + Arrays.hashCode(args); - result = prime * result + ((asyncClass == null) ? 0 : asyncClass.hashCode()); - result = prime * result + ((method == null) ? 0 : method.hashCode()); - return result; + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + ClassMethodArgs that = ClassMethodArgs.class.cast(o); + return equal(this.clazz, that.clazz) && equal(this.method, that.method) && equal(this.args, that.args); } @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - ClassMethodArgs other = (ClassMethodArgs) obj; - if (!Arrays.equals(args, other.args)) - return false; - if (asyncClass == null) { - if (other.asyncClass != null) - return false; - } else if (!asyncClass.equals(other.asyncClass)) - return false; - if (method == null) { - if (other.method != null) - return false; - } else if (!method.equals(other.method)) - return false; - return true; + public int hashCode() { + return Objects.hashCode(clazz, method, args); } - public Class getAsyncClass() { - return asyncClass; + @Override + public String toString() { + return string().toString(); + } + + protected ToStringHelper string() { + return Objects.toStringHelper("").add("clazz", clazz).add("method", method).add("args", args); } } \ No newline at end of file diff --git a/core/src/main/java/org/jclouds/internal/ClassMethodArgsAndReturnVal.java b/core/src/main/java/org/jclouds/internal/ClassMethodArgsAndReturnVal.java new file mode 100644 index 0000000000..deeeefcfa6 --- /dev/null +++ b/core/src/main/java/org/jclouds/internal/ClassMethodArgsAndReturnVal.java @@ -0,0 +1,105 @@ +/** + * 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.internal; + +import static com.google.common.base.Objects.equal; + +import java.lang.reflect.Method; + +import org.jclouds.javax.annotation.Nullable; + +import com.google.common.base.Objects; +import com.google.common.base.Objects.ToStringHelper; + +/** + * + * @author Adrian Cole + */ +public class ClassMethodArgsAndReturnVal extends ClassMethodArgs { + + public static Builder builder() { + return new ConcreteBuilder(); + } + + public Builder toBuilder() { + return builder().fromClassMethodArgsAndReturnVal(this); + } + + public static class Builder> extends ClassMethodArgs.Builder { + + private Object returnVal; + + /** + * @see ClassMethodArgsAndReturnVal#getReturnVal() + */ + public B returnVal(Object returnVal) { + this.returnVal = returnVal; + return self(); + } + + @Override + public ClassMethodArgsAndReturnVal build() { + return new ClassMethodArgsAndReturnVal(this); + } + + public B fromClassMethodArgsAndReturnVal(ClassMethodArgsAndReturnVal in) { + return fromClassMethodArgs(in).returnVal(in.getReturnVal()); + } + } + + private static class ConcreteBuilder extends Builder { + } + + private final Object returnVal; + + public ClassMethodArgsAndReturnVal(Class clazz, Method method, @Nullable Object[] args, Object returnVal) { + super(clazz, method, args); + this.returnVal = returnVal; + } + + public ClassMethodArgsAndReturnVal(Builder builder) { + super(builder); + this.returnVal = builder.returnVal; + } + + public Object getReturnVal() { + return returnVal; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + ClassMethodArgsAndReturnVal that = ClassMethodArgsAndReturnVal.class.cast(o); + return super.equals(that) && equal(this.returnVal, that.returnVal); + } + + @Override + public int hashCode() { + return Objects.hashCode(super.hashCode(), returnVal); + } + + @Override + public ToStringHelper string() { + return super.string().add("returnVal", returnVal); + } + +} \ No newline at end of file diff --git a/core/src/main/java/org/jclouds/rest/config/ClientProvider.java b/core/src/main/java/org/jclouds/rest/config/ClientProvider.java index bd55d77f9c..86bf395ecd 100644 --- a/core/src/main/java/org/jclouds/rest/config/ClientProvider.java +++ b/core/src/main/java/org/jclouds/rest/config/ClientProvider.java @@ -25,6 +25,7 @@ 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; @@ -61,7 +62,7 @@ public class ClientProvider implements Provider { @Singleton public S get() { A client = (A) injector.getInstance(Key.get(asyncClientType)); - Function> optionalConverter = injector.getInstance(Key.get(new TypeLiteral>>() { + Function> optionalConverter = injector.getInstance(Key.get(new TypeLiteral>>() { })); LoadingCache delegateMap = injector.getInstance(Key.get( new TypeLiteral>() { diff --git a/core/src/main/java/org/jclouds/rest/config/CreateClientForCaller.java b/core/src/main/java/org/jclouds/rest/config/CreateClientForCaller.java index 93ea413792..e17f150dfd 100644 --- a/core/src/main/java/org/jclouds/rest/config/CreateClientForCaller.java +++ b/core/src/main/java/org/jclouds/rest/config/CreateClientForCaller.java @@ -27,19 +27,20 @@ import javax.inject.Inject; import javax.inject.Named; import javax.inject.Provider; -import com.google.inject.Injector; -import com.google.inject.Key; -import com.google.inject.TypeLiteral; -import com.google.inject.name.Names; 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.LoadingCache; 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, @@ -69,7 +70,7 @@ public class CreateClientForCaller extends CacheLoader checkState(asyncClass != null, "configuration error, sync class " + syncClass + " not mapped to an async class"); Object asyncClient = asyncMap.get(from); checkState(asyncClient != null, "configuration error, sync client for " + from + " not found"); - Function> optionalConverter = injector.getInstance(Key.get(new TypeLiteral>>() { + Function> optionalConverter = injector.getInstance(Key.get(new TypeLiteral>>() { })); Map timeoutsMap = injector.getInstance(Key.get(new TypeLiteral>() { }, Names.named("TIMEOUTS"))); diff --git a/core/src/main/java/org/jclouds/rest/config/RestClientModule.java b/core/src/main/java/org/jclouds/rest/config/RestClientModule.java index a2bc447326..6622bfeed5 100644 --- a/core/src/main/java/org/jclouds/rest/config/RestClientModule.java +++ b/core/src/main/java/org/jclouds/rest/config/RestClientModule.java @@ -27,6 +27,7 @@ import javax.inject.Singleton; import org.jclouds.http.RequiresHttp; import org.jclouds.internal.ClassMethodArgs; +import org.jclouds.internal.ClassMethodArgsAndReturnVal; import org.jclouds.location.config.LocationModule; import org.jclouds.rest.AuthorizationException; import org.jclouds.rest.ConfiguresRestClient; @@ -80,7 +81,7 @@ public class RestClientModule extends AbstractModule { @SuppressWarnings({ "unchecked", "rawtypes" }) @Override protected void configure() { - bind(new TypeLiteral>>(){}).to(ImplicitOptionalConverter.class); + bind(new TypeLiteral>>(){}).to(ImplicitOptionalConverter.class); // this will help short circuit scenarios that can otherwise lock out users bind(new TypeLiteral>(){}).toInstance(authException); // Ensures the restcontext can be looked up without generic types. diff --git a/core/src/main/java/org/jclouds/rest/config/RestModule.java b/core/src/main/java/org/jclouds/rest/config/RestModule.java index 85b38690d4..d2f69384b3 100644 --- a/core/src/main/java/org/jclouds/rest/config/RestModule.java +++ b/core/src/main/java/org/jclouds/rest/config/RestModule.java @@ -40,9 +40,9 @@ import org.jclouds.rest.internal.RestAnnotationProcessor; import org.jclouds.rest.internal.SeedAnnotationCache; import com.google.common.base.Function; -import com.google.common.cache.LoadingCache; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; import com.google.common.collect.ImmutableMap; import com.google.inject.AbstractModule; import com.google.inject.Inject; @@ -101,7 +101,7 @@ public class RestModule extends AbstractModule { @SuppressWarnings( { "unchecked", "rawtypes" }) @Override public Object load(final ClassMethodArgs from) { - Class clazz = from.getAsyncClass(); + Class clazz = from.getClazz(); TypeLiteral typeLiteral = TypeLiteral.get(clazz); RestAnnotationProcessor util = (RestAnnotationProcessor) injector.getInstance(Key.get(TypeLiteral.get(Types .newParameterizedType(RestAnnotationProcessor.class, clazz)))); diff --git a/core/src/main/java/org/jclouds/rest/functions/AlwaysPresentImplicitOptionalConverter.java b/core/src/main/java/org/jclouds/rest/functions/AlwaysPresentImplicitOptionalConverter.java index 7580f00d50..48735ca157 100644 --- a/core/src/main/java/org/jclouds/rest/functions/AlwaysPresentImplicitOptionalConverter.java +++ b/core/src/main/java/org/jclouds/rest/functions/AlwaysPresentImplicitOptionalConverter.java @@ -18,17 +18,21 @@ */ package org.jclouds.rest.functions; +import org.jclouds.internal.ClassMethodArgsAndReturnVal; + +import com.google.common.annotations.Beta; import com.google.common.base.Optional; /** * * @author Adrian Cole */ +@Beta public class AlwaysPresentImplicitOptionalConverter implements ImplicitOptionalConverter { @Override - public Optional apply(Object input) { - return Optional.of(input); + public Optional apply(ClassMethodArgsAndReturnVal input) { + return Optional.of(input.getReturnVal()); } } \ No newline at end of file diff --git a/core/src/main/java/org/jclouds/rest/functions/ImplicitOptionalConverter.java b/core/src/main/java/org/jclouds/rest/functions/ImplicitOptionalConverter.java index d6e49d96b9..a80cf5c57a 100644 --- a/core/src/main/java/org/jclouds/rest/functions/ImplicitOptionalConverter.java +++ b/core/src/main/java/org/jclouds/rest/functions/ImplicitOptionalConverter.java @@ -18,15 +18,66 @@ */ package org.jclouds.rest.functions; +import org.jclouds.internal.ClassMethodArgsAndReturnVal; +import org.jclouds.rest.config.RestClientModule; + +import com.google.common.annotations.Beta; import com.google.common.base.Function; import com.google.common.base.Optional; import com.google.inject.ImplementedBy; /** + * When a client marked @Delegate is optional, the implementation of this is + * responsible for creating the optional object. + * + * For example. + * + *
+ * interface MyCloud {
+ *    @Delegate
+ *    Optional<KeyPairClient> getKeyPairExtensionForRegion(String region);
+ * }
+ * 
+ * + * The input object of type {@link ClassMethodArgsAndReturnVal} will include the + * following. + *
    + *
  1. the class declaring the method that returns optional: + * {@link ClassMethodArgsAndReturnVal#getClazz}; in the example above, + * {@code MyCloud}
  2. + *
  3. the method returning the optional: + * {@link ClassMethodArgsAndReturnVal#getMethod}; in the example above, + * {@code getKeyPairExtensionForRegion}
  4. + *
  5. the args passed to that method at runtime: + * {@link ClassMethodArgsAndReturnVal#getArgs}; for example {@code North}
  6. + *
  7. the rest client to be enclosed in the optional, should you choose to + * return it: {@link ClassMethodArgsAndReturnVal#getReturnVal}; in the example + * above, an implementation of {@code KeyPairClient}
  8. + *
+ * + * Using this context, your implementation of {@link ImplicitOptionalConverter} + * can perform whatever you need, when deciding if the the returnVal is present + * and available. Here are some ideas: + *
    + *
  • call a smoke test command
  • + *
  • look at the annotations on the class and compare those against a + * configuration switch enabling the extension
  • + *
  • inspect the health of the client, perhaps looking for error status
  • + *
  • call another api which can validate the feature can be presented
  • + *
+ * + * The {@link AlwaysPresentImplicitOptionalConverter default implementation} + * always returns present. To override this, add the following in your subclass + * override of {@link RestClientModule#configure} method: + * + *
+ * bind(ImplicitOptionalConverter.class).to(MyCustomOptionalConverter.class);
+ * 
* * @author Adrian Cole */ +@Beta @ImplementedBy(AlwaysPresentImplicitOptionalConverter.class) -public interface ImplicitOptionalConverter extends Function> { +public interface ImplicitOptionalConverter extends Function> { } \ No newline at end of file diff --git a/core/src/main/java/org/jclouds/rest/internal/AsyncRestClientProxy.java b/core/src/main/java/org/jclouds/rest/internal/AsyncRestClientProxy.java index f4cf02bd10..71ef6f089a 100644 --- a/core/src/main/java/org/jclouds/rest/internal/AsyncRestClientProxy.java +++ b/core/src/main/java/org/jclouds/rest/internal/AsyncRestClientProxy.java @@ -37,6 +37,7 @@ import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpResponse; import org.jclouds.http.TransformingHttpCommand; import org.jclouds.internal.ClassMethodArgs; +import org.jclouds.internal.ClassMethodArgsAndReturnVal; import org.jclouds.logging.Logger; import org.jclouds.rest.AuthorizationException; import org.jclouds.rest.InvocationContext; @@ -67,24 +68,31 @@ import com.google.inject.util.Types; *

* Particularly, this code delegates calls to other things. *

    - *
  1. if the method has a {@link Provides} annotation, it responds via a {@link Injector} lookup
  2. - *
  3. if the method has a {@link Delegate} annotation, it responds with an instance of interface - * set in returnVal, adding the current JAXrs annotations to whatever are on that class.
  4. + *
  5. if the method has a {@link Provides} annotation, it responds via a + * {@link Injector} lookup
  6. + *
  7. if the method has a {@link Delegate} annotation, it responds with an + * instance of interface set in returnVal, adding the current JAXrs annotations + * to whatever are on that class.
  8. *
      - *
    • ex. if the method with {@link Delegate} has a {@link Path} annotation, and the returnval - * interface also has {@link Path}, these values are combined.
    • + *
    • ex. if the method with {@link Delegate} has a {@link Path} annotation, + * and the returnval interface also has {@link Path}, these values are combined. + *
    • *
    - *
  9. if {@link RestAnnotationProcessor#delegationMap} contains a mapping for this, and the - * returnVal is properly assigned as a {@link ListenableFuture}, it responds with an http - * implementation.
  10. - *
  11. otherwise a RuntimeException is thrown with a message including: {@code method is intended - * solely to set constants}
  12. + *
  13. if {@link RestAnnotationProcessor#delegationMap} contains a mapping for + * this, and the returnVal is properly assigned as a {@link ListenableFuture}, + * it responds with an http implementation.
  14. + *
  15. otherwise a RuntimeException is thrown with a message including: + * {@code method is intended solely to set constants}
  16. *
* * @author Adrian Cole */ @Singleton public class AsyncRestClientProxy implements InvocationHandler { + public Class getDeclaring() { + return declaring; + } + private final Injector injector; private final RestAnnotationProcessor annotationProcessor; private final Class declaring; @@ -99,16 +107,17 @@ public class AsyncRestClientProxy implements InvocationHandler { @Resource protected Logger logger = Logger.NULL; - private final Function> optionalConverter; + private final Function> optionalConverter; private final LoadingCache delegateMap; @SuppressWarnings("unchecked") @Inject public AsyncRestClientProxy(Injector injector, Factory factory, RestAnnotationProcessor util, - TypeLiteral typeLiteral, @Named("async") LoadingCache delegateMap) { + TypeLiteral typeLiteral, @Named("async") LoadingCache delegateMap) { this.injector = injector; - this.optionalConverter = injector.getInstance(Key.get(new TypeLiteral>>() { - })); + this.optionalConverter = injector.getInstance(Key + .get(new TypeLiteral>>() { + })); this.annotationProcessor = util; this.declaring = (Class) typeLiteral.getRawType(); this.commandFactory = factory; @@ -139,27 +148,30 @@ public class AsyncRestClientProxy implements InvocationHandler { return createListenableFutureForHttpRequestMappedToMethodAndArgs(method, args); } else { throw new RuntimeException(String.format("Method is not annotated as either http or provider method: %s", - method)); + method)); } } public boolean isRestCall(Method method) { return annotationProcessor.getDelegateOrNull(method) != null - && ListenableFuture.class.isAssignableFrom(method.getReturnType()); + && ListenableFuture.class.isAssignableFrom(method.getReturnType()); } public Object propagateContextToDelegate(Method method, Object[] args) throws ExecutionException { Class asyncClass = Optionals2.returnTypeOrTypeOfOptional(method); - Object returnVal = delegateMap.get(new ClassMethodArgs(asyncClass, method, args)); - if (Optionals2.isReturnTypeOptional(method)){ - return optionalConverter.apply(returnVal); + ClassMethodArgs cma = new ClassMethodArgs(asyncClass, method, args); + Object returnVal = delegateMap.get(cma); + if (Optionals2.isReturnTypeOptional(method)) { + ClassMethodArgsAndReturnVal cmar = ClassMethodArgsAndReturnVal.builder().fromClassMethodArgs(cma) + .returnVal(returnVal).build(); + return optionalConverter.apply(cmar); } return returnVal; } public Object lookupValueFromGuice(Method method) { try { - //TODO: tidy + // TODO: tidy Type genericReturnType = method.getGenericReturnType(); try { Annotation qualifier = Iterables.find(ImmutableList.copyOf(method.getAnnotations()), isQualifierPresent); @@ -200,7 +212,7 @@ public class AsyncRestClientProxy implements InvocationHandler { // then, try looking via supplier binding = injector.getExistingBinding(Key.get(Types.newParameterizedType(Supplier.class, genericReturnType), - qualifier)); + qualifier)); if (binding != null) return Supplier.class.cast(binding.getProvider().get()).get(); @@ -208,13 +220,13 @@ public class AsyncRestClientProxy implements InvocationHandler { return injector.getInstance(Key.get(genericReturnType, qualifier)); } - @SuppressWarnings( { "unchecked", "rawtypes" }) + @SuppressWarnings({ "unchecked", "rawtypes" }) private ListenableFuture createListenableFutureForHttpRequestMappedToMethodAndArgs(Method method, Object[] args) - throws ExecutionException { + throws ExecutionException { method = annotationProcessor.getDelegateOrNull(method); logger.trace("Converting %s.%s", declaring.getSimpleName(), method.getName()); Function exceptionParser = annotationProcessor - .createExceptionParserOrThrowResourceNotFoundOn404IfNoAnnotation(method); + .createExceptionParserOrThrowResourceNotFoundOn404IfNoAnnotation(method); // in case there is an exception creating the request, we should at least // pass in args if (exceptionParser instanceof InvocationContext) { @@ -230,7 +242,7 @@ public class AsyncRestClientProxy implements InvocationHandler { Function transformer = annotationProcessor.createResponseParser(method, request); logger.trace("Response from %s.%s is parsed by %s", declaring.getSimpleName(), method.getName(), transformer - .getClass().getSimpleName()); + .getClass().getSimpleName()); logger.debug("Invoking %s.%s", declaring.getSimpleName(), method.getName()); result = commandFactory.create(request, transformer).execute(); @@ -251,7 +263,7 @@ public class AsyncRestClientProxy implements InvocationHandler { if (exceptionParser != null) { logger.trace("Exceptions from %s.%s are parsed by %s", declaring.getSimpleName(), method.getName(), - exceptionParser.getClass().getSimpleName()); + exceptionParser.getClass().getSimpleName()); result = new ExceptionParsingListenableFuture(result, exceptionParser); } return result; diff --git a/core/src/main/java/org/jclouds/rest/internal/RestAnnotationProcessor.java b/core/src/main/java/org/jclouds/rest/internal/RestAnnotationProcessor.java index 6b5e83b495..a8dd2a78ff 100644 --- a/core/src/main/java/org/jclouds/rest/internal/RestAnnotationProcessor.java +++ b/core/src/main/java/org/jclouds/rest/internal/RestAnnotationProcessor.java @@ -47,9 +47,9 @@ import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import java.util.SortedSet; -import java.util.Map.Entry; import java.util.concurrent.ExecutionException; import javax.annotation.Resource; @@ -77,6 +77,7 @@ import org.jclouds.http.HttpUtils; import org.jclouds.http.functions.ParseFirstJsonValueNamed; import org.jclouds.http.functions.ParseJson; import org.jclouds.http.functions.ParseSax; +import org.jclouds.http.functions.ParseSax.HandlerWithResult; import org.jclouds.http.functions.ParseURIFromListOrLocationHeaderIf20x; import org.jclouds.http.functions.ParseXMLWithJAXB; import org.jclouds.http.functions.ReleasePayloadAndReturn; @@ -84,7 +85,6 @@ import org.jclouds.http.functions.ReturnInputStream; import org.jclouds.http.functions.ReturnStringIf2xx; import org.jclouds.http.functions.ReturnTrueIf2xx; import org.jclouds.http.functions.UnwrapOnlyJsonValue; -import org.jclouds.http.functions.ParseSax.HandlerWithResult; import org.jclouds.http.options.HttpRequestOptions; import org.jclouds.http.utils.ModifyRequest; import org.jclouds.internal.ClassMethodArgs; @@ -147,12 +147,12 @@ import com.google.common.cache.LoadingCache; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSet.Builder; import com.google.common.collect.Iterables; import com.google.common.collect.LinkedHashMultimap; import com.google.common.collect.LinkedListMultimap; import com.google.common.collect.Lists; import com.google.common.collect.Multimap; -import com.google.common.collect.ImmutableSet.Builder; import com.google.common.util.concurrent.ListenableFuture; import com.google.inject.Inject; import com.google.inject.Injector; diff --git a/core/src/test/java/org/jclouds/rest/internal/RestAnnotationProcessorTest.java b/core/src/test/java/org/jclouds/rest/internal/RestAnnotationProcessorTest.java index d3c90b7065..d30a5a1894 100644 --- a/core/src/test/java/org/jclouds/rest/internal/RestAnnotationProcessorTest.java +++ b/core/src/test/java/org/jclouds/rest/internal/RestAnnotationProcessorTest.java @@ -96,6 +96,7 @@ import org.jclouds.http.internal.PayloadEnclosingImpl; import org.jclouds.http.options.BaseHttpRequestOptions; import org.jclouds.http.options.GetOptions; import org.jclouds.http.options.HttpRequestOptions; +import org.jclouds.internal.ClassMethodArgsAndReturnVal; import org.jclouds.io.Payload; import org.jclouds.io.PayloadEnclosing; import org.jclouds.io.Payloads; @@ -355,7 +356,7 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest { bind(ImplicitOptionalConverter.class).toInstance(new ImplicitOptionalConverter() { @Override - public Optional apply(Object input) { + public Optional apply(ClassMethodArgsAndReturnVal input) { return Optional.absent(); }