mirror of https://github.com/apache/jclouds.git
Issue 870: added more context to optionalconverter
This commit is contained in:
parent
16a44b2e02
commit
3d0d0c6094
|
@ -34,6 +34,7 @@ import javax.inject.Named;
|
||||||
|
|
||||||
import org.jclouds.concurrent.Timeout;
|
import org.jclouds.concurrent.Timeout;
|
||||||
import org.jclouds.internal.ClassMethodArgs;
|
import org.jclouds.internal.ClassMethodArgs;
|
||||||
|
import org.jclouds.internal.ClassMethodArgsAndReturnVal;
|
||||||
import org.jclouds.rest.annotations.Delegate;
|
import org.jclouds.rest.annotations.Delegate;
|
||||||
import org.jclouds.util.Optionals2;
|
import org.jclouds.util.Optionals2;
|
||||||
import org.jclouds.util.Throwables2;
|
import org.jclouds.util.Throwables2;
|
||||||
|
@ -54,7 +55,7 @@ import com.google.inject.ProvisionException;
|
||||||
public class SyncProxy implements InvocationHandler {
|
public class SyncProxy implements InvocationHandler {
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@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,
|
@Named("sync") LoadingCache<ClassMethodArgs, Object> delegateMap,
|
||||||
Map<Class<?>, Class<?>> sync2Async, Map<String, Long> timeouts) throws IllegalArgumentException, SecurityException,
|
Map<Class<?>, Class<?>> sync2Async, Map<String, Long> timeouts) throws IllegalArgumentException, SecurityException,
|
||||||
NoSuchMethodException {
|
NoSuchMethodException {
|
||||||
|
@ -62,7 +63,7 @@ public class SyncProxy implements InvocationHandler {
|
||||||
new SyncProxy(optionalConverter, clazz, async, delegateMap, sync2Async, timeouts));
|
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 Object delegate;
|
||||||
private final Class<?> declaring;
|
private final Class<?> declaring;
|
||||||
private final Map<Method, Method> methodMap;
|
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());
|
private static final Set<Method> objectMethods = ImmutableSet.copyOf(Object.class.getMethods());
|
||||||
|
|
||||||
@Inject
|
@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<?>,
|
@Named("sync") LoadingCache<ClassMethodArgs, Object> delegateMap, Map<Class<?>,
|
||||||
Class<?>> sync2Async, final Map<String, Long> timeouts)
|
Class<?>> sync2Async, final Map<String, Long> timeouts)
|
||||||
throws SecurityException, NoSuchMethodException {
|
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) {
|
private Long getTimeout(Method method, long typeNanos, final Map<String,Long> timeouts) {
|
||||||
Long timeout = overrideTimeout(method, timeouts);
|
Long timeout = overrideTimeout(method, timeouts);
|
||||||
if (timeout == null && method.isAnnotationPresent(Timeout.class)) {
|
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
|
// pass any parameters necessary to get a relevant instance of that async class
|
||||||
// ex. getClientForRegion("north") might return an instance whose endpoint is
|
// ex. getClientForRegion("north") might return an instance whose endpoint is
|
||||||
// different that "south"
|
// 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)){
|
if (Optionals2.isReturnTypeOptional(method)){
|
||||||
return optionalConverter.apply(returnVal);
|
ClassMethodArgsAndReturnVal cmar = ClassMethodArgsAndReturnVal.builder().fromClassMethodArgs(cma)
|
||||||
|
.returnVal(returnVal).build();
|
||||||
|
return optionalConverter.apply(cmar);
|
||||||
}
|
}
|
||||||
return returnVal;
|
return returnVal;
|
||||||
} else if (syncMethodMap.containsKey(method)) {
|
} else if (syncMethodMap.containsKey(method)) {
|
||||||
|
|
|
@ -18,32 +18,91 @@
|
||||||
*/
|
*/
|
||||||
package org.jclouds.internal;
|
package org.jclouds.internal;
|
||||||
|
|
||||||
|
import static com.google.common.base.Objects.equal;
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
import org.jclouds.javax.annotation.Nullable;
|
import org.jclouds.javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.google.common.base.Objects;
|
||||||
|
import com.google.common.base.Objects.ToStringHelper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
*/
|
*/
|
||||||
public class ClassMethodArgs {
|
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 Method method;
|
||||||
private final Object[] args;
|
private final Object[] args;
|
||||||
private final Class<?> asyncClass;
|
|
||||||
|
|
||||||
public ClassMethodArgs(Class<?> asyncClass, Method method, @Nullable Object[] args) {
|
public ClassMethodArgs(Builder<?> builder) {
|
||||||
this.asyncClass = checkNotNull(asyncClass, "asyncClass");
|
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.method = checkNotNull(method, "method");
|
||||||
this.args = args;
|
this.args = args;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public Class<?> getClazz() {
|
||||||
public String toString() {
|
return clazz;
|
||||||
return "[class=" + asyncClass.getSimpleName() + ", method=" + method.getName() + ", args="
|
|
||||||
+ Arrays.toString(args) + "]";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Method getMethod() {
|
public Method getMethod() {
|
||||||
|
@ -55,40 +114,26 @@ public class ClassMethodArgs {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public boolean equals(Object o) {
|
||||||
final int prime = 31;
|
if (this == o)
|
||||||
int result = 1;
|
return true;
|
||||||
result = prime * result + Arrays.hashCode(args);
|
if (o == null || getClass() != o.getClass())
|
||||||
result = prime * result + ((asyncClass == null) ? 0 : asyncClass.hashCode());
|
return false;
|
||||||
result = prime * result + ((method == null) ? 0 : method.hashCode());
|
ClassMethodArgs that = ClassMethodArgs.class.cast(o);
|
||||||
return result;
|
return equal(this.clazz, that.clazz) && equal(this.method, that.method) && equal(this.args, that.args);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public int hashCode() {
|
||||||
if (this == obj)
|
return Objects.hashCode(clazz, method, args);
|
||||||
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 Class<?> getAsyncClass() {
|
@Override
|
||||||
return asyncClass;
|
public String toString() {
|
||||||
|
return string().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ToStringHelper string() {
|
||||||
|
return Objects.toStringHelper("").add("clazz", clazz).add("method", method).add("args", args);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -25,6 +25,7 @@ import javax.inject.Singleton;
|
||||||
|
|
||||||
import org.jclouds.concurrent.internal.SyncProxy;
|
import org.jclouds.concurrent.internal.SyncProxy;
|
||||||
import org.jclouds.internal.ClassMethodArgs;
|
import org.jclouds.internal.ClassMethodArgs;
|
||||||
|
import org.jclouds.internal.ClassMethodArgsAndReturnVal;
|
||||||
|
|
||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
import com.google.common.base.Optional;
|
import com.google.common.base.Optional;
|
||||||
|
@ -61,7 +62,7 @@ public class ClientProvider<S, A> implements Provider<S> {
|
||||||
@Singleton
|
@Singleton
|
||||||
public S get() {
|
public S get() {
|
||||||
A client = (A) injector.getInstance(Key.get(asyncClientType));
|
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(
|
LoadingCache<ClassMethodArgs, Object> delegateMap = injector.getInstance(Key.get(
|
||||||
new TypeLiteral<LoadingCache<ClassMethodArgs, Object>>() {
|
new TypeLiteral<LoadingCache<ClassMethodArgs, Object>>() {
|
||||||
|
|
|
@ -27,19 +27,20 @@ import javax.inject.Inject;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import javax.inject.Provider;
|
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.concurrent.internal.SyncProxy;
|
||||||
import org.jclouds.internal.ClassMethodArgs;
|
import org.jclouds.internal.ClassMethodArgs;
|
||||||
|
import org.jclouds.internal.ClassMethodArgsAndReturnVal;
|
||||||
import org.jclouds.util.Optionals2;
|
import org.jclouds.util.Optionals2;
|
||||||
|
|
||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
import com.google.common.base.Optional;
|
import com.google.common.base.Optional;
|
||||||
import com.google.common.base.Throwables;
|
import com.google.common.base.Throwables;
|
||||||
import com.google.common.cache.LoadingCache;
|
|
||||||
import com.google.common.cache.CacheLoader;
|
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 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");
|
checkState(asyncClass != null, "configuration error, sync class " + syncClass + " not mapped to an async class");
|
||||||
Object asyncClient = asyncMap.get(from);
|
Object asyncClient = asyncMap.get(from);
|
||||||
checkState(asyncClient != null, "configuration error, sync client for " + from + " not found");
|
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>>() {
|
Map<String, Long> timeoutsMap = injector.getInstance(Key.get(new TypeLiteral<Map<String, Long>>() {
|
||||||
}, Names.named("TIMEOUTS")));
|
}, Names.named("TIMEOUTS")));
|
||||||
|
|
|
@ -27,6 +27,7 @@ import javax.inject.Singleton;
|
||||||
|
|
||||||
import org.jclouds.http.RequiresHttp;
|
import org.jclouds.http.RequiresHttp;
|
||||||
import org.jclouds.internal.ClassMethodArgs;
|
import org.jclouds.internal.ClassMethodArgs;
|
||||||
|
import org.jclouds.internal.ClassMethodArgsAndReturnVal;
|
||||||
import org.jclouds.location.config.LocationModule;
|
import org.jclouds.location.config.LocationModule;
|
||||||
import org.jclouds.rest.AuthorizationException;
|
import org.jclouds.rest.AuthorizationException;
|
||||||
import org.jclouds.rest.ConfiguresRestClient;
|
import org.jclouds.rest.ConfiguresRestClient;
|
||||||
|
@ -80,7 +81,7 @@ public class RestClientModule<S, A> extends AbstractModule {
|
||||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||||
@Override
|
@Override
|
||||||
protected void configure() {
|
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
|
// this will help short circuit scenarios that can otherwise lock out users
|
||||||
bind(new TypeLiteral<AtomicReference<AuthorizationException>>(){}).toInstance(authException);
|
bind(new TypeLiteral<AtomicReference<AuthorizationException>>(){}).toInstance(authException);
|
||||||
// Ensures the restcontext can be looked up without generic types.
|
// Ensures the restcontext can be looked up without generic types.
|
||||||
|
|
|
@ -40,9 +40,9 @@ import org.jclouds.rest.internal.RestAnnotationProcessor;
|
||||||
import org.jclouds.rest.internal.SeedAnnotationCache;
|
import org.jclouds.rest.internal.SeedAnnotationCache;
|
||||||
|
|
||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
import com.google.common.cache.LoadingCache;
|
|
||||||
import com.google.common.cache.CacheBuilder;
|
import com.google.common.cache.CacheBuilder;
|
||||||
import com.google.common.cache.CacheLoader;
|
import com.google.common.cache.CacheLoader;
|
||||||
|
import com.google.common.cache.LoadingCache;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.inject.AbstractModule;
|
import com.google.inject.AbstractModule;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
|
@ -101,7 +101,7 @@ public class RestModule extends AbstractModule {
|
||||||
@SuppressWarnings( { "unchecked", "rawtypes" })
|
@SuppressWarnings( { "unchecked", "rawtypes" })
|
||||||
@Override
|
@Override
|
||||||
public Object load(final ClassMethodArgs from) {
|
public Object load(final ClassMethodArgs from) {
|
||||||
Class clazz = from.getAsyncClass();
|
Class clazz = from.getClazz();
|
||||||
TypeLiteral typeLiteral = TypeLiteral.get(clazz);
|
TypeLiteral typeLiteral = TypeLiteral.get(clazz);
|
||||||
RestAnnotationProcessor util = (RestAnnotationProcessor) injector.getInstance(Key.get(TypeLiteral.get(Types
|
RestAnnotationProcessor util = (RestAnnotationProcessor) injector.getInstance(Key.get(TypeLiteral.get(Types
|
||||||
.newParameterizedType(RestAnnotationProcessor.class, clazz))));
|
.newParameterizedType(RestAnnotationProcessor.class, clazz))));
|
||||||
|
|
|
@ -18,17 +18,21 @@
|
||||||
*/
|
*/
|
||||||
package org.jclouds.rest.functions;
|
package org.jclouds.rest.functions;
|
||||||
|
|
||||||
|
import org.jclouds.internal.ClassMethodArgsAndReturnVal;
|
||||||
|
|
||||||
|
import com.google.common.annotations.Beta;
|
||||||
import com.google.common.base.Optional;
|
import com.google.common.base.Optional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
*/
|
*/
|
||||||
|
@Beta
|
||||||
public class AlwaysPresentImplicitOptionalConverter implements ImplicitOptionalConverter {
|
public class AlwaysPresentImplicitOptionalConverter implements ImplicitOptionalConverter {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<Object> apply(Object input) {
|
public Optional<Object> apply(ClassMethodArgsAndReturnVal input) {
|
||||||
return Optional.of(input);
|
return Optional.of(input.getReturnVal());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -18,15 +18,66 @@
|
||||||
*/
|
*/
|
||||||
package org.jclouds.rest.functions;
|
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.Function;
|
||||||
import com.google.common.base.Optional;
|
import com.google.common.base.Optional;
|
||||||
import com.google.inject.ImplementedBy;
|
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 {
|
||||||
|
* @Delegate
|
||||||
|
* Optional<KeyPairClient> 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
|
* @author Adrian Cole
|
||||||
*/
|
*/
|
||||||
|
@Beta
|
||||||
@ImplementedBy(AlwaysPresentImplicitOptionalConverter.class)
|
@ImplementedBy(AlwaysPresentImplicitOptionalConverter.class)
|
||||||
public interface ImplicitOptionalConverter extends Function<Object, Optional<Object>> {
|
public interface ImplicitOptionalConverter extends Function<ClassMethodArgsAndReturnVal, Optional<Object>> {
|
||||||
|
|
||||||
}
|
}
|
|
@ -37,6 +37,7 @@ import org.jclouds.http.HttpRequest;
|
||||||
import org.jclouds.http.HttpResponse;
|
import org.jclouds.http.HttpResponse;
|
||||||
import org.jclouds.http.TransformingHttpCommand;
|
import org.jclouds.http.TransformingHttpCommand;
|
||||||
import org.jclouds.internal.ClassMethodArgs;
|
import org.jclouds.internal.ClassMethodArgs;
|
||||||
|
import org.jclouds.internal.ClassMethodArgsAndReturnVal;
|
||||||
import org.jclouds.logging.Logger;
|
import org.jclouds.logging.Logger;
|
||||||
import org.jclouds.rest.AuthorizationException;
|
import org.jclouds.rest.AuthorizationException;
|
||||||
import org.jclouds.rest.InvocationContext;
|
import org.jclouds.rest.InvocationContext;
|
||||||
|
@ -67,24 +68,31 @@ import com.google.inject.util.Types;
|
||||||
* <p/>
|
* <p/>
|
||||||
* Particularly, this code delegates calls to other things.
|
* Particularly, this code delegates calls to other things.
|
||||||
* <ol>
|
* <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 Provides} annotation, it responds via a
|
||||||
* <li>if the method has a {@link Delegate} annotation, it responds with an instance of interface
|
* {@link Injector} lookup</li>
|
||||||
* set in returnVal, adding the current JAXrs annotations to whatever are on that class.</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>
|
* <ul>
|
||||||
* <li>ex. if the method with {@link Delegate} has a {@link Path} annotation, and the returnval
|
* <li>ex. if the method with {@link Delegate} has a {@link Path} annotation,
|
||||||
* interface also has {@link Path}, these values are combined.</li>
|
* and the returnval interface also has {@link Path}, these values are combined.
|
||||||
|
* </li>
|
||||||
* </ul>
|
* </ul>
|
||||||
* <li>if {@link RestAnnotationProcessor#delegationMap} contains a mapping for this, and the
|
* <li>if {@link RestAnnotationProcessor#delegationMap} contains a mapping for
|
||||||
* returnVal is properly assigned as a {@link ListenableFuture}, it responds with an http
|
* this, and the returnVal is properly assigned as a {@link ListenableFuture},
|
||||||
* implementation.</li>
|
* it responds with an http implementation.</li>
|
||||||
* <li>otherwise a RuntimeException is thrown with a message including: {@code method is intended
|
* <li>otherwise a RuntimeException is thrown with a message including:
|
||||||
* solely to set constants}</li>
|
* {@code method is intended solely to set constants}</li>
|
||||||
* </ol>
|
* </ol>
|
||||||
*
|
*
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
*/
|
*/
|
||||||
@Singleton
|
@Singleton
|
||||||
public class AsyncRestClientProxy<T> implements InvocationHandler {
|
public class AsyncRestClientProxy<T> implements InvocationHandler {
|
||||||
|
public Class<T> getDeclaring() {
|
||||||
|
return declaring;
|
||||||
|
}
|
||||||
|
|
||||||
private final Injector injector;
|
private final Injector injector;
|
||||||
private final RestAnnotationProcessor<T> annotationProcessor;
|
private final RestAnnotationProcessor<T> annotationProcessor;
|
||||||
private final Class<T> declaring;
|
private final Class<T> declaring;
|
||||||
|
@ -99,16 +107,17 @@ public class AsyncRestClientProxy<T> implements InvocationHandler {
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
protected Logger logger = Logger.NULL;
|
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;
|
private final LoadingCache<ClassMethodArgs, Object> delegateMap;
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@Inject
|
@Inject
|
||||||
public AsyncRestClientProxy(Injector injector, Factory factory, RestAnnotationProcessor<T> util,
|
public AsyncRestClientProxy(Injector injector, Factory factory, RestAnnotationProcessor<T> util,
|
||||||
TypeLiteral<T> typeLiteral, @Named("async") LoadingCache<ClassMethodArgs, Object> delegateMap) {
|
TypeLiteral<T> typeLiteral, @Named("async") LoadingCache<ClassMethodArgs, Object> delegateMap) {
|
||||||
this.injector = injector;
|
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.annotationProcessor = util;
|
||||||
this.declaring = (Class<T>) typeLiteral.getRawType();
|
this.declaring = (Class<T>) typeLiteral.getRawType();
|
||||||
this.commandFactory = factory;
|
this.commandFactory = factory;
|
||||||
|
@ -139,27 +148,30 @@ public class AsyncRestClientProxy<T> implements InvocationHandler {
|
||||||
return createListenableFutureForHttpRequestMappedToMethodAndArgs(method, args);
|
return createListenableFutureForHttpRequestMappedToMethodAndArgs(method, args);
|
||||||
} else {
|
} else {
|
||||||
throw new RuntimeException(String.format("Method is not annotated as either http or provider method: %s",
|
throw new RuntimeException(String.format("Method is not annotated as either http or provider method: %s",
|
||||||
method));
|
method));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isRestCall(Method method) {
|
public boolean isRestCall(Method method) {
|
||||||
return annotationProcessor.getDelegateOrNull(method) != null
|
return annotationProcessor.getDelegateOrNull(method) != null
|
||||||
&& ListenableFuture.class.isAssignableFrom(method.getReturnType());
|
&& ListenableFuture.class.isAssignableFrom(method.getReturnType());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object propagateContextToDelegate(Method method, Object[] args) throws ExecutionException {
|
public Object propagateContextToDelegate(Method method, Object[] args) throws ExecutionException {
|
||||||
Class<?> asyncClass = Optionals2.returnTypeOrTypeOfOptional(method);
|
Class<?> asyncClass = Optionals2.returnTypeOrTypeOfOptional(method);
|
||||||
Object returnVal = delegateMap.get(new ClassMethodArgs(asyncClass, method, args));
|
ClassMethodArgs cma = new ClassMethodArgs(asyncClass, method, args);
|
||||||
if (Optionals2.isReturnTypeOptional(method)){
|
Object returnVal = delegateMap.get(cma);
|
||||||
return optionalConverter.apply(returnVal);
|
if (Optionals2.isReturnTypeOptional(method)) {
|
||||||
|
ClassMethodArgsAndReturnVal cmar = ClassMethodArgsAndReturnVal.builder().fromClassMethodArgs(cma)
|
||||||
|
.returnVal(returnVal).build();
|
||||||
|
return optionalConverter.apply(cmar);
|
||||||
}
|
}
|
||||||
return returnVal;
|
return returnVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object lookupValueFromGuice(Method method) {
|
public Object lookupValueFromGuice(Method method) {
|
||||||
try {
|
try {
|
||||||
//TODO: tidy
|
// TODO: tidy
|
||||||
Type genericReturnType = method.getGenericReturnType();
|
Type genericReturnType = method.getGenericReturnType();
|
||||||
try {
|
try {
|
||||||
Annotation qualifier = Iterables.find(ImmutableList.copyOf(method.getAnnotations()), isQualifierPresent);
|
Annotation qualifier = Iterables.find(ImmutableList.copyOf(method.getAnnotations()), isQualifierPresent);
|
||||||
|
@ -200,7 +212,7 @@ public class AsyncRestClientProxy<T> implements InvocationHandler {
|
||||||
|
|
||||||
// then, try looking via supplier
|
// then, try looking via supplier
|
||||||
binding = injector.getExistingBinding(Key.get(Types.newParameterizedType(Supplier.class, genericReturnType),
|
binding = injector.getExistingBinding(Key.get(Types.newParameterizedType(Supplier.class, genericReturnType),
|
||||||
qualifier));
|
qualifier));
|
||||||
if (binding != null)
|
if (binding != null)
|
||||||
return Supplier.class.cast(binding.getProvider().get()).get();
|
return Supplier.class.cast(binding.getProvider().get()).get();
|
||||||
|
|
||||||
|
@ -208,13 +220,13 @@ public class AsyncRestClientProxy<T> implements InvocationHandler {
|
||||||
return injector.getInstance(Key.get(genericReturnType, qualifier));
|
return injector.getInstance(Key.get(genericReturnType, qualifier));
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings( { "unchecked", "rawtypes" })
|
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||||
private ListenableFuture<?> createListenableFutureForHttpRequestMappedToMethodAndArgs(Method method, Object[] args)
|
private ListenableFuture<?> createListenableFutureForHttpRequestMappedToMethodAndArgs(Method method, Object[] args)
|
||||||
throws ExecutionException {
|
throws ExecutionException {
|
||||||
method = annotationProcessor.getDelegateOrNull(method);
|
method = annotationProcessor.getDelegateOrNull(method);
|
||||||
logger.trace("Converting %s.%s", declaring.getSimpleName(), method.getName());
|
logger.trace("Converting %s.%s", declaring.getSimpleName(), method.getName());
|
||||||
Function<Exception, ?> exceptionParser = annotationProcessor
|
Function<Exception, ?> exceptionParser = annotationProcessor
|
||||||
.createExceptionParserOrThrowResourceNotFoundOn404IfNoAnnotation(method);
|
.createExceptionParserOrThrowResourceNotFoundOn404IfNoAnnotation(method);
|
||||||
// in case there is an exception creating the request, we should at least
|
// in case there is an exception creating the request, we should at least
|
||||||
// pass in args
|
// pass in args
|
||||||
if (exceptionParser instanceof InvocationContext) {
|
if (exceptionParser instanceof InvocationContext) {
|
||||||
|
@ -230,7 +242,7 @@ public class AsyncRestClientProxy<T> implements InvocationHandler {
|
||||||
|
|
||||||
Function<HttpResponse, ?> transformer = annotationProcessor.createResponseParser(method, request);
|
Function<HttpResponse, ?> transformer = annotationProcessor.createResponseParser(method, request);
|
||||||
logger.trace("Response from %s.%s is parsed by %s", declaring.getSimpleName(), method.getName(), transformer
|
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());
|
logger.debug("Invoking %s.%s", declaring.getSimpleName(), method.getName());
|
||||||
result = commandFactory.create(request, transformer).execute();
|
result = commandFactory.create(request, transformer).execute();
|
||||||
|
@ -251,7 +263,7 @@ public class AsyncRestClientProxy<T> implements InvocationHandler {
|
||||||
|
|
||||||
if (exceptionParser != null) {
|
if (exceptionParser != null) {
|
||||||
logger.trace("Exceptions from %s.%s are parsed by %s", declaring.getSimpleName(), method.getName(),
|
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);
|
result = new ExceptionParsingListenableFuture(result, exceptionParser);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
|
|
@ -47,9 +47,9 @@ import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.SortedSet;
|
import java.util.SortedSet;
|
||||||
import java.util.Map.Entry;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
|
@ -77,6 +77,7 @@ import org.jclouds.http.HttpUtils;
|
||||||
import org.jclouds.http.functions.ParseFirstJsonValueNamed;
|
import org.jclouds.http.functions.ParseFirstJsonValueNamed;
|
||||||
import org.jclouds.http.functions.ParseJson;
|
import org.jclouds.http.functions.ParseJson;
|
||||||
import org.jclouds.http.functions.ParseSax;
|
import org.jclouds.http.functions.ParseSax;
|
||||||
|
import org.jclouds.http.functions.ParseSax.HandlerWithResult;
|
||||||
import org.jclouds.http.functions.ParseURIFromListOrLocationHeaderIf20x;
|
import org.jclouds.http.functions.ParseURIFromListOrLocationHeaderIf20x;
|
||||||
import org.jclouds.http.functions.ParseXMLWithJAXB;
|
import org.jclouds.http.functions.ParseXMLWithJAXB;
|
||||||
import org.jclouds.http.functions.ReleasePayloadAndReturn;
|
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.ReturnStringIf2xx;
|
||||||
import org.jclouds.http.functions.ReturnTrueIf2xx;
|
import org.jclouds.http.functions.ReturnTrueIf2xx;
|
||||||
import org.jclouds.http.functions.UnwrapOnlyJsonValue;
|
import org.jclouds.http.functions.UnwrapOnlyJsonValue;
|
||||||
import org.jclouds.http.functions.ParseSax.HandlerWithResult;
|
|
||||||
import org.jclouds.http.options.HttpRequestOptions;
|
import org.jclouds.http.options.HttpRequestOptions;
|
||||||
import org.jclouds.http.utils.ModifyRequest;
|
import org.jclouds.http.utils.ModifyRequest;
|
||||||
import org.jclouds.internal.ClassMethodArgs;
|
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.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableMultimap;
|
import com.google.common.collect.ImmutableMultimap;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import com.google.common.collect.ImmutableSet.Builder;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.common.collect.LinkedHashMultimap;
|
import com.google.common.collect.LinkedHashMultimap;
|
||||||
import com.google.common.collect.LinkedListMultimap;
|
import com.google.common.collect.LinkedListMultimap;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.collect.Multimap;
|
import com.google.common.collect.Multimap;
|
||||||
import com.google.common.collect.ImmutableSet.Builder;
|
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Injector;
|
import com.google.inject.Injector;
|
||||||
|
|
|
@ -96,6 +96,7 @@ import org.jclouds.http.internal.PayloadEnclosingImpl;
|
||||||
import org.jclouds.http.options.BaseHttpRequestOptions;
|
import org.jclouds.http.options.BaseHttpRequestOptions;
|
||||||
import org.jclouds.http.options.GetOptions;
|
import org.jclouds.http.options.GetOptions;
|
||||||
import org.jclouds.http.options.HttpRequestOptions;
|
import org.jclouds.http.options.HttpRequestOptions;
|
||||||
|
import org.jclouds.internal.ClassMethodArgsAndReturnVal;
|
||||||
import org.jclouds.io.Payload;
|
import org.jclouds.io.Payload;
|
||||||
import org.jclouds.io.PayloadEnclosing;
|
import org.jclouds.io.PayloadEnclosing;
|
||||||
import org.jclouds.io.Payloads;
|
import org.jclouds.io.Payloads;
|
||||||
|
@ -355,7 +356,7 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest {
|
||||||
bind(ImplicitOptionalConverter.class).toInstance(new ImplicitOptionalConverter() {
|
bind(ImplicitOptionalConverter.class).toInstance(new ImplicitOptionalConverter() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<Object> apply(Object input) {
|
public Optional<Object> apply(ClassMethodArgsAndReturnVal input) {
|
||||||
return Optional.absent();
|
return Optional.absent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue