Issue 870: added more context to optionalconverter

This commit is contained in:
Adrian Cole 2012-03-16 02:30:19 -07:00
parent 16a44b2e02
commit 3d0d0c6094
12 changed files with 315 additions and 86 deletions

View File

@ -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> T proxy(Function<Object, Optional<Object>> optionalConverter, Class<T> clazz, Object async,
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 {
@ -62,7 +63,7 @@ public class SyncProxy implements InvocationHandler {
new SyncProxy(optionalConverter, clazz, async, delegateMap, sync2Async, timeouts));
}
private final Function<Object, Optional<Object>> optionalConverter;
private final Function<ClassMethodArgsAndReturnVal, Optional<Object>> optionalConverter;
private final Object delegate;
private final Class<?> declaring;
private final Map<Method, Method> methodMap;
@ -73,7 +74,7 @@ public class SyncProxy implements InvocationHandler {
private static final Set<Method> objectMethods = ImmutableSet.copyOf(Object.class.getMethods());
@Inject
private SyncProxy(Function<Object, Optional<Object>> optionalConverter, Class<?> declaring, Object async,
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)
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<String,Long> 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)) {

View File

@ -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<ConcreteBuilder> {
}
public static abstract class Builder<B extends Builder<B>> {
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);
}
}

View File

@ -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<B extends Builder<B>> extends ClassMethodArgs.Builder<B> {
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<ConcreteBuilder> {
}
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);
}
}

View File

@ -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<S, A> implements Provider<S> {
@Singleton
public S get() {
A client = (A) injector.getInstance(Key.get(asyncClientType));
Function<Object, Optional<Object>> optionalConverter = injector.getInstance(Key.get(new TypeLiteral<Function<Object, Optional<Object>>>() {
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>>() {

View File

@ -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<ClassMethodArgs, Object>
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<Object, Optional<Object>> optionalConverter = injector.getInstance(Key.get(new TypeLiteral<Function<Object, Optional<Object>>>() {
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")));

View File

@ -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<S, A> extends AbstractModule {
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
protected void configure() {
bind(new TypeLiteral<Function<Object, Optional<Object>>>(){}).to(ImplicitOptionalConverter.class);
bind(new TypeLiteral<Function<ClassMethodArgsAndReturnVal, Optional<Object>>>(){}).to(ImplicitOptionalConverter.class);
// this will help short circuit scenarios that can otherwise lock out users
bind(new TypeLiteral<AtomicReference<AuthorizationException>>(){}).toInstance(authException);
// Ensures the restcontext can be looked up without generic types.

View File

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

View File

@ -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<Object> apply(Object input) {
return Optional.of(input);
public Optional<Object> apply(ClassMethodArgsAndReturnVal input) {
return Optional.of(input.getReturnVal());
}
}

View File

@ -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.
*
* <pre>
* interface MyCloud {
* &#064;Delegate
* Optional&lt;KeyPairClient&gt; getKeyPairExtensionForRegion(String region);
* }
* </pre>
*
* The input object of type {@link ClassMethodArgsAndReturnVal} will include the
* following.
* <ol>
* <li>the class declaring the method that returns optional:
* {@link ClassMethodArgsAndReturnVal#getClazz}; in the example above,
* {@code MyCloud}</li>
* <li>the method returning the optional:
* {@link ClassMethodArgsAndReturnVal#getMethod}; in the example above,
* {@code getKeyPairExtensionForRegion}</li>
* <li>the args passed to that method at runtime:
* {@link ClassMethodArgsAndReturnVal#getArgs}; for example {@code North}</li>
* <li>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}</li>
* </ol>
*
* 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:
* <ul>
* <li>call a smoke test command</li>
* <li>look at the annotations on the class and compare those against a
* configuration switch enabling the extension</li>
* <li>inspect the health of the client, perhaps looking for error status</li>
* <li>call another api which can validate the feature can be presented</li>
* </ul>
*
* The {@link AlwaysPresentImplicitOptionalConverter default implementation}
* always returns present. To override this, add the following in your subclass
* override of {@link RestClientModule#configure} method:
*
* <pre>
* bind(ImplicitOptionalConverter.class).to(MyCustomOptionalConverter.class);
* </pre>
*
* @author Adrian Cole
*/
@Beta
@ImplementedBy(AlwaysPresentImplicitOptionalConverter.class)
public interface ImplicitOptionalConverter extends Function<Object, Optional<Object>> {
public interface ImplicitOptionalConverter extends Function<ClassMethodArgsAndReturnVal, Optional<Object>> {
}

View File

@ -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;
* <p/>
* Particularly, this code delegates calls to other things.
* <ol>
* <li>if the method has a {@link Provides} annotation, it responds via a {@link Injector} lookup</li>
* <li>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.</li>
* <li>if the method has a {@link Provides} annotation, it responds via a
* {@link Injector} lookup</li>
* <li>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.</li>
* <ul>
* <li>ex. if the method with {@link Delegate} has a {@link Path} annotation, and the returnval
* interface also has {@link Path}, these values are combined.</li>
* <li>ex. if the method with {@link Delegate} has a {@link Path} annotation,
* and the returnval interface also has {@link Path}, these values are combined.
* </li>
* </ul>
* <li>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.</li>
* <li>otherwise a RuntimeException is thrown with a message including: {@code method is intended
* solely to set constants}</li>
* <li>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.</li>
* <li>otherwise a RuntimeException is thrown with a message including:
* {@code method is intended solely to set constants}</li>
* </ol>
*
* @author Adrian Cole
*/
@Singleton
public class AsyncRestClientProxy<T> implements InvocationHandler {
public Class<T> getDeclaring() {
return declaring;
}
private final Injector injector;
private final RestAnnotationProcessor<T> annotationProcessor;
private final Class<T> declaring;
@ -99,7 +107,7 @@ public class AsyncRestClientProxy<T> implements InvocationHandler {
@Resource
protected Logger logger = Logger.NULL;
private final Function<Object, Optional<Object>> optionalConverter;
private final Function<ClassMethodArgsAndReturnVal, Optional<Object>> optionalConverter;
private final LoadingCache<ClassMethodArgs, Object> delegateMap;
@SuppressWarnings("unchecked")
@ -107,7 +115,8 @@ public class AsyncRestClientProxy<T> implements InvocationHandler {
public AsyncRestClientProxy(Injector injector, Factory factory, RestAnnotationProcessor<T> util,
TypeLiteral<T> typeLiteral, @Named("async") LoadingCache<ClassMethodArgs, Object> delegateMap) {
this.injector = injector;
this.optionalConverter = injector.getInstance(Key.get(new TypeLiteral<Function<Object, Optional<Object>>>() {
this.optionalConverter = injector.getInstance(Key
.get(new TypeLiteral<Function<ClassMethodArgsAndReturnVal, Optional<Object>>>() {
}));
this.annotationProcessor = util;
this.declaring = (Class<T>) typeLiteral.getRawType();
@ -150,9 +159,12 @@ public class AsyncRestClientProxy<T> implements InvocationHandler {
public Object propagateContextToDelegate(Method method, Object[] args) throws ExecutionException {
Class<?> asyncClass = Optionals2.returnTypeOrTypeOfOptional(method);
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;
}

View File

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

View File

@ -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<Object> apply(Object input) {
public Optional<Object> apply(ClassMethodArgsAndReturnVal input) {
return Optional.absent();
}