mirror of https://github.com/apache/jclouds.git
First wave of RestAnnotationParser refactoring introduces Invokable, removes SeedAnnotationCache and associated race conditions
This commit is contained in:
parent
37759d1c8c
commit
afc070ac07
|
@ -25,7 +25,7 @@ import java.util.Set;
|
|||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.jclouds.internal.ClassMethodArgsAndReturnVal;
|
||||
import org.jclouds.internal.ClassInvokerArgsAndReturnVal;
|
||||
import org.jclouds.openstack.v2_0.domain.Extension;
|
||||
import org.jclouds.openstack.v2_0.predicates.ExtensionPredicates;
|
||||
import org.jclouds.rest.functions.ImplicitOptionalConverter;
|
||||
|
@ -57,17 +57,17 @@ public class PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensio
|
|||
}
|
||||
|
||||
@Override
|
||||
public Optional<Object> apply(ClassMethodArgsAndReturnVal input) {
|
||||
public Optional<Object> apply(ClassInvokerArgsAndReturnVal input) {
|
||||
Optional<org.jclouds.openstack.v2_0.services.Extension> ext = Optional.fromNullable(input.getClazz().getAnnotation(
|
||||
org.jclouds.openstack.v2_0.services.Extension.class));
|
||||
if (ext.isPresent()) {
|
||||
URI namespace = URI.create(ext.get().namespace());
|
||||
if (input.getArgs().length == 0) {
|
||||
if (input.getArgs().isEmpty()) {
|
||||
if (Iterables.any(extensions.getUnchecked(""),
|
||||
ExtensionPredicates.namespaceOrAliasEquals(namespace, aliases.get(namespace))))
|
||||
return Optional.of(input.getReturnVal());
|
||||
} else if (input.getArgs().length == 1) {
|
||||
if (Iterables.any(extensions.getUnchecked(checkNotNull(input.getArgs()[0], "arg[0] in %s", input).toString()),
|
||||
} else if (input.getArgs().size() == 1) {
|
||||
if (Iterables.any(extensions.getUnchecked(checkNotNull(input.getArgs().get(0), "arg[0] in %s", input).toString()),
|
||||
ExtensionPredicates.namespaceOrAliasEquals(namespace, aliases.get(namespace))))
|
||||
return Optional.of(input.getReturnVal());
|
||||
} else {
|
||||
|
|
|
@ -6,7 +6,7 @@ import java.net.URI;
|
|||
import java.util.Set;
|
||||
|
||||
import org.jclouds.date.internal.SimpleDateFormatDateService;
|
||||
import org.jclouds.internal.ClassMethodArgsAndReturnVal;
|
||||
import org.jclouds.internal.ClassInvokerArgsAndReturnVal;
|
||||
import org.jclouds.openstack.v2_0.ServiceType;
|
||||
import org.jclouds.openstack.v2_0.domain.Extension;
|
||||
import org.jclouds.rest.annotations.Delegate;
|
||||
|
@ -17,10 +17,12 @@ import com.google.common.base.Optional;
|
|||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableMultimap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.reflect.Invokable;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Provides;
|
||||
|
@ -61,16 +63,19 @@ public class PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensio
|
|||
|
||||
}
|
||||
|
||||
ClassMethodArgsAndReturnVal getFloatingIPExtension() throws SecurityException, NoSuchMethodException {
|
||||
return ClassMethodArgsAndReturnVal.builder().clazz(FloatingIPAsyncApi.class).method(
|
||||
NovaAsyncApi.class.getDeclaredMethod("getFloatingIPExtensionForZone", String.class)).args(
|
||||
new Object[] { "zone" }).returnVal("foo").build();
|
||||
ClassInvokerArgsAndReturnVal getFloatingIPExtension() throws SecurityException, NoSuchMethodException {
|
||||
return ClassInvokerArgsAndReturnVal
|
||||
.builder()
|
||||
.clazz(FloatingIPAsyncApi.class)
|
||||
.invoker(
|
||||
Invokable.from(NovaAsyncApi.class.getDeclaredMethod("getFloatingIPExtensionForZone", String.class)))
|
||||
.args(ImmutableList.<Object> of("zone")).returnVal("foo").build();
|
||||
}
|
||||
|
||||
ClassMethodArgsAndReturnVal getKeyPairExtension() throws SecurityException, NoSuchMethodException {
|
||||
return ClassMethodArgsAndReturnVal.builder().clazz(KeyPairAsyncApi.class).method(
|
||||
NovaAsyncApi.class.getDeclaredMethod("getKeyPairExtensionForZone", String.class)).args(
|
||||
new Object[] { "zone" }).returnVal("foo").build();
|
||||
ClassInvokerArgsAndReturnVal getKeyPairExtension() throws SecurityException, NoSuchMethodException {
|
||||
return ClassInvokerArgsAndReturnVal.builder().clazz(KeyPairAsyncApi.class)
|
||||
.invoker(Invokable.from(NovaAsyncApi.class.getDeclaredMethod("getKeyPairExtensionForZone", String.class)))
|
||||
.args(ImmutableList.<Object> of("zone")).returnVal("foo").build();
|
||||
}
|
||||
|
||||
public void testPresentWhenExtensionsIncludeNamespaceFromAnnotationAbsentWhenNot() throws SecurityException, NoSuchMethodException {
|
||||
|
@ -83,9 +88,9 @@ public class PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensio
|
|||
|
||||
public void testZoneWithoutExtensionsReturnsAbsent() throws SecurityException, NoSuchMethodException {
|
||||
assertEquals(whenExtensionsInZoneInclude("zone", floatingIps).apply(
|
||||
getFloatingIPExtension().toBuilder().args(new Object[] { "differentzone" }).build()), Optional.absent());
|
||||
getFloatingIPExtension().toBuilder().args(ImmutableList.<Object> of("differentzone")).build()), Optional.absent());
|
||||
assertEquals(whenExtensionsInZoneInclude("zone", keypairs).apply(
|
||||
getKeyPairExtension().toBuilder().args(new Object[] { "differentzone" }).build()), Optional.absent());
|
||||
getKeyPairExtension().toBuilder().args(ImmutableList.<Object> of("differentzone")).build()), Optional.absent());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -63,8 +63,8 @@ public abstract class CallerArg0ToPagedIterable<T, I extends CallerArg0ToPagedIt
|
|||
return PagedIterables.of(input);
|
||||
|
||||
Optional<String> arg0Option = Optional.absent();
|
||||
if (request.getCaller().get().getArgs().length > 0) {
|
||||
Object arg0 = request.getCaller().get().getArgs()[0];
|
||||
if (request.getCaller().get().getArgs().size() > 0) {
|
||||
Object arg0 = request.getCaller().get().getArgs().get(0);
|
||||
if (arg0 != null)
|
||||
arg0Option = Optional.of(arg0.toString());
|
||||
}
|
||||
|
|
|
@ -33,8 +33,8 @@ import javax.annotation.Resource;
|
|||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
||||
import org.jclouds.internal.ClassMethodArgs;
|
||||
import org.jclouds.internal.ClassMethodArgsAndReturnVal;
|
||||
import org.jclouds.internal.ClassInvokerArgs;
|
||||
import org.jclouds.internal.ClassInvokerArgsAndReturnVal;
|
||||
import org.jclouds.logging.Logger;
|
||||
import org.jclouds.rest.annotations.Delegate;
|
||||
import org.jclouds.util.Optionals2;
|
||||
|
@ -74,20 +74,20 @@ public final class SyncProxy extends AbstractInvocationHandler {
|
|||
@Resource
|
||||
private Logger logger = Logger.NULL;
|
||||
|
||||
private final Function<ClassMethodArgsAndReturnVal, Optional<Object>> optionalConverter;
|
||||
private final Function<ClassInvokerArgsAndReturnVal, Optional<Object>> optionalConverter;
|
||||
private final Object delegate;
|
||||
private final Class<?> declaring;
|
||||
private final Map<Method, Method> methodMap;
|
||||
private final Map<Method, Method> syncMethodMap;
|
||||
private final Map<Method, Optional<Long>> timeoutMap;
|
||||
private final LoadingCache<ClassMethodArgs, Object> delegateMap;
|
||||
private final LoadingCache<ClassInvokerArgs, Object> delegateMap;
|
||||
private final Map<Class<?>, Class<?>> sync2Async;
|
||||
private static final Set<Method> objectMethods = ImmutableSet.copyOf(Object.class.getMethods());
|
||||
|
||||
@Inject
|
||||
@VisibleForTesting
|
||||
SyncProxy(Function<ClassMethodArgsAndReturnVal, Optional<Object>> optionalConverter,
|
||||
@Named("sync") LoadingCache<ClassMethodArgs, Object> delegateMap, Map<Class<?>, Class<?>> sync2Async,
|
||||
SyncProxy(Function<ClassInvokerArgsAndReturnVal, Optional<Object>> optionalConverter,
|
||||
@Named("sync") LoadingCache<ClassInvokerArgs, Object> delegateMap, Map<Class<?>, Class<?>> sync2Async,
|
||||
@Named("TIMEOUTS") Map<String, Long> timeouts, @Assisted Class<?> declaring, @Assisted Object async)
|
||||
throws SecurityException, NoSuchMethodException {
|
||||
this.optionalConverter = optionalConverter;
|
||||
|
@ -138,10 +138,10 @@ public final class SyncProxy extends AbstractInvocationHandler {
|
|||
// 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"
|
||||
ClassMethodArgs cma = new ClassMethodArgs(asyncClass, method, args);
|
||||
ClassInvokerArgs cma = ClassInvokerArgs.builder().clazz(asyncClass).invoker(method).args(args).build();
|
||||
Object returnVal = delegateMap.get(cma);
|
||||
if (Optionals2.isReturnTypeOptional(method)){
|
||||
ClassMethodArgsAndReturnVal cmar = ClassMethodArgsAndReturnVal.builder().fromClassMethodArgs(cma)
|
||||
ClassInvokerArgsAndReturnVal cmar = ClassInvokerArgsAndReturnVal.builder().fromClassInvokerArgs(cma)
|
||||
.returnVal(returnVal).build();
|
||||
return optionalConverter.apply(cmar);
|
||||
}
|
||||
|
|
|
@ -19,12 +19,17 @@
|
|||
package org.jclouds.http;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Predicates.and;
|
||||
import static com.google.common.base.Predicates.equalTo;
|
||||
import static com.google.common.base.Predicates.in;
|
||||
import static com.google.common.base.Predicates.not;
|
||||
import static com.google.common.base.Predicates.notNull;
|
||||
import static com.google.common.base.Throwables.getCausalChain;
|
||||
import static com.google.common.base.Throwables.propagate;
|
||||
import static com.google.common.collect.Iterables.filter;
|
||||
import static com.google.common.collect.Iterables.get;
|
||||
import static com.google.common.collect.Iterables.size;
|
||||
import static com.google.common.collect.Multimaps.filterKeys;
|
||||
import static com.google.common.io.BaseEncoding.base64;
|
||||
import static com.google.common.io.ByteStreams.toByteArray;
|
||||
import static com.google.common.io.Closeables.closeQuietly;
|
||||
|
@ -38,11 +43,13 @@ import static com.google.common.net.HttpHeaders.EXPIRES;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.Collection;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
import javax.ws.rs.HttpMethod;
|
||||
|
||||
import org.jclouds.Constants;
|
||||
import org.jclouds.io.ContentMetadata;
|
||||
|
@ -53,7 +60,13 @@ import org.jclouds.io.Payloads;
|
|||
import org.jclouds.logging.Logger;
|
||||
import org.jclouds.logging.internal.Wire;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.ImmutableMultimap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.collect.ImmutableSet.Builder;
|
||||
import com.google.common.reflect.Invokable;
|
||||
import com.google.inject.Inject;
|
||||
|
||||
/**
|
||||
|
@ -172,6 +185,24 @@ public class HttpUtils {
|
|||
return null;
|
||||
}
|
||||
|
||||
public static Optional<String> tryFindHttpMethod(Invokable<?, ?> method) {
|
||||
Builder<String> methodsBuilder = ImmutableSet.builder();
|
||||
for (Annotation annotation : method.getAnnotations()) {
|
||||
HttpMethod http = annotation.annotationType().getAnnotation(HttpMethod.class);
|
||||
if (http != null)
|
||||
methodsBuilder.add(http.value());
|
||||
}
|
||||
Collection<String> methods = methodsBuilder.build();
|
||||
switch (methods.size()) {
|
||||
case 0:
|
||||
return Optional.absent();
|
||||
case 1:
|
||||
return Optional.of(get(methods, 0));
|
||||
default:
|
||||
throw new IllegalStateException("You must specify at most one HttpMethod annotation on: " + method);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Content stream may need to be read. However, we should always close the http stream.
|
||||
*
|
||||
|
@ -322,6 +353,11 @@ public class HttpUtils {
|
|||
return null;
|
||||
}
|
||||
|
||||
public static Multimap<String, String> filterOutContentHeaders(Multimap<String, String> headers) {
|
||||
// http message usually comes in as a null key header, let's filter it out.
|
||||
return ImmutableMultimap.copyOf(filterKeys(headers, and(notNull(), not(in(ContentMetadata.HTTP_HEADERS)))));
|
||||
}
|
||||
|
||||
public static boolean contains404(Throwable t) {
|
||||
return returnValueOnCodeOrNull(t, true, equalTo(404)) != null;
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ import static com.google.common.io.Closeables.closeQuietly;
|
|||
import static com.google.common.net.HttpHeaders.CONTENT_LENGTH;
|
||||
import static com.google.common.net.HttpHeaders.HOST;
|
||||
import static com.google.common.net.HttpHeaders.USER_AGENT;
|
||||
import static org.jclouds.http.HttpUtils.filterOutContentHeaders;
|
||||
import static org.jclouds.io.Payloads.newInputStreamPayload;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
|
@ -65,7 +66,6 @@ import org.jclouds.io.ContentMetadataCodec;
|
|||
import org.jclouds.io.MutableContentMetadata;
|
||||
import org.jclouds.io.Payload;
|
||||
import org.jclouds.logging.Logger;
|
||||
import org.jclouds.rest.internal.RestAnnotationProcessor;
|
||||
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.collect.ImmutableMultimap;
|
||||
|
@ -142,7 +142,7 @@ public class JavaUrlHttpCommandExecutorService extends BaseHttpCommandExecutorSe
|
|||
contentMetadataCodec.fromHeaders(payload.getContentMetadata(), headers);
|
||||
builder.payload(payload);
|
||||
}
|
||||
builder.headers(RestAnnotationProcessor.filterOutContentHeaders(headers));
|
||||
builder.headers(filterOutContentHeaders(headers));
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
|
|
|
@ -22,22 +22,25 @@ 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 java.util.List;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.base.Objects.ToStringHelper;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.reflect.Invokable;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
public class ClassMethodArgs {
|
||||
public class ClassInvokerArgs {
|
||||
public static Builder<?> builder() {
|
||||
return new ConcreteBuilder();
|
||||
}
|
||||
|
||||
public Builder<?> toBuilder() {
|
||||
return builder().fromClassMethodArgs(this);
|
||||
return builder().fromClassInvokerArgs(this);
|
||||
}
|
||||
|
||||
private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
|
||||
|
@ -45,8 +48,8 @@ public class ClassMethodArgs {
|
|||
|
||||
public abstract static class Builder<B extends Builder<B>> {
|
||||
private Class<?> clazz;
|
||||
private Method method;
|
||||
private Object[] args = {};
|
||||
private Invokable<?, ?> invoker;
|
||||
private List<Object> args = ImmutableList.of();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected B self() {
|
||||
|
@ -54,7 +57,7 @@ public class ClassMethodArgs {
|
|||
}
|
||||
|
||||
/**
|
||||
* @see ClassMethodArgs#getClazz()
|
||||
* @see ClassInvokerArgs#getClazz()
|
||||
*/
|
||||
public B clazz(Class<?> clazz) {
|
||||
this.clazz = clazz;
|
||||
|
@ -62,53 +65,78 @@ public class ClassMethodArgs {
|
|||
}
|
||||
|
||||
/**
|
||||
* @see ClassMethodArgs#getMethod()
|
||||
* @see ClassInvokerArgs#getInvoker()
|
||||
*/
|
||||
public B method(Method method) {
|
||||
this.method = method;
|
||||
public B invoker(Invokable<?, ?> invoker) {
|
||||
this.invoker = invoker;
|
||||
return self();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ClassInvokerArgs#getInvoker()
|
||||
*/
|
||||
@Deprecated
|
||||
public B invoker(Method method) {
|
||||
return invoker(Invokable.from(method));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ClassMethodArgs#getArgs()
|
||||
* @see ClassInvokerArgs#getArgs()
|
||||
*/
|
||||
@Deprecated
|
||||
public B args(Object[] args) {
|
||||
return args(args == null ? Lists.newArrayList(new Object[] { null }) : Lists.newArrayList(args));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ClassInvokerArgs#getArgs()
|
||||
*/
|
||||
public B args(List<Object> args) {
|
||||
this.args = args;
|
||||
return self();
|
||||
}
|
||||
|
||||
public ClassMethodArgs build() {
|
||||
return new ClassMethodArgs(this);
|
||||
public ClassInvokerArgs build() {
|
||||
return new ClassInvokerArgs(this);
|
||||
}
|
||||
|
||||
public B fromClassMethodArgs(ClassMethodArgs in) {
|
||||
return clazz(in.getClazz()).method(in.getMethod()).args(in.getArgs());
|
||||
public B fromClassInvokerArgs(ClassInvokerArgs in) {
|
||||
return clazz(in.getClazz()).invoker(in.getInvoker()).args(in.getArgs());
|
||||
}
|
||||
}
|
||||
|
||||
private final Class<?> clazz;
|
||||
private final Method method;
|
||||
private final Object[] args;
|
||||
private final Invokable<?, ?> invoker;
|
||||
private final List<Object> args;
|
||||
|
||||
public ClassMethodArgs(Builder<?> builder) {
|
||||
this(builder.clazz, builder.method, builder.args);
|
||||
public ClassInvokerArgs(Builder<?> builder) {
|
||||
this(builder.clazz, builder.invoker, builder.args);
|
||||
}
|
||||
|
||||
public ClassMethodArgs(Class<?> clazz, Method method, Object[] args) {
|
||||
/**
|
||||
* @param args as these represent parameters, can contain nulls
|
||||
*/
|
||||
public ClassInvokerArgs(Class<?> clazz, Invokable<?, ?> invoker, List<Object> args) {
|
||||
this.clazz = checkNotNull(clazz, "clazz");
|
||||
this.method = checkNotNull(method, "method");
|
||||
this.invoker = checkNotNull(invoker, "invoker");
|
||||
this.args = checkNotNull(args, "args");
|
||||
}
|
||||
|
||||
/**
|
||||
* not necessarily the declaring class of the invoker.
|
||||
*/
|
||||
public Class<?> getClazz() {
|
||||
return clazz;
|
||||
}
|
||||
|
||||
public Method getMethod() {
|
||||
return method;
|
||||
public Invokable<?, ?> getInvoker() {
|
||||
return invoker;
|
||||
}
|
||||
|
||||
public Object[] getArgs() {
|
||||
|
||||
/**
|
||||
* @param args as these represent parameters, can contain nulls
|
||||
*/
|
||||
public List<Object> getArgs() {
|
||||
return args;
|
||||
}
|
||||
|
||||
|
@ -118,13 +146,13 @@ public class ClassMethodArgs {
|
|||
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);
|
||||
ClassInvokerArgs that = ClassInvokerArgs.class.cast(o);
|
||||
return equal(this.clazz, that.clazz) && equal(this.invoker, that.invoker) && equal(this.args, that.args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(clazz, method, args);
|
||||
return Objects.hashCode(clazz, invoker, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -133,7 +161,7 @@ public class ClassMethodArgs {
|
|||
}
|
||||
|
||||
protected ToStringHelper string() {
|
||||
return Objects.toStringHelper("").omitNullValues().add("clazz", clazz).add("method", method)
|
||||
.add("args", args.length != 0 ? Arrays.asList(args) : null);
|
||||
return Objects.toStringHelper("").omitNullValues().add("clazz", clazz).add("invoker", invoker)
|
||||
.add("args", args.size() != 0 ? args : null);
|
||||
}
|
||||
}
|
|
@ -20,31 +20,32 @@ package org.jclouds.internal;
|
|||
|
||||
import static com.google.common.base.Objects.equal;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.base.Objects.ToStringHelper;
|
||||
import com.google.common.reflect.Invokable;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
public final class ClassMethodArgsAndReturnVal extends ClassMethodArgs {
|
||||
public final class ClassInvokerArgsAndReturnVal extends ClassInvokerArgs {
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public Builder toBuilder() {
|
||||
return builder().fromClassMethodArgsAndReturnVal(this);
|
||||
return builder().fromClassInvokerArgsAndReturnVal(this);
|
||||
}
|
||||
|
||||
public final static class Builder extends ClassMethodArgs.Builder<Builder> {
|
||||
public final static class Builder extends ClassInvokerArgs.Builder<Builder> {
|
||||
|
||||
private Object returnVal;
|
||||
|
||||
/**
|
||||
* @see ClassMethodArgsAndReturnVal#getReturnVal()
|
||||
* @see ClassInvokerArgsAndReturnVal#getReturnVal()
|
||||
*/
|
||||
public Builder returnVal(Object returnVal) {
|
||||
this.returnVal = returnVal;
|
||||
|
@ -52,23 +53,23 @@ public final class ClassMethodArgsAndReturnVal extends ClassMethodArgs {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ClassMethodArgsAndReturnVal build() {
|
||||
return new ClassMethodArgsAndReturnVal(this);
|
||||
public ClassInvokerArgsAndReturnVal build() {
|
||||
return new ClassInvokerArgsAndReturnVal(this);
|
||||
}
|
||||
|
||||
public Builder fromClassMethodArgsAndReturnVal(ClassMethodArgsAndReturnVal in) {
|
||||
return fromClassMethodArgs(in).returnVal(in.getReturnVal());
|
||||
public Builder fromClassInvokerArgsAndReturnVal(ClassInvokerArgsAndReturnVal in) {
|
||||
return fromClassInvokerArgs(in).returnVal(in.getReturnVal());
|
||||
}
|
||||
}
|
||||
|
||||
private final Object returnVal;
|
||||
|
||||
private ClassMethodArgsAndReturnVal(Class<?> clazz, Method method, Object[] args, Object returnVal) {
|
||||
super(clazz, method, args);
|
||||
private ClassInvokerArgsAndReturnVal(Class<?> clazz, Invokable<?, ?> invoker, List<Object> args, Object returnVal) {
|
||||
super(clazz, invoker, args);
|
||||
this.returnVal = returnVal;
|
||||
}
|
||||
|
||||
private ClassMethodArgsAndReturnVal(Builder builder) {
|
||||
private ClassInvokerArgsAndReturnVal(Builder builder) {
|
||||
super(builder);
|
||||
this.returnVal = builder.returnVal;
|
||||
}
|
||||
|
@ -83,7 +84,7 @@ public final class ClassMethodArgsAndReturnVal extends ClassMethodArgs {
|
|||
return true;
|
||||
if (o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
ClassMethodArgsAndReturnVal that = ClassMethodArgsAndReturnVal.class.cast(o);
|
||||
ClassInvokerArgsAndReturnVal that = ClassInvokerArgsAndReturnVal.class.cast(o);
|
||||
return super.equals(that) && equal(this.returnVal, that.returnVal);
|
||||
}
|
||||
|
|
@ -32,7 +32,7 @@ import javax.inject.Singleton;
|
|||
|
||||
import org.jclouds.collect.Memoized;
|
||||
import org.jclouds.domain.Location;
|
||||
import org.jclouds.internal.ClassMethodArgsAndReturnVal;
|
||||
import org.jclouds.internal.ClassInvokerArgsAndReturnVal;
|
||||
import org.jclouds.location.Iso3166;
|
||||
import org.jclouds.location.Provider;
|
||||
import org.jclouds.location.Region;
|
||||
|
@ -73,7 +73,7 @@ public class LocationModule extends AbstractModule {
|
|||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(new TypeLiteral<Function<ClassMethodArgsAndReturnVal, Optional<Object>>>(){}).to(ImplicitOptionalConverter.class);
|
||||
bind(new TypeLiteral<Function<ClassInvokerArgsAndReturnVal, Optional<Object>>>(){}).to(ImplicitOptionalConverter.class);
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
|
|
@ -19,130 +19,140 @@
|
|||
package org.jclouds.rest;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.collect.Collections2.filter;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.jclouds.javax.annotation.Nullable;
|
||||
import org.jclouds.predicates.Validator;
|
||||
import org.jclouds.rest.annotations.ParamValidators;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.reflect.Invokable;
|
||||
import com.google.common.reflect.Parameter;
|
||||
import com.google.inject.Injector;
|
||||
|
||||
/**
|
||||
* Validates method parameters.
|
||||
*
|
||||
* Checks the {@link ParamValidators} annotation for validators. There can be method-level
|
||||
* validators that apply to all parameters, and parameter-level validators. When validation on at
|
||||
* least one parameter doesn't pass, throws {@link IllegalStateException}.
|
||||
*
|
||||
*
|
||||
* Checks the {@link ParamValidators} annotation for validators. There can be method-level validators that apply to all
|
||||
* parameters, and parameter-level validators. When validation on at least one parameter doesn't pass, throws
|
||||
* {@link IllegalStateException}.
|
||||
*
|
||||
* @author Oleksiy Yarmula
|
||||
*/
|
||||
public class InputParamValidator {
|
||||
|
||||
private final Injector injector;
|
||||
private final Injector injector;
|
||||
|
||||
@Inject
|
||||
public InputParamValidator(Injector injector) {
|
||||
this.injector = injector;
|
||||
}
|
||||
@Inject
|
||||
public InputParamValidator(Injector injector) {
|
||||
this.injector = injector;
|
||||
}
|
||||
|
||||
public InputParamValidator() {
|
||||
injector = null;
|
||||
}
|
||||
public InputParamValidator() {
|
||||
injector = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that method parameters are correct, according to {@link ParamValidators}.
|
||||
*
|
||||
* @param method
|
||||
* method with optionally set {@link ParamValidators}
|
||||
* @param args
|
||||
* method arguments with optionally set {@link ParamValidators}
|
||||
* @see ParamValidators
|
||||
* @see Validator
|
||||
*
|
||||
* @throws IllegalStateException
|
||||
* if validation failed
|
||||
*/
|
||||
public void validateMethodParametersOrThrow(Method method, Object... args) {
|
||||
/**
|
||||
* Validates that method parameters are correct, according to {@link ParamValidators}.
|
||||
*
|
||||
* @param method
|
||||
* method with optionally set {@link ParamValidators}
|
||||
* @param args
|
||||
* method arguments with optionally set {@link ParamValidators}
|
||||
* @see ParamValidators
|
||||
* @see Validator
|
||||
*
|
||||
* @throws IllegalStateException
|
||||
* if validation failed
|
||||
*/
|
||||
@Deprecated
|
||||
public void validateMethodParametersOrThrow(Method method, @Nullable Object... args) {
|
||||
validateMethodParametersOrThrow(Invokable.from(checkNotNull(method, "method")),
|
||||
Lists.newArrayList(args));
|
||||
}
|
||||
|
||||
try {
|
||||
performMethodValidation(method, args);
|
||||
performParameterValidation(method.getParameterAnnotations(), args);
|
||||
} catch(IllegalArgumentException e) {
|
||||
String argsString = Iterables.toString(Arrays.asList(args));
|
||||
throw new IllegalArgumentException(String.format("Validation on '%s#%s' didn't pass for arguments: " +
|
||||
"%s. %n Reason: %s.", method.getDeclaringClass().getName(), method.getName(), argsString,
|
||||
e.getMessage()));
|
||||
}
|
||||
}
|
||||
public void validateMethodParametersOrThrow(Invokable<?, ?> method, List<Object> args) {
|
||||
try {
|
||||
performMethodValidation(checkNotNull(method, "method"), args);
|
||||
performParameterValidation(method.getParameters(), args);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new IllegalArgumentException(String.format("Validation on '%s#%s' didn't pass for arguments: "
|
||||
+ "%s. %n Reason: %s.", method.getDeclaringClass().getName(), method.getName(), args,
|
||||
e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if all the method parameters passed all of the method-level
|
||||
* validators or throws an {@link IllegalArgumentException}.
|
||||
* @param method
|
||||
* method with optionally set {@link ParamValidators}. This can not be null.
|
||||
* @param args
|
||||
* method's parameters
|
||||
*/
|
||||
private void performMethodValidation(Method method, Object... args) {
|
||||
ParamValidators paramValidatorsAnnotation = checkNotNull(method).getAnnotation(
|
||||
ParamValidators.class);
|
||||
if (paramValidatorsAnnotation == null)
|
||||
return; // by contract
|
||||
/**
|
||||
* Returns if all the method parameters passed all of the method-level validators or throws an
|
||||
* {@link IllegalArgumentException}.
|
||||
*
|
||||
* @param method
|
||||
* method with optionally set {@link ParamValidators}. This can not be null.
|
||||
* @param args
|
||||
* method's parameters
|
||||
*/
|
||||
private void performMethodValidation(Invokable<?, ?> method, List<Object> args) {
|
||||
ParamValidators paramValidatorsAnnotation = method.getAnnotation(ParamValidators.class);
|
||||
if (paramValidatorsAnnotation == null)
|
||||
return; // by contract
|
||||
|
||||
List<Validator<?>> methodValidators = getValidatorsFromAnnotation(paramValidatorsAnnotation);
|
||||
List<Validator<?>> methodValidators = getValidatorsFromAnnotation(paramValidatorsAnnotation);
|
||||
|
||||
runPredicatesAgainstArgs(methodValidators, args);
|
||||
}
|
||||
runPredicatesAgainstArgs(methodValidators, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if all the method parameters passed all of their corresponding
|
||||
* validators or throws an {@link IllegalArgumentException}.
|
||||
*
|
||||
* @param annotations
|
||||
* annotations for method's arguments
|
||||
* @param args
|
||||
* arguments that correspond to the array of annotations
|
||||
*/
|
||||
private void performParameterValidation(Annotation[][] annotations, Object... args) {
|
||||
for (int currentParameterIndex = 0; currentParameterIndex < annotations.length; currentParameterIndex++) {
|
||||
ParamValidators annotation = findParamValidatorsAnnotationOrReturnNull(annotations[currentParameterIndex]);
|
||||
if (annotation == null)
|
||||
continue;
|
||||
List<Validator<?>> parameterValidators = getValidatorsFromAnnotation(annotation);
|
||||
runPredicatesAgainstArgs(parameterValidators,
|
||||
args[currentParameterIndex]);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Returns if all the method parameters passed all of their corresponding validators or throws an
|
||||
* {@link IllegalArgumentException}.
|
||||
*
|
||||
* @param parameters
|
||||
* annotations for method's arguments
|
||||
* @param args
|
||||
* arguments that correspond to the array of annotations
|
||||
*/
|
||||
private void performParameterValidation(List<Parameter> parameters, List<Object> args) {
|
||||
for (Parameter param : filter(parameters, new Predicate<Parameter>() {
|
||||
public boolean apply(Parameter in) {
|
||||
return in.isAnnotationPresent(ParamValidators.class);
|
||||
}
|
||||
})) {
|
||||
ParamValidators annotation = param.getAnnotation(ParamValidators.class);
|
||||
if (annotation == null)
|
||||
continue;
|
||||
List<Validator<?>> parameterValidators = getValidatorsFromAnnotation(annotation);
|
||||
runPredicatesAgainstArg(parameterValidators, args.get(param.hashCode()));// TODO position guava issue 1243
|
||||
}
|
||||
}
|
||||
|
||||
private List<Validator<?>> getValidatorsFromAnnotation(ParamValidators paramValidatorsAnnotation) {
|
||||
List<Validator<?>> validators = Lists.newArrayList();
|
||||
for (Class<? extends Validator<?>> validator : paramValidatorsAnnotation.value()) {
|
||||
validators.add(checkNotNull(injector.getInstance(validator)));
|
||||
}
|
||||
return validators;
|
||||
}
|
||||
private List<Validator<?>> getValidatorsFromAnnotation(ParamValidators paramValidatorsAnnotation) {
|
||||
List<Validator<?>> validators = Lists.newArrayList();
|
||||
for (Class<? extends Validator<?>> validator : paramValidatorsAnnotation.value()) {
|
||||
validators.add(checkNotNull(injector.getInstance(validator)));
|
||||
}
|
||||
return validators;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void runPredicatesAgainstArg(List<Validator<?>> predicates, Object arg) {
|
||||
for (@SuppressWarnings("rawtypes")
|
||||
Validator validator : predicates) {
|
||||
validator.apply(arg);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void runPredicatesAgainstArgs(List<Validator<?>> predicates, Object... args) {
|
||||
for (@SuppressWarnings("rawtypes") Validator validator : predicates) {
|
||||
Iterables.all(Arrays.asList(args), validator);
|
||||
}
|
||||
}
|
||||
|
||||
private ParamValidators findParamValidatorsAnnotationOrReturnNull(
|
||||
Annotation[] parameterAnnotations) {
|
||||
for (Annotation annotation : parameterAnnotations) {
|
||||
if (annotation instanceof ParamValidators)
|
||||
return (ParamValidators) annotation;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
private void runPredicatesAgainstArgs(List<Validator<?>> predicates, List<Object> args) {
|
||||
for (@SuppressWarnings("rawtypes")
|
||||
Validator validator : predicates) {
|
||||
Iterables.all(args, validator);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@ package org.jclouds.rest.config;
|
|||
import static org.jclouds.Constants.PROPERTY_TIMEOUTS_PREFIX;
|
||||
import static org.jclouds.rest.config.BinderUtils.bindClientAndAsyncClient;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URI;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
@ -32,7 +31,7 @@ import javax.inject.Singleton;
|
|||
import org.jclouds.concurrent.internal.SyncProxy;
|
||||
import org.jclouds.functions.IdentityFunction;
|
||||
import org.jclouds.http.functions.config.SaxParserModule;
|
||||
import org.jclouds.internal.ClassMethodArgs;
|
||||
import org.jclouds.internal.ClassInvokerArgs;
|
||||
import org.jclouds.internal.FilterStringsBoundToInjectorByName;
|
||||
import org.jclouds.json.config.GsonModule;
|
||||
import org.jclouds.location.config.LocationModule;
|
||||
|
@ -44,15 +43,12 @@ import org.jclouds.rest.internal.AsyncRestClientProxy;
|
|||
import org.jclouds.rest.internal.CreateAsyncClientForCaller;
|
||||
import org.jclouds.rest.internal.CreateClientForCaller;
|
||||
import org.jclouds.rest.internal.RestAnnotationProcessor;
|
||||
import org.jclouds.rest.internal.RestAnnotationProcessor.MethodKey;
|
||||
import org.jclouds.rest.internal.SeedAnnotationCache;
|
||||
import org.jclouds.util.Maps2;
|
||||
import org.jclouds.util.Predicates2;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
@ -124,24 +120,18 @@ public class RestModule extends AbstractModule {
|
|||
});
|
||||
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
private LoadingCache<Class<?>, Cache<MethodKey, Method>> seedAnnotationCache(SeedAnnotationCache seedAnnotationCache) {
|
||||
return CacheBuilder.newBuilder().build(seedAnnotationCache);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Named("async")
|
||||
LoadingCache<ClassMethodArgs, Object> provideAsyncDelegateMap(CreateAsyncClientForCaller createAsyncClientForCaller) {
|
||||
LoadingCache<ClassInvokerArgs, Object> provideAsyncDelegateMap(CreateAsyncClientForCaller createAsyncClientForCaller) {
|
||||
return CacheBuilder.newBuilder().build(createAsyncClientForCaller);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Named("sync")
|
||||
LoadingCache<ClassMethodArgs, Object> provideSyncDelegateMap(CreateClientForCaller createClientForCaller) {
|
||||
LoadingCache<ClassInvokerArgs, Object> provideSyncDelegateMap(CreateClientForCaller createClientForCaller) {
|
||||
return CacheBuilder.newBuilder().build(createClientForCaller);
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
package org.jclouds.rest.functions;
|
||||
|
||||
import org.jclouds.internal.ClassMethodArgsAndReturnVal;
|
||||
import org.jclouds.internal.ClassInvokerArgsAndReturnVal;
|
||||
|
||||
import com.google.common.annotations.Beta;
|
||||
import com.google.common.base.Optional;
|
||||
|
@ -31,7 +31,7 @@ import com.google.common.base.Optional;
|
|||
public class AlwaysPresentImplicitOptionalConverter implements ImplicitOptionalConverter {
|
||||
|
||||
@Override
|
||||
public Optional<Object> apply(ClassMethodArgsAndReturnVal input) {
|
||||
public Optional<Object> apply(ClassInvokerArgsAndReturnVal input) {
|
||||
return Optional.of(input.getReturnVal());
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
package org.jclouds.rest.functions;
|
||||
|
||||
import org.jclouds.internal.ClassMethodArgsAndReturnVal;
|
||||
import org.jclouds.internal.ClassInvokerArgsAndReturnVal;
|
||||
import org.jclouds.rest.config.RestClientModule;
|
||||
|
||||
import com.google.common.annotations.Beta;
|
||||
|
@ -39,19 +39,19 @@ import com.google.inject.ImplementedBy;
|
|||
* }
|
||||
* </pre>
|
||||
*
|
||||
* The input object of type {@link ClassMethodArgsAndReturnVal} will include the
|
||||
* The input object of type {@link ClassInvokerArgsAndReturnVal} will include the
|
||||
* following.
|
||||
* <ol>
|
||||
* <li>the class declaring the method that returns optional:
|
||||
* {@link ClassMethodArgsAndReturnVal#getClazz}; in the example above,
|
||||
* {@link ClassInvokerArgsAndReturnVal#getClazz}; in the example above,
|
||||
* {@code MyCloud}</li>
|
||||
* <li>the method returning the optional:
|
||||
* {@link ClassMethodArgsAndReturnVal#getMethod}; in the example above,
|
||||
* {@link ClassInvokerArgsAndReturnVal#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>
|
||||
* {@link ClassInvokerArgsAndReturnVal#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
|
||||
* return it: {@link ClassInvokerArgsAndReturnVal#getReturnVal}; in the example
|
||||
* above, an implementation of {@code KeyPairClient}</li>
|
||||
* </ol>
|
||||
*
|
||||
|
@ -80,6 +80,6 @@ import com.google.inject.ImplementedBy;
|
|||
*/
|
||||
@Beta
|
||||
@ImplementedBy(PresentWhenApiVersionLexicographicallyAtOrAfterSinceApiVersion.class)
|
||||
public interface ImplicitOptionalConverter extends Function<ClassMethodArgsAndReturnVal, Optional<Object>> {
|
||||
public interface ImplicitOptionalConverter extends Function<ClassInvokerArgsAndReturnVal, Optional<Object>> {
|
||||
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
|||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.jclouds.internal.ClassMethodArgsAndReturnVal;
|
||||
import org.jclouds.internal.ClassInvokerArgsAndReturnVal;
|
||||
import org.jclouds.rest.annotations.ApiVersion;
|
||||
import org.jclouds.rest.annotations.SinceApiVersion;
|
||||
|
||||
|
@ -43,7 +43,7 @@ import com.google.common.cache.LoadingCache;
|
|||
public class PresentWhenApiVersionLexicographicallyAtOrAfterSinceApiVersion implements ImplicitOptionalConverter {
|
||||
|
||||
@VisibleForTesting
|
||||
static final class Loader extends CacheLoader<ClassMethodArgsAndReturnVal, Optional<Object>> {
|
||||
static final class Loader extends CacheLoader<ClassInvokerArgsAndReturnVal, Optional<Object>> {
|
||||
private final String apiVersion;
|
||||
|
||||
@Inject
|
||||
|
@ -52,7 +52,7 @@ public class PresentWhenApiVersionLexicographicallyAtOrAfterSinceApiVersion impl
|
|||
}
|
||||
|
||||
@Override
|
||||
public Optional<Object> load(ClassMethodArgsAndReturnVal input) {
|
||||
public Optional<Object> load(ClassInvokerArgsAndReturnVal input) {
|
||||
Optional<SinceApiVersion> sinceApiVersion = Optional.fromNullable(input.getClazz().getAnnotation(
|
||||
SinceApiVersion.class));
|
||||
if (sinceApiVersion.isPresent()) {
|
||||
|
@ -67,7 +67,7 @@ public class PresentWhenApiVersionLexicographicallyAtOrAfterSinceApiVersion impl
|
|||
}
|
||||
}
|
||||
|
||||
private final LoadingCache<ClassMethodArgsAndReturnVal, Optional<Object>> lookupCache;
|
||||
private final LoadingCache<ClassInvokerArgsAndReturnVal, Optional<Object>> lookupCache;
|
||||
|
||||
@Inject
|
||||
protected PresentWhenApiVersionLexicographicallyAtOrAfterSinceApiVersion(@ApiVersion String apiVersion) {
|
||||
|
@ -76,7 +76,7 @@ public class PresentWhenApiVersionLexicographicallyAtOrAfterSinceApiVersion impl
|
|||
}
|
||||
|
||||
@Override
|
||||
public Optional<Object> apply(ClassMethodArgsAndReturnVal input) {
|
||||
public Optional<Object> apply(ClassInvokerArgsAndReturnVal input) {
|
||||
return lookupCache.getUnchecked(input);
|
||||
}
|
||||
|
||||
|
|
|
@ -18,14 +18,34 @@
|
|||
*/
|
||||
package org.jclouds.rest.internal;
|
||||
|
||||
import static com.google.common.base.Functions.compose;
|
||||
import static com.google.common.base.Throwables.propagate;
|
||||
import static com.google.common.collect.Iterables.any;
|
||||
import static com.google.common.collect.Iterables.find;
|
||||
import static com.google.common.collect.Lists.newArrayList;
|
||||
import static com.google.common.collect.Sets.difference;
|
||||
import static com.google.common.util.concurrent.Callables.returning;
|
||||
import static com.google.common.util.concurrent.Futures.immediateFailedFuture;
|
||||
import static com.google.common.util.concurrent.Futures.transform;
|
||||
import static com.google.common.util.concurrent.Futures.withFallback;
|
||||
import static com.google.inject.util.Types.newParameterizedType;
|
||||
import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
|
||||
import static javax.ws.rs.core.MediaType.APPLICATION_XML;
|
||||
import static org.jclouds.concurrent.Futures.makeListenable;
|
||||
import static org.jclouds.http.HttpUtils.tryFindHttpMethod;
|
||||
import static org.jclouds.util.Optionals2.isReturnTypeOptional;
|
||||
import static org.jclouds.util.Optionals2.returnTypeOrTypeOfOptional;
|
||||
import static org.jclouds.util.Throwables2.getFirstThrowableOfType;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.lang.reflect.WildcardType;
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
|
@ -34,34 +54,60 @@ import javax.inject.Inject;
|
|||
import javax.inject.Named;
|
||||
import javax.inject.Qualifier;
|
||||
import javax.inject.Singleton;
|
||||
import javax.lang.model.type.NullType;
|
||||
import javax.ws.rs.Path;
|
||||
|
||||
import org.jclouds.Constants;
|
||||
import org.jclouds.fallbacks.MapHttp4xxCodesToExceptions;
|
||||
import org.jclouds.functions.IdentityFunction;
|
||||
import org.jclouds.functions.OnlyElementOrNull;
|
||||
import org.jclouds.http.HttpCommand;
|
||||
import org.jclouds.http.HttpCommandExecutorService;
|
||||
import org.jclouds.http.HttpRequest;
|
||||
import org.jclouds.http.HttpResponse;
|
||||
import org.jclouds.internal.ClassMethodArgs;
|
||||
import org.jclouds.internal.ClassMethodArgsAndReturnVal;
|
||||
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;
|
||||
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.internal.ClassInvokerArgs;
|
||||
import org.jclouds.internal.ClassInvokerArgsAndReturnVal;
|
||||
import org.jclouds.json.internal.GsonWrapper;
|
||||
import org.jclouds.logging.Logger;
|
||||
import org.jclouds.rest.AuthorizationException;
|
||||
import org.jclouds.rest.InvocationContext;
|
||||
import org.jclouds.rest.annotations.Delegate;
|
||||
import org.jclouds.rest.annotations.Fallback;
|
||||
import org.jclouds.util.Optionals2;
|
||||
import org.jclouds.util.Throwables2;
|
||||
import org.jclouds.rest.annotations.JAXBResponseParser;
|
||||
import org.jclouds.rest.annotations.OnlyElement;
|
||||
import org.jclouds.rest.annotations.ResponseParser;
|
||||
import org.jclouds.rest.annotations.SelectJson;
|
||||
import org.jclouds.rest.annotations.Transform;
|
||||
import org.jclouds.rest.annotations.Unwrap;
|
||||
import org.jclouds.rest.annotations.XMLResponseParser;
|
||||
import org.jclouds.rest.internal.RestAnnotationProcessor.InvokerKey;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.reflect.AbstractInvocationHandler;
|
||||
import com.google.common.reflect.Invokable;
|
||||
import com.google.common.reflect.Parameter;
|
||||
import com.google.common.reflect.TypeToken;
|
||||
import com.google.common.util.concurrent.FutureFallback;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.inject.Binding;
|
||||
|
@ -70,27 +116,23 @@ import com.google.inject.Injector;
|
|||
import com.google.inject.Key;
|
||||
import com.google.inject.Provides;
|
||||
import com.google.inject.ProvisionException;
|
||||
import com.google.inject.TypeLiteral;
|
||||
import com.google.inject.assistedinject.Assisted;
|
||||
import com.google.inject.util.Types;
|
||||
|
||||
/**
|
||||
* Generates RESTful clients from appropriately annotated interfaces.
|
||||
* <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 {@code Path} annotation,
|
||||
* and the returnval interface also has {@code Path}, these values are combined.
|
||||
* </li>
|
||||
* <li>ex. if the method with {@link Delegate} has a {@code Path} annotation, and the returnval interface also has
|
||||
* {@code 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>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>
|
||||
|
@ -102,51 +144,86 @@ public abstract class AsyncRestClientProxy extends AbstractInvocationHandler {
|
|||
public static interface Factory {
|
||||
Declaring declaring(Class<?> declaring);
|
||||
|
||||
Caller caller(ClassMethodArgs caller);
|
||||
Caller caller(ClassInvokerArgs caller);
|
||||
}
|
||||
|
||||
public final static class Declaring extends AsyncRestClientProxy {
|
||||
@Inject
|
||||
private Declaring(Injector injector, Function<ClassMethodArgsAndReturnVal, Optional<Object>> optionalConverter,
|
||||
private Declaring(Injector injector, Function<ClassInvokerArgsAndReturnVal, Optional<Object>> optionalConverter,
|
||||
HttpCommandExecutorService http, @Named(Constants.PROPERTY_USER_THREADS) ExecutorService userThreads,
|
||||
@Named("async") LoadingCache<ClassMethodArgs, Object> delegateMap, RestAnnotationProcessor.Factory rap,
|
||||
@Assisted Class<?> declaring) {
|
||||
super(injector, optionalConverter, http, userThreads, delegateMap, rap.declaring(declaring), declaring);
|
||||
@Named("async") LoadingCache<ClassInvokerArgs, Object> delegateMap, RestAnnotationProcessor.Factory rap,
|
||||
ParseSax.Factory parserFactory, @Assisted Class<?> declaring) {
|
||||
super(injector, optionalConverter, http, userThreads, delegateMap, rap.declaring(declaring), parserFactory,
|
||||
declaring);
|
||||
}
|
||||
}
|
||||
|
||||
public final static class Caller extends AsyncRestClientProxy {
|
||||
@Inject
|
||||
private Caller(Injector injector, Function<ClassMethodArgsAndReturnVal, Optional<Object>> optionalConverter,
|
||||
private Caller(Injector injector, Function<ClassInvokerArgsAndReturnVal, Optional<Object>> optionalConverter,
|
||||
HttpCommandExecutorService http, @Named(Constants.PROPERTY_USER_THREADS) ExecutorService userThreads,
|
||||
@Named("async") LoadingCache<ClassMethodArgs, Object> delegateMap, RestAnnotationProcessor.Factory rap,
|
||||
@Assisted ClassMethodArgs caller) {
|
||||
super(injector, optionalConverter, http, userThreads, delegateMap, rap.caller(caller), caller.getClazz());
|
||||
@Named("async") LoadingCache<ClassInvokerArgs, Object> delegateMap, RestAnnotationProcessor.Factory rap,
|
||||
ParseSax.Factory parserFactory, @Assisted ClassInvokerArgs caller) {
|
||||
super(injector, optionalConverter, http, userThreads, delegateMap, rap.caller(caller), parserFactory, caller
|
||||
.getClazz());
|
||||
}
|
||||
}
|
||||
|
||||
@Resource
|
||||
private Logger logger = Logger.NULL;
|
||||
|
||||
|
||||
private final Injector injector;
|
||||
private final HttpCommandExecutorService http;
|
||||
private final ExecutorService userThreads;
|
||||
private final Function<ClassMethodArgsAndReturnVal, Optional<Object>> optionalConverter;
|
||||
private final LoadingCache<ClassMethodArgs, Object> delegateMap;
|
||||
private final Function<ClassInvokerArgsAndReturnVal, Optional<Object>> optionalConverter;
|
||||
private final LoadingCache<ClassInvokerArgs, Object> delegateMap;
|
||||
private final RestAnnotationProcessor annotationProcessor;
|
||||
private final ParseSax.Factory parserFactory;
|
||||
private final Class<?> declaring;
|
||||
|
||||
private static final LoadingCache<Class<?>, Cache<InvokerKey, Invokable<?, ?>>> delegationMapCache = CacheBuilder
|
||||
.newBuilder().build(new CacheLoader<Class<?>, Cache<InvokerKey, Invokable<?, ?>>>() {
|
||||
public Cache<InvokerKey, Invokable<?, ?>> load(Class<?> declaring) throws ExecutionException {
|
||||
Cache<InvokerKey, Invokable<?, ?>> delegationMap = CacheBuilder.newBuilder()
|
||||
.<InvokerKey, Invokable<?, ?>> build();
|
||||
for (Method method : difference(ImmutableSet.copyOf(declaring.getMethods()),
|
||||
ImmutableSet.copyOf(Object.class.getMethods()))) {
|
||||
Invokable<?, ?> invoker = Invokable.from(method);
|
||||
if (isHttpMethod(invoker) || method.isAnnotationPresent(Delegate.class)) {
|
||||
delegationMap.get(new InvokerKey(invoker), returning(invoker));
|
||||
} else if (!method.getDeclaringClass().equals(declaring)) { // potentially overridden
|
||||
} else if (method.isAnnotationPresent(Provides.class)) {
|
||||
}
|
||||
}
|
||||
return delegationMap;
|
||||
}
|
||||
});
|
||||
|
||||
private Invokable<?, ?> getDelegateOrNull(Invokable<?, ?> in) {
|
||||
return delegationMapCache.getUnchecked(declaring).getIfPresent(new InvokerKey(in));
|
||||
}
|
||||
|
||||
private static boolean isHttpMethod(Invokable<?, ?> invoker) {
|
||||
return invoker.isAnnotationPresent(Path.class) || tryFindHttpMethod(invoker).isPresent()
|
||||
|| any(invoker.getParameters(), new Predicate<Parameter>() {
|
||||
public boolean apply(Parameter in) {
|
||||
return in.getType().isAssignableFrom(HttpRequest.class);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private AsyncRestClientProxy(Injector injector,
|
||||
Function<ClassMethodArgsAndReturnVal, Optional<Object>> optionalConverter, HttpCommandExecutorService http,
|
||||
ExecutorService userThreads, LoadingCache<ClassMethodArgs, Object> delegateMap,
|
||||
RestAnnotationProcessor annotationProcessor, Class<?> declaring) {
|
||||
Function<ClassInvokerArgsAndReturnVal, Optional<Object>> optionalConverter, HttpCommandExecutorService http,
|
||||
ExecutorService userThreads, LoadingCache<ClassInvokerArgs, Object> delegateMap,
|
||||
RestAnnotationProcessor annotationProcessor, ParseSax.Factory parserFactory, Class<?> declaring) {
|
||||
this.injector = injector;
|
||||
this.optionalConverter = optionalConverter;
|
||||
this.http = http;
|
||||
this.userThreads = userThreads;
|
||||
this.delegateMap = delegateMap;
|
||||
this.declaring = declaring;
|
||||
this.annotationProcessor = annotationProcessor;
|
||||
this.parserFactory = parserFactory;
|
||||
this.declaring = declaring;
|
||||
}
|
||||
|
||||
private static final Predicate<Annotation> isQualifierPresent = new Predicate<Annotation>() {
|
||||
|
@ -172,44 +249,42 @@ public abstract class AsyncRestClientProxy extends AbstractInvocationHandler {
|
|||
}
|
||||
}
|
||||
|
||||
public boolean isRestCall(Method method) {
|
||||
return annotationProcessor.getDelegateOrNull(method) != null
|
||||
private boolean isRestCall(Method method) {
|
||||
return getDelegateOrNull(Invokable.from(method)) != null
|
||||
&& ListenableFuture.class.isAssignableFrom(method.getReturnType());
|
||||
}
|
||||
|
||||
public Object propagateContextToDelegate(Method method, Object[] args) throws ExecutionException {
|
||||
Class<?> asyncClass = Optionals2.returnTypeOrTypeOfOptional(method);
|
||||
ClassMethodArgs cma = new ClassMethodArgs(asyncClass, method, args);
|
||||
private Object propagateContextToDelegate(Method method, Object[] args) throws ExecutionException {
|
||||
Class<?> asyncClass = returnTypeOrTypeOfOptional(method);
|
||||
ClassInvokerArgs cma = ClassInvokerArgs.builder().clazz(asyncClass).invoker(method).args(args).build();
|
||||
Object returnVal = delegateMap.get(cma);
|
||||
if (Optionals2.isReturnTypeOptional(method)) {
|
||||
ClassMethodArgsAndReturnVal cmar = ClassMethodArgsAndReturnVal.builder().fromClassMethodArgs(cma)
|
||||
if (isReturnTypeOptional(method)) {
|
||||
ClassInvokerArgsAndReturnVal cmar = ClassInvokerArgsAndReturnVal.builder().fromClassInvokerArgs(cma)
|
||||
.returnVal(returnVal).build();
|
||||
return optionalConverter.apply(cmar);
|
||||
}
|
||||
return returnVal;
|
||||
}
|
||||
|
||||
public Object lookupValueFromGuice(Method method) {
|
||||
private Object lookupValueFromGuice(Method method) {
|
||||
try {
|
||||
// TODO: tidy
|
||||
Type genericReturnType = method.getGenericReturnType();
|
||||
try {
|
||||
Annotation qualifier = Iterables.find(ImmutableList.copyOf(method.getAnnotations()), isQualifierPresent);
|
||||
Annotation qualifier = find(ImmutableList.copyOf(method.getAnnotations()), isQualifierPresent);
|
||||
return getInstanceOfTypeWithQualifier(genericReturnType, qualifier);
|
||||
} catch (ProvisionException e) {
|
||||
throw Throwables.propagate(e.getCause());
|
||||
throw propagate(e.getCause());
|
||||
} catch (RuntimeException e) {
|
||||
return instanceOfTypeOrPropagate(genericReturnType, e);
|
||||
}
|
||||
} catch (ProvisionException e) {
|
||||
AuthorizationException aex = Throwables2.getFirstThrowableOfType(e, AuthorizationException.class);
|
||||
AuthorizationException aex = getFirstThrowableOfType(e, AuthorizationException.class);
|
||||
if (aex != null)
|
||||
throw aex;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: tidy
|
||||
private Object instanceOfTypeOrPropagate(Type genericReturnType, RuntimeException e) {
|
||||
try {
|
||||
// look for an existing binding
|
||||
|
@ -218,7 +293,7 @@ public abstract class AsyncRestClientProxy extends AbstractInvocationHandler {
|
|||
return binding.getProvider().get();
|
||||
|
||||
// then, try looking via supplier
|
||||
binding = injector.getExistingBinding(Key.get(Types.newParameterizedType(Supplier.class, genericReturnType)));
|
||||
binding = injector.getExistingBinding(Key.get(newParameterizedType(Supplier.class, genericReturnType)));
|
||||
if (binding != null)
|
||||
return Supplier.class.cast(binding.getProvider().get()).get();
|
||||
|
||||
|
@ -229,7 +304,6 @@ public abstract class AsyncRestClientProxy extends AbstractInvocationHandler {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: tidy
|
||||
private Object getInstanceOfTypeWithQualifier(Type genericReturnType, Annotation qualifier) {
|
||||
// look for an existing binding
|
||||
Binding<?> binding = injector.getExistingBinding(Key.get(genericReturnType, qualifier));
|
||||
|
@ -237,40 +311,102 @@ public abstract class AsyncRestClientProxy extends AbstractInvocationHandler {
|
|||
return binding.getProvider().get();
|
||||
|
||||
// then, try looking via supplier
|
||||
binding = injector.getExistingBinding(Key.get(Types.newParameterizedType(Supplier.class, genericReturnType),
|
||||
qualifier));
|
||||
binding = injector
|
||||
.getExistingBinding(Key.get(newParameterizedType(Supplier.class, genericReturnType), qualifier));
|
||||
if (binding != null)
|
||||
return Supplier.class.cast(binding.getProvider().get()).get();
|
||||
|
||||
// else try to create an instance
|
||||
return injector.getInstance(Key.get(genericReturnType, qualifier));
|
||||
}
|
||||
|
||||
|
||||
@Deprecated
|
||||
static Function<HttpResponse, ?> createResponseParser(ParseSax.Factory parserFactory, Injector injector,
|
||||
Method method, HttpRequest request) {
|
||||
return createResponseParser(parserFactory, injector, Invokable.from(method), request);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@VisibleForTesting
|
||||
private static Function<HttpResponse, ?> createResponseParser(ParseSax.Factory parserFactory, Injector injector,
|
||||
Invokable<?, ?> method, HttpRequest request) {
|
||||
Function<HttpResponse, ?> transformer;
|
||||
Class<? extends HandlerWithResult<?>> handler = getSaxResponseParserClassOrNull(method);
|
||||
if (handler != null) {
|
||||
transformer = parserFactory.create(injector.getInstance(handler));
|
||||
} else {
|
||||
transformer = getTransformerForMethod(method, injector);
|
||||
}
|
||||
if (transformer instanceof InvocationContext<?>) {
|
||||
((InvocationContext<?>) transformer).setContext(request);
|
||||
}
|
||||
if (method.isAnnotationPresent(Transform.class)) {
|
||||
Function<?, ?> wrappingTransformer = injector.getInstance(method.getAnnotation(Transform.class).value());
|
||||
if (wrappingTransformer instanceof InvocationContext<?>) {
|
||||
((InvocationContext<?>) wrappingTransformer).setContext(request);
|
||||
}
|
||||
transformer = compose(Function.class.cast(wrappingTransformer), transformer);
|
||||
}
|
||||
return transformer;
|
||||
}
|
||||
|
||||
private static Class<? extends HandlerWithResult<?>> getSaxResponseParserClassOrNull(Invokable<?, ?> method) {
|
||||
XMLResponseParser annotation = method.getAnnotation(XMLResponseParser.class);
|
||||
if (annotation != null) {
|
||||
return annotation.value();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// TODO: refactor this out of here
|
||||
@VisibleForTesting
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public static Function<HttpResponse, ?> getTransformerForMethod(Invokable<?, ?> method, Injector injector) {
|
||||
Function<HttpResponse, ?> transformer;
|
||||
if (method.isAnnotationPresent(SelectJson.class)) {
|
||||
Type returnVal = getReturnTypeFor(method.getReturnType());
|
||||
if (method.isAnnotationPresent(OnlyElement.class))
|
||||
returnVal = newParameterizedType(Set.class, returnVal);
|
||||
transformer = new ParseFirstJsonValueNamed(injector.getInstance(GsonWrapper.class),
|
||||
TypeLiteral.get(returnVal), method.getAnnotation(SelectJson.class).value());
|
||||
if (method.isAnnotationPresent(OnlyElement.class))
|
||||
transformer = compose(new OnlyElementOrNull(), transformer);
|
||||
} else {
|
||||
transformer = injector.getInstance(getParserOrThrowException(method));
|
||||
}
|
||||
return transformer;
|
||||
}
|
||||
|
||||
private ListenableFuture<?> createListenableFutureForHttpRequestMappedToMethodAndArgs(Method method, Object[] args)
|
||||
throws ExecutionException {
|
||||
method = annotationProcessor.getDelegateOrNull(method);
|
||||
String name = method.getDeclaringClass().getSimpleName() + "." + method.getName();
|
||||
return createListenableFutureForHttpRequestMappedToMethodAndArgs(method, Invokable.from(method),
|
||||
args == null ? newArrayList(new Object[] { null }) : newArrayList(args));
|
||||
}
|
||||
|
||||
private ListenableFuture<?> createListenableFutureForHttpRequestMappedToMethodAndArgs(Method method,
|
||||
Invokable<?, ?> invoker, List<Object> args) throws ExecutionException {
|
||||
String name = invoker.getDeclaringClass().getSimpleName() + "." + invoker.getName();
|
||||
logger.trace(">> converting %s", name);
|
||||
FutureFallback<?> fallback = fallbacks.getUnchecked(method);
|
||||
FutureFallback<?> fallback = fallbacks.getUnchecked(invoker);
|
||||
// in case there is an exception creating the request, we should at least pass in args
|
||||
if (fallback instanceof InvocationContext) {
|
||||
InvocationContext.class.cast(fallback).setContext((HttpRequest) null);
|
||||
}
|
||||
ListenableFuture<?> result;
|
||||
try {
|
||||
GeneratedHttpRequest request = annotationProcessor.createRequest(method, args);
|
||||
GeneratedHttpRequest request = annotationProcessor.createRequest(method, invoker, newArrayList(args));
|
||||
if (fallback instanceof InvocationContext) {
|
||||
InvocationContext.class.cast(fallback).setContext(request);
|
||||
}
|
||||
logger.trace("<< converted %s to %s", name, request.getRequestLine());
|
||||
|
||||
Function<HttpResponse, ?> transformer = annotationProcessor.createResponseParser(method, request);
|
||||
Function<HttpResponse, ?> transformer = createResponseParser(parserFactory, injector, invoker, request);
|
||||
logger.trace("<< response from %s is parsed by %s", name, transformer.getClass().getSimpleName());
|
||||
|
||||
logger.debug(">> invoking %s", name);
|
||||
result = transform(makeListenable(http.submit(new HttpCommand(request)), userThreads), transformer);
|
||||
} catch (RuntimeException e) {
|
||||
AuthorizationException aex = Throwables2.getFirstThrowableOfType(e, AuthorizationException.class);
|
||||
AuthorizationException aex = getFirstThrowableOfType(e, AuthorizationException.class);
|
||||
if (aex != null)
|
||||
e = aex;
|
||||
try {
|
||||
|
@ -283,15 +419,16 @@ public abstract class AsyncRestClientProxy extends AbstractInvocationHandler {
|
|||
return withFallback(result, fallback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Client Proxy for :" + declaring.getName();
|
||||
}
|
||||
|
||||
private final LoadingCache<Method, FutureFallback<?>> fallbacks = CacheBuilder.newBuilder().build(
|
||||
new CacheLoader<Method, FutureFallback<?>>() {
|
||||
private final LoadingCache<Invokable<?, ?>, FutureFallback<?>> fallbacks = CacheBuilder.newBuilder().build(
|
||||
new CacheLoader<Invokable<?, ?>, FutureFallback<?>>() {
|
||||
|
||||
@Override
|
||||
public FutureFallback<?> load(Method key) throws Exception {
|
||||
public FutureFallback<?> load(Invokable<?, ?> key) throws Exception {
|
||||
Fallback annotation = key.getAnnotation(Fallback.class);
|
||||
if (annotation != null) {
|
||||
return injector.getInstance(annotation.value());
|
||||
|
@ -301,4 +438,96 @@ public abstract class AsyncRestClientProxy extends AbstractInvocationHandler {
|
|||
|
||||
});
|
||||
|
||||
private static final TypeToken<ListenableFuture<Boolean>> futureBooleanLiteral = new TypeToken<ListenableFuture<Boolean>>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
};
|
||||
private static final TypeToken<ListenableFuture<String>> futureStringLiteral = new TypeToken<ListenableFuture<String>>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
};
|
||||
private static final TypeToken<ListenableFuture<Void>> futureVoidLiteral = new TypeToken<ListenableFuture<Void>>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
};
|
||||
private static final TypeToken<ListenableFuture<URI>> futureURILiteral = new TypeToken<ListenableFuture<URI>>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
};
|
||||
private static final TypeToken<ListenableFuture<InputStream>> futureInputStreamLiteral = new TypeToken<ListenableFuture<InputStream>>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
};
|
||||
private static final TypeToken<ListenableFuture<HttpResponse>> futureHttpResponseLiteral = new TypeToken<ListenableFuture<HttpResponse>>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
};
|
||||
|
||||
@Deprecated
|
||||
static Key<? extends Function<HttpResponse, ?>> getParserOrThrowException(Method method) {
|
||||
return getParserOrThrowException(Invokable.from(method));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Key<? extends Function<HttpResponse, ?>> getParserOrThrowException(Invokable<?, ?> method) {
|
||||
|
||||
ResponseParser annotation = method.getAnnotation(ResponseParser.class);
|
||||
if (annotation == null) {
|
||||
if (method.getReturnType().equals(void.class) || method.getReturnType().equals(futureVoidLiteral)) {
|
||||
return Key.get(ReleasePayloadAndReturn.class);
|
||||
} else if (method.getReturnType().equals(boolean.class) || method.getReturnType().equals(Boolean.class)
|
||||
|| method.getReturnType().equals(futureBooleanLiteral)) {
|
||||
return Key.get(ReturnTrueIf2xx.class);
|
||||
} else if (method.getReturnType().equals(InputStream.class)
|
||||
|| method.getReturnType().equals(futureInputStreamLiteral)) {
|
||||
return Key.get(ReturnInputStream.class);
|
||||
} else if (method.getReturnType().equals(HttpResponse.class)
|
||||
|| method.getReturnType().equals(futureHttpResponseLiteral)) {
|
||||
return Key.get(Class.class.cast(IdentityFunction.class));
|
||||
} else if (RestAnnotationProcessor.getAcceptHeadersOrNull(method).contains(APPLICATION_JSON)) {
|
||||
return getJsonParserKeyForMethod(method);
|
||||
} else if (RestAnnotationProcessor.getAcceptHeadersOrNull(method).contains(APPLICATION_XML)
|
||||
|| method.isAnnotationPresent(JAXBResponseParser.class)) {
|
||||
return getJAXBParserKeyForMethod(method);
|
||||
} else if (method.getReturnType().equals(String.class) || method.getReturnType().equals(futureStringLiteral)) {
|
||||
return Key.get(ReturnStringIf2xx.class);
|
||||
} else if (method.getReturnType().equals(URI.class) || method.getReturnType().equals(futureURILiteral)) {
|
||||
return Key.get(ParseURIFromListOrLocationHeaderIf20x.class);
|
||||
} else {
|
||||
throw new IllegalStateException("You must specify a ResponseParser annotation on: " + method.toString());
|
||||
}
|
||||
}
|
||||
return Key.get(annotation.value());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Key<? extends Function<HttpResponse, ?>> getJAXBParserKeyForMethod(Invokable<?, ?> method) {
|
||||
Optional<Type> configuredReturnVal = Optional.absent();
|
||||
if (method.isAnnotationPresent(JAXBResponseParser.class)) {
|
||||
Type configuredClass = method.getAnnotation(JAXBResponseParser.class).value();
|
||||
configuredReturnVal = configuredClass.equals(NullType.class) ? Optional.<Type> absent() : Optional
|
||||
.<Type> of(configuredClass);
|
||||
}
|
||||
Type returnVal = configuredReturnVal.or(getReturnTypeFor(method.getReturnType()));
|
||||
Type parserType = newParameterizedType(ParseXMLWithJAXB.class, returnVal);
|
||||
return (Key<? extends Function<HttpResponse, ?>>) Key.get(parserType);
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked" })
|
||||
private static Key<? extends Function<HttpResponse, ?>> getJsonParserKeyForMethod(Invokable<?, ?> method) {
|
||||
ParameterizedType parserType;
|
||||
if (method.isAnnotationPresent(Unwrap.class)) {
|
||||
parserType = newParameterizedType(UnwrapOnlyJsonValue.class, getReturnTypeFor(method.getReturnType()));
|
||||
} else {
|
||||
parserType = newParameterizedType(ParseJson.class, getReturnTypeFor(method.getReturnType()));
|
||||
}
|
||||
return (Key<? extends Function<HttpResponse, ?>>) Key.get(parserType);
|
||||
}
|
||||
|
||||
private static Type getReturnTypeFor(TypeToken<?> typeToken) {
|
||||
Type returnVal = typeToken.getType();
|
||||
if (typeToken.getRawType().getTypeParameters().length == 0) {
|
||||
returnVal = typeToken.getRawType();
|
||||
} else if (typeToken.getRawType().equals(ListenableFuture.class)) {
|
||||
ParameterizedType futureType = (ParameterizedType) typeToken.getType();
|
||||
returnVal = futureType.getActualTypeArguments()[0];
|
||||
if (returnVal instanceof WildcardType)
|
||||
returnVal = WildcardType.class.cast(returnVal).getUpperBounds()[0];
|
||||
}
|
||||
return returnVal;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,13 +20,13 @@ package org.jclouds.rest.internal;
|
|||
|
||||
import static com.google.common.reflect.Reflection.newProxy;
|
||||
|
||||
import org.jclouds.internal.ClassMethodArgs;
|
||||
import org.jclouds.internal.ClassInvokerArgs;
|
||||
import org.jclouds.rest.internal.AsyncRestClientProxy.Factory;
|
||||
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.inject.Inject;
|
||||
|
||||
public final class CreateAsyncClientForCaller extends CacheLoader<ClassMethodArgs, Object> {
|
||||
public final class CreateAsyncClientForCaller extends CacheLoader<ClassInvokerArgs, Object> {
|
||||
private final Factory factory;
|
||||
|
||||
@Inject
|
||||
|
@ -35,7 +35,7 @@ public final class CreateAsyncClientForCaller extends CacheLoader<ClassMethodArg
|
|||
}
|
||||
|
||||
@Override
|
||||
public Object load(ClassMethodArgs from) {
|
||||
public Object load(ClassInvokerArgs from) {
|
||||
return newProxy(from.getClazz(), factory.caller(from));
|
||||
}
|
||||
}
|
|
@ -27,7 +27,7 @@ import javax.inject.Inject;
|
|||
import javax.inject.Named;
|
||||
|
||||
import org.jclouds.concurrent.internal.SyncProxy;
|
||||
import org.jclouds.internal.ClassMethodArgs;
|
||||
import org.jclouds.internal.ClassInvokerArgs;
|
||||
import org.jclouds.util.Optionals2;
|
||||
|
||||
import com.google.common.cache.CacheLoader;
|
||||
|
@ -39,22 +39,22 @@ import com.google.common.cache.LoadingCache;
|
|||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
public class CreateClientForCaller extends CacheLoader<ClassMethodArgs, Object> {
|
||||
public class CreateClientForCaller extends CacheLoader<ClassInvokerArgs, Object> {
|
||||
private final SyncProxy.Factory factory;
|
||||
private final LoadingCache<ClassMethodArgs, Object> asyncMap;
|
||||
private final LoadingCache<ClassInvokerArgs, Object> asyncMap;
|
||||
private final Map<Class<?>, Class<?>> sync2Async;
|
||||
|
||||
@Inject
|
||||
private CreateClientForCaller(SyncProxy.Factory factory,
|
||||
@Named("async") LoadingCache<ClassMethodArgs, Object> asyncMap, Map<Class<?>, Class<?>> sync2Async) {
|
||||
@Named("async") LoadingCache<ClassInvokerArgs, Object> asyncMap, Map<Class<?>, Class<?>> sync2Async) {
|
||||
this.factory = factory;
|
||||
this.asyncMap = asyncMap;
|
||||
this.sync2Async = sync2Async;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object load(ClassMethodArgs from) {
|
||||
Class<?> syncClass = Optionals2.returnTypeOrTypeOfOptional(from.getMethod());
|
||||
public Object load(ClassInvokerArgs from) {
|
||||
Class<?> syncClass = Optionals2.returnTypeOrTypeOfOptional(from.getInvoker());
|
||||
Class<?> asyncClass = sync2Async.get(syncClass);
|
||||
checkState(asyncClass != null, "configuration error, sync class " + syncClass + " not mapped to an async class");
|
||||
Object asyncClient = asyncMap.getUnchecked(from);
|
||||
|
|
|
@ -28,13 +28,14 @@ import java.util.List;
|
|||
|
||||
import org.jclouds.http.HttpRequest;
|
||||
import org.jclouds.http.HttpRequestFilter;
|
||||
import org.jclouds.internal.ClassMethodArgs;
|
||||
import org.jclouds.internal.ClassInvokerArgs;
|
||||
import org.jclouds.io.Payload;
|
||||
import org.jclouds.javax.annotation.Nullable;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.reflect.Invokable;
|
||||
|
||||
/**
|
||||
* Represents a request generated from annotations
|
||||
|
@ -54,9 +55,10 @@ public class GeneratedHttpRequest extends HttpRequest {
|
|||
public static class Builder extends HttpRequest.Builder<Builder> {
|
||||
protected Class<?> declaring;
|
||||
protected Method javaMethod;
|
||||
protected Invokable<?, ?> invoker;
|
||||
// args can be null, so cannot use immutable list
|
||||
protected List<Object> args = Lists.newArrayList();
|
||||
protected Optional<ClassMethodArgs> caller = Optional.absent();
|
||||
protected Optional<ClassInvokerArgs> caller = Optional.absent();
|
||||
|
||||
/**
|
||||
* @see GeneratedHttpRequest#getDeclaring()
|
||||
|
@ -69,11 +71,21 @@ public class GeneratedHttpRequest extends HttpRequest {
|
|||
/**
|
||||
* @see GeneratedHttpRequest#getJavaMethod()
|
||||
*/
|
||||
@Deprecated
|
||||
public Builder javaMethod(Method javaMethod) {
|
||||
this.javaMethod = checkNotNull(javaMethod, "javaMethod");
|
||||
this.invoker(Invokable.from(javaMethod));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see GeneratedHttpRequest#getInvoker()
|
||||
*/
|
||||
public Builder invoker(Invokable<?, ?> invoker) {
|
||||
this.invoker = checkNotNull(invoker, "invoker");
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see GeneratedHttpRequest#getArgs()
|
||||
*/
|
||||
|
@ -100,20 +112,21 @@ public class GeneratedHttpRequest extends HttpRequest {
|
|||
/**
|
||||
* @see GeneratedHttpRequest#getCaller()
|
||||
*/
|
||||
public Builder caller(@Nullable ClassMethodArgs caller) {
|
||||
public Builder caller(@Nullable ClassInvokerArgs caller) {
|
||||
this.caller = Optional.fromNullable(caller);
|
||||
return this;
|
||||
}
|
||||
|
||||
public GeneratedHttpRequest build() {
|
||||
return new GeneratedHttpRequest(method, endpoint, headers.build(), payload, declaring, javaMethod,
|
||||
args, filters.build(), caller);
|
||||
return new GeneratedHttpRequest(method, endpoint, headers.build(), payload, declaring, javaMethod, invoker,
|
||||
args, filters.build(), caller);
|
||||
}
|
||||
|
||||
public Builder fromGeneratedHttpRequest(GeneratedHttpRequest in) {
|
||||
return super.fromHttpRequest(in)
|
||||
.declaring(in.getDeclaring())
|
||||
.javaMethod(in.getJavaMethod())
|
||||
.invoker(in.invoker)
|
||||
.args(in.getArgs())
|
||||
.caller(in.getCaller().orNull());
|
||||
}
|
||||
|
@ -126,15 +139,17 @@ public class GeneratedHttpRequest extends HttpRequest {
|
|||
|
||||
private final Class<?> declaring;
|
||||
private final Method javaMethod;
|
||||
private final Invokable<?, ?> invoker;
|
||||
private final List<Object> args;
|
||||
private final Optional<ClassMethodArgs> caller;
|
||||
private final Optional<ClassInvokerArgs> caller;
|
||||
|
||||
protected GeneratedHttpRequest(String method, URI endpoint, Multimap<String, String> headers,
|
||||
@Nullable Payload payload, Class<?> declaring, Method javaMethod, Iterable<Object> args,
|
||||
Iterable<HttpRequestFilter> filters, Optional<ClassMethodArgs> caller) {
|
||||
@Nullable Payload payload, Class<?> declaring, Method javaMethod, Invokable<?, ?> invoker,
|
||||
Iterable<Object> args, Iterable<HttpRequestFilter> filters, Optional<ClassInvokerArgs> caller) {
|
||||
super(method, endpoint, headers, payload, filters);
|
||||
this.declaring = checkNotNull(declaring, "declaring");
|
||||
this.javaMethod = checkNotNull(javaMethod, "javaMethod");
|
||||
this.invoker = checkNotNull(invoker, "invoker");
|
||||
// TODO make immutable. ImmutableList.of() doesn't accept nulls
|
||||
this.args = Lists.newArrayList(checkNotNull(args, "args"));
|
||||
this.caller = checkNotNull(caller, "caller");
|
||||
|
@ -144,15 +159,23 @@ public class GeneratedHttpRequest extends HttpRequest {
|
|||
return declaring;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated see {@link #getInvoker()}
|
||||
*/
|
||||
@Deprecated
|
||||
public Method getJavaMethod() {
|
||||
return javaMethod;
|
||||
}
|
||||
|
||||
public Invokable<?,?> getInvoker() {
|
||||
return invoker;
|
||||
}
|
||||
|
||||
public List<Object> getArgs() {
|
||||
return Collections.unmodifiableList(args);
|
||||
}
|
||||
|
||||
public Optional<ClassMethodArgs> getCaller() {
|
||||
public Optional<ClassInvokerArgs> getCaller() {
|
||||
return caller;
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,106 +0,0 @@
|
|||
/**
|
||||
* 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.rest.internal;
|
||||
|
||||
import static com.google.common.collect.Sets.difference;
|
||||
import static org.jclouds.rest.internal.RestAnnotationProcessor.getHttpMethods;
|
||||
import static org.jclouds.rest.internal.RestAnnotationProcessor.methodToIndexOfParamToBinderParamAnnotation;
|
||||
import static org.jclouds.rest.internal.RestAnnotationProcessor.methodToIndexOfParamToEndpointAnnotations;
|
||||
import static org.jclouds.rest.internal.RestAnnotationProcessor.methodToIndexOfParamToEndpointParamAnnotations;
|
||||
import static org.jclouds.rest.internal.RestAnnotationProcessor.methodToIndexOfParamToFormParamAnnotations;
|
||||
import static org.jclouds.rest.internal.RestAnnotationProcessor.methodToIndexOfParamToHeaderParamAnnotations;
|
||||
import static org.jclouds.rest.internal.RestAnnotationProcessor.methodToIndexOfParamToParamParserAnnotations;
|
||||
import static org.jclouds.rest.internal.RestAnnotationProcessor.methodToIndexOfParamToPartParamAnnotations;
|
||||
import static org.jclouds.rest.internal.RestAnnotationProcessor.methodToIndexOfParamToPathParamAnnotations;
|
||||
import static org.jclouds.rest.internal.RestAnnotationProcessor.methodToIndexOfParamToPostParamAnnotations;
|
||||
import static org.jclouds.rest.internal.RestAnnotationProcessor.methodToIndexOfParamToQueryParamAnnotations;
|
||||
import static org.jclouds.rest.internal.RestAnnotationProcessor.methodToIndexOfParamToWrapWithAnnotation;
|
||||
import static org.jclouds.rest.internal.RestAnnotationProcessor.methodToIndexesOfOptions;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.inject.Singleton;
|
||||
import javax.ws.rs.Path;
|
||||
|
||||
import org.jclouds.http.HttpRequest;
|
||||
import org.jclouds.logging.Logger;
|
||||
import org.jclouds.rest.RestContext;
|
||||
import org.jclouds.rest.annotations.Delegate;
|
||||
import org.jclouds.rest.internal.RestAnnotationProcessor.MethodKey;
|
||||
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.util.concurrent.Callables;
|
||||
import com.google.inject.Provides;
|
||||
|
||||
/**
|
||||
* seeds the annotation cache located at
|
||||
* {@link RestAnnotationProcessor#delegationMap}. Note this call is only
|
||||
* intended to be called once per {@link RestContext} and avoids expensive
|
||||
* lookups on each call.
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Singleton
|
||||
public final class SeedAnnotationCache extends CacheLoader<Class<?>, Cache<MethodKey, Method>> {
|
||||
@Resource
|
||||
private Logger logger = Logger.NULL;
|
||||
|
||||
@Override
|
||||
public Cache<MethodKey, Method> load(Class<?> declaring) throws ExecutionException {
|
||||
Cache<MethodKey, Method> delegationMap = CacheBuilder.newBuilder().<MethodKey, Method>build();
|
||||
for (Method method : difference(ImmutableSet.copyOf(declaring.getMethods()), ImmutableSet.copyOf(Object.class
|
||||
.getMethods()))) {
|
||||
if (isHttpMethod(method) || method.isAnnotationPresent(Delegate.class)) {
|
||||
for (int index = 0; index < method.getParameterTypes().length; index++) {
|
||||
methodToIndexOfParamToBinderParamAnnotation.get(method).get(index);
|
||||
methodToIndexOfParamToWrapWithAnnotation.get(method).get(index);
|
||||
methodToIndexOfParamToHeaderParamAnnotations.get(method).get(index);
|
||||
methodToIndexOfParamToFormParamAnnotations.get(method).get(index);
|
||||
methodToIndexOfParamToQueryParamAnnotations.get(method).get(index);
|
||||
methodToIndexOfParamToEndpointAnnotations.get(method).get(index);
|
||||
methodToIndexOfParamToEndpointParamAnnotations.get(method).get(index);
|
||||
methodToIndexOfParamToPathParamAnnotations.get(method).get(index);
|
||||
methodToIndexOfParamToPostParamAnnotations.get(method).get(index);
|
||||
methodToIndexOfParamToParamParserAnnotations.get(method).get(index);
|
||||
methodToIndexOfParamToPartParamAnnotations.get(method).get(index);
|
||||
methodToIndexesOfOptions.get(method);
|
||||
}
|
||||
delegationMap.get(new MethodKey(method), Callables.returning(method));
|
||||
} else if (!method.getDeclaringClass().equals(declaring)) {
|
||||
logger.trace("skipping potentially overridden method %s", method);
|
||||
} else if (method.isAnnotationPresent(Provides.class)) {
|
||||
logger.trace("skipping provider method %s", method);
|
||||
} else {
|
||||
logger.trace("Method is not annotated as either http or provider method: %s", method);
|
||||
}
|
||||
}
|
||||
return delegationMap;
|
||||
}
|
||||
|
||||
public static boolean isHttpMethod(Method method) {
|
||||
return method.isAnnotationPresent(Path.class) || !getHttpMethods(method).isEmpty()
|
||||
|| ImmutableSet.copyOf(method.getParameterTypes()).contains(HttpRequest.class);
|
||||
}
|
||||
|
||||
}
|
|
@ -24,18 +24,28 @@ import java.lang.reflect.Type;
|
|||
import java.lang.reflect.WildcardType;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.reflect.Invokable;
|
||||
import com.google.common.reflect.TypeToken;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
public class Optionals2 {
|
||||
public static Class<?> returnTypeOrTypeOfOptional(Invokable<?, ?> method) {
|
||||
TypeToken<?> type = method.getReturnType();
|
||||
return returnTypeOrTypeOfOptional(type.getRawType(), type.getType());
|
||||
}
|
||||
|
||||
public static Class<?> returnTypeOrTypeOfOptional(Method method) {
|
||||
boolean optional = isReturnTypeOptional(method);
|
||||
Class<?> syncClass;
|
||||
if (optional) {
|
||||
ParameterizedType futureType = ParameterizedType.class.cast(method.getGenericReturnType());
|
||||
Class<?> syncClass = method.getReturnType();
|
||||
Type genericType = method.getGenericReturnType();
|
||||
return returnTypeOrTypeOfOptional(syncClass, genericType);
|
||||
}
|
||||
|
||||
private static Class<?> returnTypeOrTypeOfOptional(Class<?> syncClass, Type genericType) {
|
||||
if (syncClass.isAssignableFrom(Optional.class)) {
|
||||
ParameterizedType futureType = ParameterizedType.class.cast(genericType);
|
||||
// TODO: error checking in case this is a type, not a class.
|
||||
Type t = futureType.getActualTypeArguments()[0];
|
||||
if (t instanceof WildcardType) {
|
||||
|
@ -43,14 +53,12 @@ public class Optionals2 {
|
|||
}
|
||||
syncClass = Class.class.cast(t);
|
||||
} else {
|
||||
syncClass = method.getReturnType();
|
||||
}
|
||||
return syncClass;
|
||||
}
|
||||
|
||||
public static boolean isReturnTypeOptional(Method method) {
|
||||
boolean optional = method.getReturnType().isAssignableFrom(Optional.class);
|
||||
return optional;
|
||||
return method.getReturnType().isAssignableFrom(Optional.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ import java.util.concurrent.ExecutionException;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import org.jclouds.internal.ClassMethodArgs;
|
||||
import org.jclouds.internal.ClassInvokerArgs;
|
||||
import org.jclouds.rest.functions.AlwaysPresentImplicitOptionalConverter;
|
||||
import org.testng.annotations.BeforeMethod;
|
||||
import org.testng.annotations.Test;
|
||||
|
@ -103,7 +103,7 @@ public class SyncProxyTest {
|
|||
}
|
||||
|
||||
private Sync syncProxyForTimeouts(ImmutableMap<String, Long> timeouts) throws NoSuchMethodException {
|
||||
LoadingCache<ClassMethodArgs, Object> cache = CacheBuilder.newBuilder().build(
|
||||
LoadingCache<ClassInvokerArgs, Object> cache = CacheBuilder.newBuilder().build(
|
||||
CacheLoader.from(Functions.<Object> constant(null)));
|
||||
return newProxy(
|
||||
Sync.class,
|
||||
|
|
|
@ -31,11 +31,12 @@ import org.jclouds.http.HttpResponse;
|
|||
import org.jclouds.io.Payload;
|
||||
import org.jclouds.io.Payloads;
|
||||
import org.jclouds.json.config.GsonModule;
|
||||
import org.jclouds.rest.internal.RestAnnotationProcessor;
|
||||
import org.jclouds.rest.internal.AsyncRestClientProxy;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.reflect.Invokable;
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
|
||||
|
@ -55,8 +56,8 @@ public abstract class BaseParserTest<T, G> {
|
|||
@SuppressWarnings("unchecked")
|
||||
protected Function<HttpResponse, T> parser(Injector i) {
|
||||
try {
|
||||
return (Function<HttpResponse, T>) RestAnnotationProcessor.getTransformerForMethod(getClass()
|
||||
.getMethod("expected"), i);
|
||||
return (Function<HttpResponse, T>) AsyncRestClientProxy.getTransformerForMethod(
|
||||
Invokable.from(getClass().getMethod("expected")), i);
|
||||
} catch (Exception e) {
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ import static org.testng.Assert.assertTrue;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.jclouds.internal.ClassMethodArgsAndReturnVal;
|
||||
import org.jclouds.internal.ClassInvokerArgsAndReturnVal;
|
||||
import org.jclouds.rest.annotations.Delegate;
|
||||
import org.jclouds.rest.annotations.SinceApiVersion;
|
||||
import org.jclouds.rest.functions.PresentWhenApiVersionLexicographicallyAtOrAfterSinceApiVersion.Loader;
|
||||
|
@ -120,7 +120,7 @@ public class PresentWhenApiVersionLexicographicallyAtOrAfterSinceApiVersionTest
|
|||
}
|
||||
|
||||
public void testCacheIsFasterWhenNoAnnotationPresent() {
|
||||
ClassMethodArgsAndReturnVal keyPairApi = getKeyPairApi();
|
||||
ClassInvokerArgsAndReturnVal keyPairApi = getKeyPairApi();
|
||||
ImplicitOptionalConverter fn = forApiVersion("2011-07-15");
|
||||
Stopwatch watch = new Stopwatch().start();
|
||||
fn.apply(keyPairApi);
|
||||
|
@ -134,7 +134,7 @@ public class PresentWhenApiVersionLexicographicallyAtOrAfterSinceApiVersionTest
|
|||
}
|
||||
|
||||
public void testCacheIsFasterWhenAnnotationPresent() {
|
||||
ClassMethodArgsAndReturnVal floatingIpApi = getKeyPairApi();
|
||||
ClassInvokerArgsAndReturnVal floatingIpApi = getKeyPairApi();
|
||||
ImplicitOptionalConverter fn = forApiVersion("2011-07-15");
|
||||
Stopwatch watch = new Stopwatch().start();
|
||||
fn.apply(floatingIpApi);
|
||||
|
@ -148,22 +148,22 @@ public class PresentWhenApiVersionLexicographicallyAtOrAfterSinceApiVersionTest
|
|||
|
||||
}
|
||||
|
||||
ClassMethodArgsAndReturnVal getFloatingIPApi() {
|
||||
ClassInvokerArgsAndReturnVal getFloatingIPApi() {
|
||||
return getApi("Tag", TagAsyncApi.class);
|
||||
}
|
||||
|
||||
ClassMethodArgsAndReturnVal getKeyPairApi() {
|
||||
ClassInvokerArgsAndReturnVal getKeyPairApi() {
|
||||
return getApi("KeyPair", KeyPairAsyncApi.class);
|
||||
}
|
||||
|
||||
ClassMethodArgsAndReturnVal getVpcApi() {
|
||||
ClassInvokerArgsAndReturnVal getVpcApi() {
|
||||
return getApi("Vpc", VpcAsyncApi.class);
|
||||
}
|
||||
|
||||
ClassMethodArgsAndReturnVal getApi(String name, Class<?> type) {
|
||||
ClassInvokerArgsAndReturnVal getApi(String name, Class<?> type) {
|
||||
try {
|
||||
return ClassMethodArgsAndReturnVal.builder().clazz(type)
|
||||
.method(EC2AsyncApi.class.getDeclaredMethod("get" + name + "ApiForRegion", String.class))
|
||||
return ClassInvokerArgsAndReturnVal.builder().clazz(type)
|
||||
.invoker(EC2AsyncApi.class.getDeclaredMethod("get" + name + "ApiForRegion", String.class))
|
||||
.args(new Object[] { "region" }).returnVal("present").build();
|
||||
} catch (Exception e) {
|
||||
throw propagate(e);
|
||||
|
|
|
@ -22,8 +22,6 @@ import static com.google.common.hash.Hashing.md5;
|
|||
import static org.easymock.EasyMock.createMock;
|
||||
import static org.eclipse.jetty.http.HttpHeaders.TRANSFER_ENCODING;
|
||||
import static org.jclouds.io.ByteSources.asByteSource;
|
||||
import static org.jclouds.rest.internal.RestAnnotationProcessor.createResponseParser;
|
||||
import static org.jclouds.rest.internal.RestAnnotationProcessor.getSaxResponseParserClassOrNull;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.assertNull;
|
||||
|
||||
|
@ -44,6 +42,7 @@ import org.jclouds.http.functions.ParseSax;
|
|||
import org.jclouds.io.MutableContentMetadata;
|
||||
import org.jclouds.javax.annotation.Nullable;
|
||||
import org.jclouds.rest.annotations.Fallback;
|
||||
import org.jclouds.rest.annotations.XMLResponseParser;
|
||||
import org.jclouds.util.Strings2;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
|
@ -171,11 +170,13 @@ public abstract class BaseRestApiTest {
|
|||
}
|
||||
|
||||
protected void assertSaxResponseParserClassEquals(Method method, @Nullable Class<?> parserClass) {
|
||||
assertEquals(getSaxResponseParserClassOrNull(method), parserClass);
|
||||
XMLResponseParser annotation = method.getAnnotation(XMLResponseParser.class);
|
||||
Class<?> expected = (annotation != null) ? annotation.value() :null;
|
||||
assertEquals(expected, parserClass);
|
||||
}
|
||||
|
||||
protected void assertResponseParserClassEquals(Method method, HttpRequest request, @Nullable Class<?> parserClass) {
|
||||
assertEquals(createResponseParser(parserFactory, injector, method, request).getClass(), parserClass);
|
||||
assertEquals(AsyncRestClientProxy.createResponseParser(parserFactory, injector, method, request).getClass(), parserClass);
|
||||
}
|
||||
|
||||
protected RestAnnotationProcessor factory(Class<?> clazz) {
|
||||
|
|
|
@ -64,7 +64,6 @@ import javax.ws.rs.PathParam;
|
|||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
import org.eclipse.jetty.http.HttpHeaders;
|
||||
import org.jclouds.ContextBuilder;
|
||||
|
@ -81,7 +80,6 @@ import org.jclouds.http.functions.ParseFirstJsonValueNamed;
|
|||
import org.jclouds.http.functions.ParseJson;
|
||||
import org.jclouds.http.functions.ParseSax;
|
||||
import org.jclouds.http.functions.ParseURIFromListOrLocationHeaderIf20x;
|
||||
import org.jclouds.http.functions.ParseXMLWithJAXB;
|
||||
import org.jclouds.http.functions.ReturnInputStream;
|
||||
import org.jclouds.http.functions.ReturnStringIf2xx;
|
||||
import org.jclouds.http.functions.ReturnTrueIf2xx;
|
||||
|
@ -90,7 +88,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.internal.ClassInvokerArgsAndReturnVal;
|
||||
import org.jclouds.io.Payload;
|
||||
import org.jclouds.io.PayloadEnclosing;
|
||||
import org.jclouds.io.Payloads;
|
||||
|
@ -106,7 +104,6 @@ import org.jclouds.rest.annotations.Endpoint;
|
|||
import org.jclouds.rest.annotations.EndpointParam;
|
||||
import org.jclouds.rest.annotations.FormParams;
|
||||
import org.jclouds.rest.annotations.Headers;
|
||||
import org.jclouds.rest.annotations.JAXBResponseParser;
|
||||
import org.jclouds.rest.annotations.MapBinder;
|
||||
import org.jclouds.rest.annotations.OnlyElement;
|
||||
import org.jclouds.rest.annotations.OverrideRequestFilters;
|
||||
|
@ -129,7 +126,6 @@ import org.jclouds.rest.config.AsyncClientProvider;
|
|||
import org.jclouds.rest.config.RestClientModule;
|
||||
import org.jclouds.rest.functions.ImplicitOptionalConverter;
|
||||
import org.jclouds.util.Strings2;
|
||||
import org.jclouds.xml.XMLParser;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.DataProvider;
|
||||
|
@ -181,7 +177,6 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
}).annotatedWith(Localhost2.class).toInstance(Suppliers.ofInstance(URI.create("http://localhost:1111")));
|
||||
bind(IOExceptionRetryHandler.class).toInstance(IOExceptionRetryHandler.NEVER_RETRY);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Path("/client/{jclouds.api-version}")
|
||||
|
@ -394,7 +389,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
bind(ImplicitOptionalConverter.class).toInstance(new ImplicitOptionalConverter() {
|
||||
|
||||
@Override
|
||||
public Optional<Object> apply(ClassMethodArgsAndReturnVal input) {
|
||||
public Optional<Object> apply(ClassInvokerArgsAndReturnVal input) {
|
||||
return Optional.absent();
|
||||
}
|
||||
|
||||
|
@ -1177,7 +1172,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
assertResponseParserClassEquals(method, request, ParseJson.class);
|
||||
// now test that it works!
|
||||
|
||||
Function<HttpResponse, View> parser = (Function<HttpResponse, View>) RestAnnotationProcessor
|
||||
Function<HttpResponse, View> parser = (Function<HttpResponse, View>) AsyncRestClientProxy
|
||||
.createResponseParser(parserFactory, injector, method, request);
|
||||
|
||||
assertEquals(parser.apply(HttpResponse.builder().statusCode(200).message("ok").payload("{ foo:\"bar\"}").build()).foo, "bar");
|
||||
|
@ -1192,7 +1187,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
assertResponseParserClassEquals(method, request, ParseJson.class);
|
||||
// now test that it works!
|
||||
|
||||
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) RestAnnotationProcessor
|
||||
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) AsyncRestClientProxy
|
||||
.createResponseParser(parserFactory, injector, method, request);
|
||||
|
||||
assertEquals(parser.apply(HttpResponse.builder().statusCode(200).message("ok").payload("{ foo:\"bar\"}").build()),
|
||||
|
@ -1208,7 +1203,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
assertResponseParserClassEquals(method, request, ParseJson.class);
|
||||
// now test that it works!
|
||||
|
||||
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) RestAnnotationProcessor
|
||||
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) AsyncRestClientProxy
|
||||
.createResponseParser(parserFactory, injector, method, request);
|
||||
|
||||
assertEquals(parser.apply(HttpResponse.builder().statusCode(200).message("ok").payload("{ foo:\"bar\"}").build()),
|
||||
|
@ -1224,7 +1219,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
assertResponseParserClassEquals(method, request, ParseJson.class);
|
||||
// now test that it works!
|
||||
|
||||
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) RestAnnotationProcessor
|
||||
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) AsyncRestClientProxy
|
||||
.createResponseParser(parserFactory, injector, method, request);
|
||||
|
||||
assertEquals(parser.apply(HttpResponse.builder().statusCode(200).message("ok").payload("{ foo:\"bar\"}").build()),
|
||||
|
@ -1240,7 +1235,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class);
|
||||
// now test that it works!
|
||||
|
||||
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) RestAnnotationProcessor
|
||||
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) AsyncRestClientProxy
|
||||
.createResponseParser(parserFactory, injector, method, request);
|
||||
|
||||
assertEquals(parser.apply(HttpResponse.builder().statusCode(200).message("ok").payload("{ foo:\"bar\"}").build()), "bar");
|
||||
|
@ -1255,7 +1250,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
assertResponseParserClassEquals(method, request, ParseFirstJsonValueNamed.class);
|
||||
// now test that it works!
|
||||
|
||||
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) RestAnnotationProcessor
|
||||
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) AsyncRestClientProxy
|
||||
.createResponseParser(parserFactory, injector, method, request);
|
||||
|
||||
assertEquals(parser.apply(HttpResponse.builder().statusCode(200).message("ok").payload("{ foo:\"bar\"}").build()), "bar");
|
||||
|
@ -1276,7 +1271,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class);
|
||||
// now test that it works!
|
||||
|
||||
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) RestAnnotationProcessor
|
||||
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) AsyncRestClientProxy
|
||||
.createResponseParser(parserFactory, injector, method, request);
|
||||
|
||||
assertEquals(parser.apply(HttpResponse.builder().statusCode(200).message("ok").payload("{ foo:\"bar\"}").build()), "bar");
|
||||
|
@ -1291,7 +1286,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class);
|
||||
// now test that it works!
|
||||
|
||||
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) RestAnnotationProcessor
|
||||
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) AsyncRestClientProxy
|
||||
.createResponseParser(parserFactory, injector, method, request);
|
||||
|
||||
assertEquals(parser.apply(HttpResponse.builder().statusCode(200).message("ok").payload("{\"runit\":[\"0.7.0\",\"0.7.1\"]}").build()),
|
||||
|
@ -1306,7 +1301,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class);
|
||||
// now test that it works!
|
||||
|
||||
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) RestAnnotationProcessor
|
||||
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) AsyncRestClientProxy
|
||||
.createResponseParser(parserFactory, injector, method, request);
|
||||
|
||||
assertEquals(parser.apply(HttpResponse.builder().statusCode(200).message("ok").payload("{\"runit\":[\"0.7.0\",\"0.7.1\"]}").build()),
|
||||
|
@ -1321,7 +1316,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
assertResponseParserClassEquals(method, request, ParseFirstJsonValueNamed.class);
|
||||
// now test that it works!
|
||||
|
||||
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) RestAnnotationProcessor
|
||||
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) AsyncRestClientProxy
|
||||
.createResponseParser(parserFactory, injector, method, request);
|
||||
|
||||
assertEquals(parser.apply(HttpResponse.builder().statusCode(200).message("ok")
|
||||
|
@ -1333,7 +1328,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
Method method = TestPut.class.getMethod("selectLongAddOne");
|
||||
HttpRequest request = factory(TestPut.class).createRequest(method);
|
||||
|
||||
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) RestAnnotationProcessor
|
||||
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) AsyncRestClientProxy
|
||||
.createResponseParser(parserFactory, injector, method, request);
|
||||
|
||||
assertEquals(parser.apply(HttpResponse.builder().statusCode(200).message("ok")
|
||||
|
@ -1476,13 +1471,6 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
@Path("/")
|
||||
public void oneFormParamExtractor(@FormParam("one") @ParamParser(FirstCharacter.class) String one) {
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/{path}")
|
||||
@PathParam("path")
|
||||
@ParamParser(FirstCharacterFirstElement.class)
|
||||
public void onePathParamExtractorMethod(String path) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1531,15 +1519,6 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
assertEquals(e.getMessage(), "param{one} for method TestPath.oneFormParamExtractor");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParamExtractorMethod() throws SecurityException, NoSuchMethodException {
|
||||
Method method = TestPath.class.getMethod("onePathParamExtractorMethod", String.class);
|
||||
HttpRequest request = factory(TestPath.class).createRequest(method, new Object[] { "localhost" });
|
||||
assertEquals(request.getEndpoint().getPath(), "/l");
|
||||
assertEquals(request.getMethod(), HttpMethod.GET);
|
||||
assertEquals(request.getHeaders().size(), 0);
|
||||
}
|
||||
|
||||
static class FirstCharacter implements Function<Object, String> {
|
||||
public String apply(Object from) {
|
||||
|
@ -1729,25 +1708,19 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
|
||||
public interface TestTransformers {
|
||||
@GET
|
||||
int noTransformer();
|
||||
ListenableFuture<Integer> noTransformer();
|
||||
|
||||
@GET
|
||||
@ResponseParser(ReturnStringIf2xx.class)
|
||||
void oneTransformer();
|
||||
ListenableFuture<Void> oneTransformer();
|
||||
|
||||
@GET
|
||||
@ResponseParser(ReturnStringIf200Context.class)
|
||||
void oneTransformerWithContext();
|
||||
|
||||
@GET
|
||||
InputStream inputStream();
|
||||
ListenableFuture<Void> oneTransformerWithContext();
|
||||
|
||||
@GET
|
||||
ListenableFuture<InputStream> futureInputStream();
|
||||
|
||||
@GET
|
||||
URI uri();
|
||||
|
||||
@GET
|
||||
ListenableFuture<URI> futureUri();
|
||||
|
||||
|
@ -1874,12 +1847,6 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
assertPayloadEquals(request, "whoops", "application/unknown", true);
|
||||
}
|
||||
|
||||
public void testInputStream() throws SecurityException, NoSuchMethodException {
|
||||
Method method = TestTransformers.class.getMethod("inputStream");
|
||||
Class<? extends Function<HttpResponse, ?>> transformer = unwrap(factory(TestTransformers.class), method);
|
||||
assertEquals(transformer, ReturnInputStream.class);
|
||||
}
|
||||
|
||||
public void testInputStreamListenableFuture() throws SecurityException, NoSuchMethodException {
|
||||
Method method = TestTransformers.class.getMethod("futureInputStream");
|
||||
Class<? extends Function<HttpResponse, ?>> transformer = unwrap(factory(TestTransformers.class), method);
|
||||
|
@ -1889,16 +1856,10 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
@SuppressWarnings("unchecked")
|
||||
public static Class<? extends Function<HttpResponse, ?>> unwrap(RestAnnotationProcessor processor,
|
||||
Method method) {
|
||||
return (Class<? extends Function<HttpResponse, ?>>) RestAnnotationProcessor.getParserOrThrowException(method)
|
||||
return (Class<? extends Function<HttpResponse, ?>>) AsyncRestClientProxy.getParserOrThrowException(method)
|
||||
.getTypeLiteral().getRawType();
|
||||
}
|
||||
|
||||
public void testURI() throws SecurityException, NoSuchMethodException {
|
||||
Method method = TestTransformers.class.getMethod("uri");
|
||||
Class<? extends Function<HttpResponse, ?>> transformer = unwrap(factory(TestTransformers.class), method);
|
||||
assertEquals(transformer, ParseURIFromListOrLocationHeaderIf20x.class);
|
||||
}
|
||||
|
||||
public void testURIListenableFuture() throws SecurityException, NoSuchMethodException {
|
||||
Method method = TestTransformers.class.getMethod("futureUri");
|
||||
Class<? extends Function<HttpResponse, ?>> transformer = unwrap(factory(TestTransformers.class), method);
|
||||
|
@ -1921,7 +1882,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
@Test(expectedExceptions = { RuntimeException.class })
|
||||
public void testNoTransformer() throws SecurityException, NoSuchMethodException {
|
||||
Method method = TestTransformers.class.getMethod("noTransformer");
|
||||
factory(TestTransformers.class).getParserOrThrowException(method);
|
||||
AsyncRestClientProxy.getParserOrThrowException(method);
|
||||
}
|
||||
|
||||
public void oneTransformerWithContext() throws SecurityException, NoSuchMethodException {
|
||||
|
@ -1930,7 +1891,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
GeneratedHttpRequest request = GeneratedHttpRequest.builder()
|
||||
.method("GET").endpoint("http://localhost").declaring(TestTransformers.class)
|
||||
.javaMethod(method).args(new Object[] {}).build();
|
||||
Function<HttpResponse, ?> transformer = processor.createResponseParser(method, request);
|
||||
Function<HttpResponse, ?> transformer = AsyncRestClientProxy.createResponseParser(parserFactory, injector, method, request);
|
||||
assertEquals(transformer.getClass(), ReturnStringIf200Context.class);
|
||||
assertEquals(((ReturnStringIf200Context) transformer).request, request);
|
||||
}
|
||||
|
@ -2272,13 +2233,11 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
|
|||
|
||||
}
|
||||
|
||||
@SuppressWarnings("static-access")
|
||||
@Test
|
||||
@Test(expectedExceptions = IllegalStateException.class)
|
||||
public void testTwoDifferentEndpointParams() throws SecurityException, NoSuchMethodException, ExecutionException {
|
||||
Method method = TestEndpointParams.class.getMethod("twoEndpointParams", String.class, String.class);
|
||||
URI uri = factory(TestEndpointParams.class).getEndpointInParametersOrNull(method,
|
||||
factory(TestEndpointParams.class).getEndpointInParametersOrNull(method,
|
||||
new Object[] { "robot", "egg" }, injector);
|
||||
assertEquals(uri, URI.create("robot/egg"));
|
||||
}
|
||||
|
||||
public interface TestPayload {
|
||||
|
|
|
@ -17,9 +17,9 @@
|
|||
* under the License.
|
||||
*/
|
||||
package org.jclouds.http.apachehc;
|
||||
|
||||
import static com.google.common.hash.Hashing.md5;
|
||||
import static com.google.common.io.BaseEncoding.base64;
|
||||
import static org.jclouds.http.HttpUtils.filterOutContentHeaders;
|
||||
import static org.jclouds.io.ByteSources.asByteSource;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -45,7 +45,6 @@ import org.jclouds.http.internal.HttpWire;
|
|||
import org.jclouds.io.ContentMetadataCodec;
|
||||
import org.jclouds.io.Payload;
|
||||
import org.jclouds.io.Payloads;
|
||||
import org.jclouds.rest.internal.RestAnnotationProcessor;
|
||||
|
||||
import com.google.common.collect.LinkedHashMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
|
@ -108,7 +107,7 @@ public class ApacheHCHttpCommandExecutorService extends BaseHttpCommandExecutorS
|
|||
return HttpResponse.builder().statusCode(apacheResponse.getStatusLine().getStatusCode())
|
||||
.message(apacheResponse.getStatusLine().getReasonPhrase())
|
||||
.payload(payload)
|
||||
.headers(RestAnnotationProcessor.filterOutContentHeaders(headers)).build();
|
||||
.headers(filterOutContentHeaders(headers)).build();
|
||||
}
|
||||
|
||||
private org.apache.http.HttpResponse executeRequest(HttpUriRequest nativeRequest) throws IOException,
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
package org.jclouds.gae;
|
||||
import static org.jclouds.http.HttpUtils.filterOutContentHeaders;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
|
@ -24,7 +25,6 @@ import org.jclouds.http.HttpResponse;
|
|||
import org.jclouds.io.ContentMetadataCodec;
|
||||
import org.jclouds.io.Payload;
|
||||
import org.jclouds.io.Payloads;
|
||||
import org.jclouds.rest.internal.RestAnnotationProcessor;
|
||||
|
||||
import com.google.appengine.api.urlfetch.HTTPHeader;
|
||||
import com.google.appengine.api.urlfetch.HTTPResponse;
|
||||
|
@ -67,6 +67,6 @@ public class ConvertToJcloudsResponse implements Function<HTTPResponse, HttpResp
|
|||
.statusCode(gaeResponse.getResponseCode())
|
||||
.message(message)
|
||||
.payload(payload)
|
||||
.headers(RestAnnotationProcessor.filterOutContentHeaders(headers)).build();
|
||||
.headers(filterOutContentHeaders(headers)).build();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,11 +19,9 @@
|
|||
|
||||
package org.jclouds.googlecompute.functions.internal;
|
||||
|
||||
import com.google.common.annotations.Beta;
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.base.Predicates;
|
||||
import com.google.common.collect.Iterables;
|
||||
import static com.google.common.base.Predicates.instanceOf;
|
||||
import static com.google.common.collect.Iterables.tryFind;
|
||||
|
||||
import org.jclouds.collect.IterableWithMarker;
|
||||
import org.jclouds.collect.PagedIterable;
|
||||
import org.jclouds.collect.PagedIterables;
|
||||
|
@ -33,14 +31,16 @@ import org.jclouds.http.HttpRequest;
|
|||
import org.jclouds.rest.InvocationContext;
|
||||
import org.jclouds.rest.internal.GeneratedHttpRequest;
|
||||
|
||||
import java.util.Arrays;
|
||||
import com.google.common.annotations.Beta;
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Optional;
|
||||
|
||||
/**
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Beta
|
||||
public abstract class BaseToPagedIterable<T, I extends BaseToPagedIterable<T, I>> implements
|
||||
Function<ListPage<T>, PagedIterable<T>>, InvocationContext<I> {
|
||||
Function<ListPage<T>, PagedIterable<T>>, InvocationContext<I> {
|
||||
|
||||
private GeneratedHttpRequest request;
|
||||
|
||||
|
@ -49,22 +49,21 @@ public abstract class BaseToPagedIterable<T, I extends BaseToPagedIterable<T, I>
|
|||
if (input.nextMarker() == null)
|
||||
return PagedIterables.of(input);
|
||||
|
||||
Optional<Object> project = Iterables.tryFind(Arrays.asList(request.getCaller().get().getArgs()),
|
||||
Predicates.instanceOf(String.class));
|
||||
Optional<Object> project = tryFind(request.getCaller().get().getArgs(), instanceOf(String.class));
|
||||
|
||||
Optional<Object> listOptions = Iterables.tryFind(request.getArgs(),
|
||||
Predicates.instanceOf(ListOptions.class));
|
||||
Optional<Object> listOptions = tryFind(request.getArgs(), instanceOf(ListOptions.class));
|
||||
|
||||
assert project.isPresent() : String.format("programming error, method %s should have a string param for the " +
|
||||
"project", request.getCaller().get().getMethod());
|
||||
assert project.isPresent() : String.format("programming error, method %s should have a string param for the "
|
||||
+ "project", request.getCaller().get().getInvoker());
|
||||
|
||||
return PagedIterables.advance(input, fetchNextPage(project.get().toString(), (String) input.nextMarker().orNull(),
|
||||
(ListOptions) listOptions.orNull()));
|
||||
return PagedIterables.advance(
|
||||
input,
|
||||
fetchNextPage(project.get().toString(), (String) input.nextMarker().orNull(),
|
||||
(ListOptions) listOptions.orNull()));
|
||||
}
|
||||
|
||||
|
||||
protected abstract Function<Object, IterableWithMarker<T>> fetchNextPage(String projectName, String marker,
|
||||
ListOptions listOptions);
|
||||
ListOptions listOptions);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
|
|
Loading…
Reference in New Issue