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