First wave of RestAnnotationParser refactoring introduces Invokable, removes SeedAnnotationCache and associated race conditions

This commit is contained in:
Adrian Cole 2013-01-04 11:43:11 -08:00
parent 37759d1c8c
commit afc070ac07
29 changed files with 917 additions and 1107 deletions

View File

@ -25,7 +25,7 @@ import java.util.Set;
import javax.inject.Inject;
import org.jclouds.internal.ClassMethodArgsAndReturnVal;
import org.jclouds.internal.ClassInvokerArgsAndReturnVal;
import org.jclouds.openstack.v2_0.domain.Extension;
import org.jclouds.openstack.v2_0.predicates.ExtensionPredicates;
import org.jclouds.rest.functions.ImplicitOptionalConverter;
@ -57,17 +57,17 @@ public class PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensio
}
@Override
public Optional<Object> apply(ClassMethodArgsAndReturnVal input) {
public Optional<Object> apply(ClassInvokerArgsAndReturnVal input) {
Optional<org.jclouds.openstack.v2_0.services.Extension> ext = Optional.fromNullable(input.getClazz().getAnnotation(
org.jclouds.openstack.v2_0.services.Extension.class));
if (ext.isPresent()) {
URI namespace = URI.create(ext.get().namespace());
if (input.getArgs().length == 0) {
if (input.getArgs().isEmpty()) {
if (Iterables.any(extensions.getUnchecked(""),
ExtensionPredicates.namespaceOrAliasEquals(namespace, aliases.get(namespace))))
return Optional.of(input.getReturnVal());
} else if (input.getArgs().length == 1) {
if (Iterables.any(extensions.getUnchecked(checkNotNull(input.getArgs()[0], "arg[0] in %s", input).toString()),
} else if (input.getArgs().size() == 1) {
if (Iterables.any(extensions.getUnchecked(checkNotNull(input.getArgs().get(0), "arg[0] in %s", input).toString()),
ExtensionPredicates.namespaceOrAliasEquals(namespace, aliases.get(namespace))))
return Optional.of(input.getReturnVal());
} else {

View File

@ -6,7 +6,7 @@ import java.net.URI;
import java.util.Set;
import org.jclouds.date.internal.SimpleDateFormatDateService;
import org.jclouds.internal.ClassMethodArgsAndReturnVal;
import org.jclouds.internal.ClassInvokerArgsAndReturnVal;
import org.jclouds.openstack.v2_0.ServiceType;
import org.jclouds.openstack.v2_0.domain.Extension;
import org.jclouds.rest.annotations.Delegate;
@ -17,10 +17,12 @@ import com.google.common.base.Optional;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap;
import com.google.common.reflect.Invokable;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Provides;
@ -61,16 +63,19 @@ public class PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensio
}
ClassMethodArgsAndReturnVal getFloatingIPExtension() throws SecurityException, NoSuchMethodException {
return ClassMethodArgsAndReturnVal.builder().clazz(FloatingIPAsyncApi.class).method(
NovaAsyncApi.class.getDeclaredMethod("getFloatingIPExtensionForZone", String.class)).args(
new Object[] { "zone" }).returnVal("foo").build();
ClassInvokerArgsAndReturnVal getFloatingIPExtension() throws SecurityException, NoSuchMethodException {
return ClassInvokerArgsAndReturnVal
.builder()
.clazz(FloatingIPAsyncApi.class)
.invoker(
Invokable.from(NovaAsyncApi.class.getDeclaredMethod("getFloatingIPExtensionForZone", String.class)))
.args(ImmutableList.<Object> of("zone")).returnVal("foo").build();
}
ClassMethodArgsAndReturnVal getKeyPairExtension() throws SecurityException, NoSuchMethodException {
return ClassMethodArgsAndReturnVal.builder().clazz(KeyPairAsyncApi.class).method(
NovaAsyncApi.class.getDeclaredMethod("getKeyPairExtensionForZone", String.class)).args(
new Object[] { "zone" }).returnVal("foo").build();
ClassInvokerArgsAndReturnVal getKeyPairExtension() throws SecurityException, NoSuchMethodException {
return ClassInvokerArgsAndReturnVal.builder().clazz(KeyPairAsyncApi.class)
.invoker(Invokable.from(NovaAsyncApi.class.getDeclaredMethod("getKeyPairExtensionForZone", String.class)))
.args(ImmutableList.<Object> of("zone")).returnVal("foo").build();
}
public void testPresentWhenExtensionsIncludeNamespaceFromAnnotationAbsentWhenNot() throws SecurityException, NoSuchMethodException {
@ -83,9 +88,9 @@ public class PresentWhenExtensionAnnotationNamespaceEqualsAnyNamespaceInExtensio
public void testZoneWithoutExtensionsReturnsAbsent() throws SecurityException, NoSuchMethodException {
assertEquals(whenExtensionsInZoneInclude("zone", floatingIps).apply(
getFloatingIPExtension().toBuilder().args(new Object[] { "differentzone" }).build()), Optional.absent());
getFloatingIPExtension().toBuilder().args(ImmutableList.<Object> of("differentzone")).build()), Optional.absent());
assertEquals(whenExtensionsInZoneInclude("zone", keypairs).apply(
getKeyPairExtension().toBuilder().args(new Object[] { "differentzone" }).build()), Optional.absent());
getKeyPairExtension().toBuilder().args(ImmutableList.<Object> of("differentzone")).build()), Optional.absent());
}
/**

View File

@ -63,8 +63,8 @@ public abstract class CallerArg0ToPagedIterable<T, I extends CallerArg0ToPagedIt
return PagedIterables.of(input);
Optional<String> arg0Option = Optional.absent();
if (request.getCaller().get().getArgs().length > 0) {
Object arg0 = request.getCaller().get().getArgs()[0];
if (request.getCaller().get().getArgs().size() > 0) {
Object arg0 = request.getCaller().get().getArgs().get(0);
if (arg0 != null)
arg0Option = Optional.of(arg0.toString());
}

View File

@ -33,8 +33,8 @@ import javax.annotation.Resource;
import javax.inject.Inject;
import javax.inject.Named;
import org.jclouds.internal.ClassMethodArgs;
import org.jclouds.internal.ClassMethodArgsAndReturnVal;
import org.jclouds.internal.ClassInvokerArgs;
import org.jclouds.internal.ClassInvokerArgsAndReturnVal;
import org.jclouds.logging.Logger;
import org.jclouds.rest.annotations.Delegate;
import org.jclouds.util.Optionals2;
@ -74,20 +74,20 @@ public final class SyncProxy extends AbstractInvocationHandler {
@Resource
private Logger logger = Logger.NULL;
private final Function<ClassMethodArgsAndReturnVal, Optional<Object>> optionalConverter;
private final Function<ClassInvokerArgsAndReturnVal, Optional<Object>> optionalConverter;
private final Object delegate;
private final Class<?> declaring;
private final Map<Method, Method> methodMap;
private final Map<Method, Method> syncMethodMap;
private final Map<Method, Optional<Long>> timeoutMap;
private final LoadingCache<ClassMethodArgs, Object> delegateMap;
private final LoadingCache<ClassInvokerArgs, Object> delegateMap;
private final Map<Class<?>, Class<?>> sync2Async;
private static final Set<Method> objectMethods = ImmutableSet.copyOf(Object.class.getMethods());
@Inject
@VisibleForTesting
SyncProxy(Function<ClassMethodArgsAndReturnVal, Optional<Object>> optionalConverter,
@Named("sync") LoadingCache<ClassMethodArgs, Object> delegateMap, Map<Class<?>, Class<?>> sync2Async,
SyncProxy(Function<ClassInvokerArgsAndReturnVal, Optional<Object>> optionalConverter,
@Named("sync") LoadingCache<ClassInvokerArgs, Object> delegateMap, Map<Class<?>, Class<?>> sync2Async,
@Named("TIMEOUTS") Map<String, Long> timeouts, @Assisted Class<?> declaring, @Assisted Object async)
throws SecurityException, NoSuchMethodException {
this.optionalConverter = optionalConverter;
@ -138,10 +138,10 @@ public final class SyncProxy extends AbstractInvocationHandler {
// pass any parameters necessary to get a relevant instance of that async class
// ex. getClientForRegion("north") might return an instance whose endpoint is
// different that "south"
ClassMethodArgs cma = new ClassMethodArgs(asyncClass, method, args);
ClassInvokerArgs cma = ClassInvokerArgs.builder().clazz(asyncClass).invoker(method).args(args).build();
Object returnVal = delegateMap.get(cma);
if (Optionals2.isReturnTypeOptional(method)){
ClassMethodArgsAndReturnVal cmar = ClassMethodArgsAndReturnVal.builder().fromClassMethodArgs(cma)
ClassInvokerArgsAndReturnVal cmar = ClassInvokerArgsAndReturnVal.builder().fromClassInvokerArgs(cma)
.returnVal(returnVal).build();
return optionalConverter.apply(cmar);
}

View File

@ -19,12 +19,17 @@
package org.jclouds.http;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Predicates.and;
import static com.google.common.base.Predicates.equalTo;
import static com.google.common.base.Predicates.in;
import static com.google.common.base.Predicates.not;
import static com.google.common.base.Predicates.notNull;
import static com.google.common.base.Throwables.getCausalChain;
import static com.google.common.base.Throwables.propagate;
import static com.google.common.collect.Iterables.filter;
import static com.google.common.collect.Iterables.get;
import static com.google.common.collect.Iterables.size;
import static com.google.common.collect.Multimaps.filterKeys;
import static com.google.common.io.BaseEncoding.base64;
import static com.google.common.io.ByteStreams.toByteArray;
import static com.google.common.io.Closeables.closeQuietly;
@ -38,11 +43,13 @@ import static com.google.common.net.HttpHeaders.EXPIRES;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.util.Collection;
import java.util.Map.Entry;
import javax.inject.Named;
import javax.inject.Singleton;
import javax.ws.rs.HttpMethod;
import org.jclouds.Constants;
import org.jclouds.io.ContentMetadata;
@ -53,7 +60,13 @@ import org.jclouds.io.Payloads;
import org.jclouds.logging.Logger;
import org.jclouds.logging.internal.Wire;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap;
import com.google.common.collect.ImmutableSet.Builder;
import com.google.common.reflect.Invokable;
import com.google.inject.Inject;
/**
@ -172,6 +185,24 @@ public class HttpUtils {
return null;
}
public static Optional<String> tryFindHttpMethod(Invokable<?, ?> method) {
Builder<String> methodsBuilder = ImmutableSet.builder();
for (Annotation annotation : method.getAnnotations()) {
HttpMethod http = annotation.annotationType().getAnnotation(HttpMethod.class);
if (http != null)
methodsBuilder.add(http.value());
}
Collection<String> methods = methodsBuilder.build();
switch (methods.size()) {
case 0:
return Optional.absent();
case 1:
return Optional.of(get(methods, 0));
default:
throw new IllegalStateException("You must specify at most one HttpMethod annotation on: " + method);
}
}
/**
* Content stream may need to be read. However, we should always close the http stream.
*
@ -322,6 +353,11 @@ public class HttpUtils {
return null;
}
public static Multimap<String, String> filterOutContentHeaders(Multimap<String, String> headers) {
// http message usually comes in as a null key header, let's filter it out.
return ImmutableMultimap.copyOf(filterKeys(headers, and(notNull(), not(in(ContentMetadata.HTTP_HEADERS)))));
}
public static boolean contains404(Throwable t) {
return returnValueOnCodeOrNull(t, true, equalTo(404)) != null;
}

View File

@ -26,6 +26,7 @@ import static com.google.common.io.Closeables.closeQuietly;
import static com.google.common.net.HttpHeaders.CONTENT_LENGTH;
import static com.google.common.net.HttpHeaders.HOST;
import static com.google.common.net.HttpHeaders.USER_AGENT;
import static org.jclouds.http.HttpUtils.filterOutContentHeaders;
import static org.jclouds.io.Payloads.newInputStreamPayload;
import java.io.ByteArrayInputStream;
@ -65,7 +66,6 @@ import org.jclouds.io.ContentMetadataCodec;
import org.jclouds.io.MutableContentMetadata;
import org.jclouds.io.Payload;
import org.jclouds.logging.Logger;
import org.jclouds.rest.internal.RestAnnotationProcessor;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableMultimap;
@ -142,7 +142,7 @@ public class JavaUrlHttpCommandExecutorService extends BaseHttpCommandExecutorSe
contentMetadataCodec.fromHeaders(payload.getContentMetadata(), headers);
builder.payload(payload);
}
builder.headers(RestAnnotationProcessor.filterOutContentHeaders(headers));
builder.headers(filterOutContentHeaders(headers));
return builder.build();
}

View File

@ -22,22 +22,25 @@ import static com.google.common.base.Objects.equal;
import static com.google.common.base.Preconditions.checkNotNull;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import com.google.common.base.Objects;
import com.google.common.base.Objects.ToStringHelper;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.reflect.Invokable;
/**
*
* @author Adrian Cole
*/
public class ClassMethodArgs {
public class ClassInvokerArgs {
public static Builder<?> builder() {
return new ConcreteBuilder();
}
public Builder<?> toBuilder() {
return builder().fromClassMethodArgs(this);
return builder().fromClassInvokerArgs(this);
}
private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
@ -45,8 +48,8 @@ public class ClassMethodArgs {
public abstract static class Builder<B extends Builder<B>> {
private Class<?> clazz;
private Method method;
private Object[] args = {};
private Invokable<?, ?> invoker;
private List<Object> args = ImmutableList.of();
@SuppressWarnings("unchecked")
protected B self() {
@ -54,7 +57,7 @@ public class ClassMethodArgs {
}
/**
* @see ClassMethodArgs#getClazz()
* @see ClassInvokerArgs#getClazz()
*/
public B clazz(Class<?> clazz) {
this.clazz = clazz;
@ -62,53 +65,78 @@ public class ClassMethodArgs {
}
/**
* @see ClassMethodArgs#getMethod()
* @see ClassInvokerArgs#getInvoker()
*/
public B method(Method method) {
this.method = method;
public B invoker(Invokable<?, ?> invoker) {
this.invoker = invoker;
return self();
}
/**
* @see ClassInvokerArgs#getInvoker()
*/
@Deprecated
public B invoker(Method method) {
return invoker(Invokable.from(method));
}
/**
* @see ClassMethodArgs#getArgs()
* @see ClassInvokerArgs#getArgs()
*/
@Deprecated
public B args(Object[] args) {
return args(args == null ? Lists.newArrayList(new Object[] { null }) : Lists.newArrayList(args));
}
/**
* @see ClassInvokerArgs#getArgs()
*/
public B args(List<Object> args) {
this.args = args;
return self();
}
public ClassMethodArgs build() {
return new ClassMethodArgs(this);
public ClassInvokerArgs build() {
return new ClassInvokerArgs(this);
}
public B fromClassMethodArgs(ClassMethodArgs in) {
return clazz(in.getClazz()).method(in.getMethod()).args(in.getArgs());
public B fromClassInvokerArgs(ClassInvokerArgs in) {
return clazz(in.getClazz()).invoker(in.getInvoker()).args(in.getArgs());
}
}
private final Class<?> clazz;
private final Method method;
private final Object[] args;
private final Invokable<?, ?> invoker;
private final List<Object> args;
public ClassMethodArgs(Builder<?> builder) {
this(builder.clazz, builder.method, builder.args);
public ClassInvokerArgs(Builder<?> builder) {
this(builder.clazz, builder.invoker, builder.args);
}
public ClassMethodArgs(Class<?> clazz, Method method, Object[] args) {
/**
* @param args as these represent parameters, can contain nulls
*/
public ClassInvokerArgs(Class<?> clazz, Invokable<?, ?> invoker, List<Object> args) {
this.clazz = checkNotNull(clazz, "clazz");
this.method = checkNotNull(method, "method");
this.invoker = checkNotNull(invoker, "invoker");
this.args = checkNotNull(args, "args");
}
/**
* not necessarily the declaring class of the invoker.
*/
public Class<?> getClazz() {
return clazz;
}
public Method getMethod() {
return method;
public Invokable<?, ?> getInvoker() {
return invoker;
}
public Object[] getArgs() {
/**
* @param args as these represent parameters, can contain nulls
*/
public List<Object> getArgs() {
return args;
}
@ -118,13 +146,13 @@ public class ClassMethodArgs {
return true;
if (o == null || getClass() != o.getClass())
return false;
ClassMethodArgs that = ClassMethodArgs.class.cast(o);
return equal(this.clazz, that.clazz) && equal(this.method, that.method) && equal(this.args, that.args);
ClassInvokerArgs that = ClassInvokerArgs.class.cast(o);
return equal(this.clazz, that.clazz) && equal(this.invoker, that.invoker) && equal(this.args, that.args);
}
@Override
public int hashCode() {
return Objects.hashCode(clazz, method, args);
return Objects.hashCode(clazz, invoker, args);
}
@Override
@ -133,7 +161,7 @@ public class ClassMethodArgs {
}
protected ToStringHelper string() {
return Objects.toStringHelper("").omitNullValues().add("clazz", clazz).add("method", method)
.add("args", args.length != 0 ? Arrays.asList(args) : null);
return Objects.toStringHelper("").omitNullValues().add("clazz", clazz).add("invoker", invoker)
.add("args", args.size() != 0 ? args : null);
}
}

View File

@ -20,31 +20,32 @@ package org.jclouds.internal;
import static com.google.common.base.Objects.equal;
import java.lang.reflect.Method;
import java.util.List;
import com.google.common.base.Objects;
import com.google.common.base.Objects.ToStringHelper;
import com.google.common.reflect.Invokable;
/**
*
* @author Adrian Cole
*/
public final class ClassMethodArgsAndReturnVal extends ClassMethodArgs {
public final class ClassInvokerArgsAndReturnVal extends ClassInvokerArgs {
public static Builder builder() {
return new Builder();
}
public Builder toBuilder() {
return builder().fromClassMethodArgsAndReturnVal(this);
return builder().fromClassInvokerArgsAndReturnVal(this);
}
public final static class Builder extends ClassMethodArgs.Builder<Builder> {
public final static class Builder extends ClassInvokerArgs.Builder<Builder> {
private Object returnVal;
/**
* @see ClassMethodArgsAndReturnVal#getReturnVal()
* @see ClassInvokerArgsAndReturnVal#getReturnVal()
*/
public Builder returnVal(Object returnVal) {
this.returnVal = returnVal;
@ -52,23 +53,23 @@ public final class ClassMethodArgsAndReturnVal extends ClassMethodArgs {
}
@Override
public ClassMethodArgsAndReturnVal build() {
return new ClassMethodArgsAndReturnVal(this);
public ClassInvokerArgsAndReturnVal build() {
return new ClassInvokerArgsAndReturnVal(this);
}
public Builder fromClassMethodArgsAndReturnVal(ClassMethodArgsAndReturnVal in) {
return fromClassMethodArgs(in).returnVal(in.getReturnVal());
public Builder fromClassInvokerArgsAndReturnVal(ClassInvokerArgsAndReturnVal in) {
return fromClassInvokerArgs(in).returnVal(in.getReturnVal());
}
}
private final Object returnVal;
private ClassMethodArgsAndReturnVal(Class<?> clazz, Method method, Object[] args, Object returnVal) {
super(clazz, method, args);
private ClassInvokerArgsAndReturnVal(Class<?> clazz, Invokable<?, ?> invoker, List<Object> args, Object returnVal) {
super(clazz, invoker, args);
this.returnVal = returnVal;
}
private ClassMethodArgsAndReturnVal(Builder builder) {
private ClassInvokerArgsAndReturnVal(Builder builder) {
super(builder);
this.returnVal = builder.returnVal;
}
@ -83,7 +84,7 @@ public final class ClassMethodArgsAndReturnVal extends ClassMethodArgs {
return true;
if (o == null || getClass() != o.getClass())
return false;
ClassMethodArgsAndReturnVal that = ClassMethodArgsAndReturnVal.class.cast(o);
ClassInvokerArgsAndReturnVal that = ClassInvokerArgsAndReturnVal.class.cast(o);
return super.equals(that) && equal(this.returnVal, that.returnVal);
}

View File

@ -32,7 +32,7 @@ import javax.inject.Singleton;
import org.jclouds.collect.Memoized;
import org.jclouds.domain.Location;
import org.jclouds.internal.ClassMethodArgsAndReturnVal;
import org.jclouds.internal.ClassInvokerArgsAndReturnVal;
import org.jclouds.location.Iso3166;
import org.jclouds.location.Provider;
import org.jclouds.location.Region;
@ -73,7 +73,7 @@ public class LocationModule extends AbstractModule {
@Override
protected void configure() {
bind(new TypeLiteral<Function<ClassMethodArgsAndReturnVal, Optional<Object>>>(){}).to(ImplicitOptionalConverter.class);
bind(new TypeLiteral<Function<ClassInvokerArgsAndReturnVal, Optional<Object>>>(){}).to(ImplicitOptionalConverter.class);
}
@Provides

View File

@ -19,130 +19,140 @@
package org.jclouds.rest;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.Collections2.filter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import javax.inject.Inject;
import org.jclouds.javax.annotation.Nullable;
import org.jclouds.predicates.Validator;
import org.jclouds.rest.annotations.ParamValidators;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.reflect.Invokable;
import com.google.common.reflect.Parameter;
import com.google.inject.Injector;
/**
* Validates method parameters.
*
* Checks the {@link ParamValidators} annotation for validators. There can be method-level
* validators that apply to all parameters, and parameter-level validators. When validation on at
* least one parameter doesn't pass, throws {@link IllegalStateException}.
*
*
* Checks the {@link ParamValidators} annotation for validators. There can be method-level validators that apply to all
* parameters, and parameter-level validators. When validation on at least one parameter doesn't pass, throws
* {@link IllegalStateException}.
*
* @author Oleksiy Yarmula
*/
public class InputParamValidator {
private final Injector injector;
private final Injector injector;
@Inject
public InputParamValidator(Injector injector) {
this.injector = injector;
}
@Inject
public InputParamValidator(Injector injector) {
this.injector = injector;
}
public InputParamValidator() {
injector = null;
}
public InputParamValidator() {
injector = null;
}
/**
* Validates that method parameters are correct, according to {@link ParamValidators}.
*
* @param method
* method with optionally set {@link ParamValidators}
* @param args
* method arguments with optionally set {@link ParamValidators}
* @see ParamValidators
* @see Validator
*
* @throws IllegalStateException
* if validation failed
*/
public void validateMethodParametersOrThrow(Method method, Object... args) {
/**
* Validates that method parameters are correct, according to {@link ParamValidators}.
*
* @param method
* method with optionally set {@link ParamValidators}
* @param args
* method arguments with optionally set {@link ParamValidators}
* @see ParamValidators
* @see Validator
*
* @throws IllegalStateException
* if validation failed
*/
@Deprecated
public void validateMethodParametersOrThrow(Method method, @Nullable Object... args) {
validateMethodParametersOrThrow(Invokable.from(checkNotNull(method, "method")),
Lists.newArrayList(args));
}
try {
performMethodValidation(method, args);
performParameterValidation(method.getParameterAnnotations(), args);
} catch(IllegalArgumentException e) {
String argsString = Iterables.toString(Arrays.asList(args));
throw new IllegalArgumentException(String.format("Validation on '%s#%s' didn't pass for arguments: " +
"%s. %n Reason: %s.", method.getDeclaringClass().getName(), method.getName(), argsString,
e.getMessage()));
}
}
public void validateMethodParametersOrThrow(Invokable<?, ?> method, List<Object> args) {
try {
performMethodValidation(checkNotNull(method, "method"), args);
performParameterValidation(method.getParameters(), args);
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException(String.format("Validation on '%s#%s' didn't pass for arguments: "
+ "%s. %n Reason: %s.", method.getDeclaringClass().getName(), method.getName(), args,
e.getMessage()));
}
}
/**
* Returns if all the method parameters passed all of the method-level
* validators or throws an {@link IllegalArgumentException}.
* @param method
* method with optionally set {@link ParamValidators}. This can not be null.
* @param args
* method's parameters
*/
private void performMethodValidation(Method method, Object... args) {
ParamValidators paramValidatorsAnnotation = checkNotNull(method).getAnnotation(
ParamValidators.class);
if (paramValidatorsAnnotation == null)
return; // by contract
/**
* Returns if all the method parameters passed all of the method-level validators or throws an
* {@link IllegalArgumentException}.
*
* @param method
* method with optionally set {@link ParamValidators}. This can not be null.
* @param args
* method's parameters
*/
private void performMethodValidation(Invokable<?, ?> method, List<Object> args) {
ParamValidators paramValidatorsAnnotation = method.getAnnotation(ParamValidators.class);
if (paramValidatorsAnnotation == null)
return; // by contract
List<Validator<?>> methodValidators = getValidatorsFromAnnotation(paramValidatorsAnnotation);
List<Validator<?>> methodValidators = getValidatorsFromAnnotation(paramValidatorsAnnotation);
runPredicatesAgainstArgs(methodValidators, args);
}
runPredicatesAgainstArgs(methodValidators, args);
}
/**
* Returns if all the method parameters passed all of their corresponding
* validators or throws an {@link IllegalArgumentException}.
*
* @param annotations
* annotations for method's arguments
* @param args
* arguments that correspond to the array of annotations
*/
private void performParameterValidation(Annotation[][] annotations, Object... args) {
for (int currentParameterIndex = 0; currentParameterIndex < annotations.length; currentParameterIndex++) {
ParamValidators annotation = findParamValidatorsAnnotationOrReturnNull(annotations[currentParameterIndex]);
if (annotation == null)
continue;
List<Validator<?>> parameterValidators = getValidatorsFromAnnotation(annotation);
runPredicatesAgainstArgs(parameterValidators,
args[currentParameterIndex]);
}
}
/**
* Returns if all the method parameters passed all of their corresponding validators or throws an
* {@link IllegalArgumentException}.
*
* @param parameters
* annotations for method's arguments
* @param args
* arguments that correspond to the array of annotations
*/
private void performParameterValidation(List<Parameter> parameters, List<Object> args) {
for (Parameter param : filter(parameters, new Predicate<Parameter>() {
public boolean apply(Parameter in) {
return in.isAnnotationPresent(ParamValidators.class);
}
})) {
ParamValidators annotation = param.getAnnotation(ParamValidators.class);
if (annotation == null)
continue;
List<Validator<?>> parameterValidators = getValidatorsFromAnnotation(annotation);
runPredicatesAgainstArg(parameterValidators, args.get(param.hashCode()));// TODO position guava issue 1243
}
}
private List<Validator<?>> getValidatorsFromAnnotation(ParamValidators paramValidatorsAnnotation) {
List<Validator<?>> validators = Lists.newArrayList();
for (Class<? extends Validator<?>> validator : paramValidatorsAnnotation.value()) {
validators.add(checkNotNull(injector.getInstance(validator)));
}
return validators;
}
private List<Validator<?>> getValidatorsFromAnnotation(ParamValidators paramValidatorsAnnotation) {
List<Validator<?>> validators = Lists.newArrayList();
for (Class<? extends Validator<?>> validator : paramValidatorsAnnotation.value()) {
validators.add(checkNotNull(injector.getInstance(validator)));
}
return validators;
}
@SuppressWarnings("unchecked")
private void runPredicatesAgainstArg(List<Validator<?>> predicates, Object arg) {
for (@SuppressWarnings("rawtypes")
Validator validator : predicates) {
validator.apply(arg);
}
}
@SuppressWarnings("unchecked")
private void runPredicatesAgainstArgs(List<Validator<?>> predicates, Object... args) {
for (@SuppressWarnings("rawtypes") Validator validator : predicates) {
Iterables.all(Arrays.asList(args), validator);
}
}
private ParamValidators findParamValidatorsAnnotationOrReturnNull(
Annotation[] parameterAnnotations) {
for (Annotation annotation : parameterAnnotations) {
if (annotation instanceof ParamValidators)
return (ParamValidators) annotation;
}
return null;
}
@SuppressWarnings("unchecked")
private void runPredicatesAgainstArgs(List<Validator<?>> predicates, List<Object> args) {
for (@SuppressWarnings("rawtypes")
Validator validator : predicates) {
Iterables.all(args, validator);
}
}
}

View File

@ -21,7 +21,6 @@ package org.jclouds.rest.config;
import static org.jclouds.Constants.PROPERTY_TIMEOUTS_PREFIX;
import static org.jclouds.rest.config.BinderUtils.bindClientAndAsyncClient;
import java.lang.reflect.Method;
import java.net.URI;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
@ -32,7 +31,7 @@ import javax.inject.Singleton;
import org.jclouds.concurrent.internal.SyncProxy;
import org.jclouds.functions.IdentityFunction;
import org.jclouds.http.functions.config.SaxParserModule;
import org.jclouds.internal.ClassMethodArgs;
import org.jclouds.internal.ClassInvokerArgs;
import org.jclouds.internal.FilterStringsBoundToInjectorByName;
import org.jclouds.json.config.GsonModule;
import org.jclouds.location.config.LocationModule;
@ -44,15 +43,12 @@ import org.jclouds.rest.internal.AsyncRestClientProxy;
import org.jclouds.rest.internal.CreateAsyncClientForCaller;
import org.jclouds.rest.internal.CreateClientForCaller;
import org.jclouds.rest.internal.RestAnnotationProcessor;
import org.jclouds.rest.internal.RestAnnotationProcessor.MethodKey;
import org.jclouds.rest.internal.SeedAnnotationCache;
import org.jclouds.util.Maps2;
import org.jclouds.util.Predicates2;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Supplier;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableMap;
@ -124,24 +120,18 @@ public class RestModule extends AbstractModule {
});
}
@Provides
@Singleton
private LoadingCache<Class<?>, Cache<MethodKey, Method>> seedAnnotationCache(SeedAnnotationCache seedAnnotationCache) {
return CacheBuilder.newBuilder().build(seedAnnotationCache);
}
@Provides
@Singleton
@Named("async")
LoadingCache<ClassMethodArgs, Object> provideAsyncDelegateMap(CreateAsyncClientForCaller createAsyncClientForCaller) {
LoadingCache<ClassInvokerArgs, Object> provideAsyncDelegateMap(CreateAsyncClientForCaller createAsyncClientForCaller) {
return CacheBuilder.newBuilder().build(createAsyncClientForCaller);
}
@Provides
@Singleton
@Named("sync")
LoadingCache<ClassMethodArgs, Object> provideSyncDelegateMap(CreateClientForCaller createClientForCaller) {
LoadingCache<ClassInvokerArgs, Object> provideSyncDelegateMap(CreateClientForCaller createClientForCaller) {
return CacheBuilder.newBuilder().build(createClientForCaller);
}

View File

@ -18,7 +18,7 @@
*/
package org.jclouds.rest.functions;
import org.jclouds.internal.ClassMethodArgsAndReturnVal;
import org.jclouds.internal.ClassInvokerArgsAndReturnVal;
import com.google.common.annotations.Beta;
import com.google.common.base.Optional;
@ -31,7 +31,7 @@ import com.google.common.base.Optional;
public class AlwaysPresentImplicitOptionalConverter implements ImplicitOptionalConverter {
@Override
public Optional<Object> apply(ClassMethodArgsAndReturnVal input) {
public Optional<Object> apply(ClassInvokerArgsAndReturnVal input) {
return Optional.of(input.getReturnVal());
}

View File

@ -18,7 +18,7 @@
*/
package org.jclouds.rest.functions;
import org.jclouds.internal.ClassMethodArgsAndReturnVal;
import org.jclouds.internal.ClassInvokerArgsAndReturnVal;
import org.jclouds.rest.config.RestClientModule;
import com.google.common.annotations.Beta;
@ -39,19 +39,19 @@ import com.google.inject.ImplementedBy;
* }
* </pre>
*
* The input object of type {@link ClassMethodArgsAndReturnVal} will include the
* The input object of type {@link ClassInvokerArgsAndReturnVal} will include the
* following.
* <ol>
* <li>the class declaring the method that returns optional:
* {@link ClassMethodArgsAndReturnVal#getClazz}; in the example above,
* {@link ClassInvokerArgsAndReturnVal#getClazz}; in the example above,
* {@code MyCloud}</li>
* <li>the method returning the optional:
* {@link ClassMethodArgsAndReturnVal#getMethod}; in the example above,
* {@link ClassInvokerArgsAndReturnVal#getMethod}; in the example above,
* {@code getKeyPairExtensionForRegion}</li>
* <li>the args passed to that method at runtime:
* {@link ClassMethodArgsAndReturnVal#getArgs}; for example {@code North}</li>
* {@link ClassInvokerArgsAndReturnVal#getArgs}; for example {@code North}</li>
* <li>the rest client to be enclosed in the optional, should you choose to
* return it: {@link ClassMethodArgsAndReturnVal#getReturnVal}; in the example
* return it: {@link ClassInvokerArgsAndReturnVal#getReturnVal}; in the example
* above, an implementation of {@code KeyPairClient}</li>
* </ol>
*
@ -80,6 +80,6 @@ import com.google.inject.ImplementedBy;
*/
@Beta
@ImplementedBy(PresentWhenApiVersionLexicographicallyAtOrAfterSinceApiVersion.class)
public interface ImplicitOptionalConverter extends Function<ClassMethodArgsAndReturnVal, Optional<Object>> {
public interface ImplicitOptionalConverter extends Function<ClassInvokerArgsAndReturnVal, Optional<Object>> {
}

View File

@ -23,7 +23,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.jclouds.internal.ClassMethodArgsAndReturnVal;
import org.jclouds.internal.ClassInvokerArgsAndReturnVal;
import org.jclouds.rest.annotations.ApiVersion;
import org.jclouds.rest.annotations.SinceApiVersion;
@ -43,7 +43,7 @@ import com.google.common.cache.LoadingCache;
public class PresentWhenApiVersionLexicographicallyAtOrAfterSinceApiVersion implements ImplicitOptionalConverter {
@VisibleForTesting
static final class Loader extends CacheLoader<ClassMethodArgsAndReturnVal, Optional<Object>> {
static final class Loader extends CacheLoader<ClassInvokerArgsAndReturnVal, Optional<Object>> {
private final String apiVersion;
@Inject
@ -52,7 +52,7 @@ public class PresentWhenApiVersionLexicographicallyAtOrAfterSinceApiVersion impl
}
@Override
public Optional<Object> load(ClassMethodArgsAndReturnVal input) {
public Optional<Object> load(ClassInvokerArgsAndReturnVal input) {
Optional<SinceApiVersion> sinceApiVersion = Optional.fromNullable(input.getClazz().getAnnotation(
SinceApiVersion.class));
if (sinceApiVersion.isPresent()) {
@ -67,7 +67,7 @@ public class PresentWhenApiVersionLexicographicallyAtOrAfterSinceApiVersion impl
}
}
private final LoadingCache<ClassMethodArgsAndReturnVal, Optional<Object>> lookupCache;
private final LoadingCache<ClassInvokerArgsAndReturnVal, Optional<Object>> lookupCache;
@Inject
protected PresentWhenApiVersionLexicographicallyAtOrAfterSinceApiVersion(@ApiVersion String apiVersion) {
@ -76,7 +76,7 @@ public class PresentWhenApiVersionLexicographicallyAtOrAfterSinceApiVersion impl
}
@Override
public Optional<Object> apply(ClassMethodArgsAndReturnVal input) {
public Optional<Object> apply(ClassInvokerArgsAndReturnVal input) {
return lookupCache.getUnchecked(input);
}

View File

@ -18,14 +18,34 @@
*/
package org.jclouds.rest.internal;
import static com.google.common.base.Functions.compose;
import static com.google.common.base.Throwables.propagate;
import static com.google.common.collect.Iterables.any;
import static com.google.common.collect.Iterables.find;
import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Sets.difference;
import static com.google.common.util.concurrent.Callables.returning;
import static com.google.common.util.concurrent.Futures.immediateFailedFuture;
import static com.google.common.util.concurrent.Futures.transform;
import static com.google.common.util.concurrent.Futures.withFallback;
import static com.google.inject.util.Types.newParameterizedType;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
import static javax.ws.rs.core.MediaType.APPLICATION_XML;
import static org.jclouds.concurrent.Futures.makeListenable;
import static org.jclouds.http.HttpUtils.tryFindHttpMethod;
import static org.jclouds.util.Optionals2.isReturnTypeOptional;
import static org.jclouds.util.Optionals2.returnTypeOrTypeOfOptional;
import static org.jclouds.util.Throwables2.getFirstThrowableOfType;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.WildcardType;
import java.net.URI;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
@ -34,34 +54,60 @@ import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Qualifier;
import javax.inject.Singleton;
import javax.lang.model.type.NullType;
import javax.ws.rs.Path;
import org.jclouds.Constants;
import org.jclouds.fallbacks.MapHttp4xxCodesToExceptions;
import org.jclouds.functions.IdentityFunction;
import org.jclouds.functions.OnlyElementOrNull;
import org.jclouds.http.HttpCommand;
import org.jclouds.http.HttpCommandExecutorService;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.jclouds.internal.ClassMethodArgs;
import org.jclouds.internal.ClassMethodArgsAndReturnVal;
import org.jclouds.http.functions.ParseFirstJsonValueNamed;
import org.jclouds.http.functions.ParseJson;
import org.jclouds.http.functions.ParseSax;
import org.jclouds.http.functions.ParseSax.HandlerWithResult;
import org.jclouds.http.functions.ParseURIFromListOrLocationHeaderIf20x;
import org.jclouds.http.functions.ParseXMLWithJAXB;
import org.jclouds.http.functions.ReleasePayloadAndReturn;
import org.jclouds.http.functions.ReturnInputStream;
import org.jclouds.http.functions.ReturnStringIf2xx;
import org.jclouds.http.functions.ReturnTrueIf2xx;
import org.jclouds.http.functions.UnwrapOnlyJsonValue;
import org.jclouds.internal.ClassInvokerArgs;
import org.jclouds.internal.ClassInvokerArgsAndReturnVal;
import org.jclouds.json.internal.GsonWrapper;
import org.jclouds.logging.Logger;
import org.jclouds.rest.AuthorizationException;
import org.jclouds.rest.InvocationContext;
import org.jclouds.rest.annotations.Delegate;
import org.jclouds.rest.annotations.Fallback;
import org.jclouds.util.Optionals2;
import org.jclouds.util.Throwables2;
import org.jclouds.rest.annotations.JAXBResponseParser;
import org.jclouds.rest.annotations.OnlyElement;
import org.jclouds.rest.annotations.ResponseParser;
import org.jclouds.rest.annotations.SelectJson;
import org.jclouds.rest.annotations.Transform;
import org.jclouds.rest.annotations.Unwrap;
import org.jclouds.rest.annotations.XMLResponseParser;
import org.jclouds.rest.internal.RestAnnotationProcessor.InvokerKey;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.base.Supplier;
import com.google.common.base.Throwables;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.ImmutableSet;
import com.google.common.reflect.AbstractInvocationHandler;
import com.google.common.reflect.Invokable;
import com.google.common.reflect.Parameter;
import com.google.common.reflect.TypeToken;
import com.google.common.util.concurrent.FutureFallback;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.inject.Binding;
@ -70,27 +116,23 @@ import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Provides;
import com.google.inject.ProvisionException;
import com.google.inject.TypeLiteral;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.util.Types;
/**
* Generates RESTful clients from appropriately annotated interfaces.
* <p/>
* Particularly, this code delegates calls to other things.
* <ol>
* <li>if the method has a {@link Provides} annotation, it responds via a
* {@link Injector} lookup</li>
* <li>if the method has a {@link Delegate} annotation, it responds with an
* instance of interface set in returnVal, adding the current JAXrs annotations
* to whatever are on that class.</li>
* <li>if the method has a {@link Provides} annotation, it responds via a {@link Injector} lookup</li>
* <li>if the method has a {@link Delegate} annotation, it responds with an instance of interface set in returnVal,
* adding the current JAXrs annotations to whatever are on that class.</li>
* <ul>
* <li>ex. if the method with {@link Delegate} has a {@code Path} annotation,
* and the returnval interface also has {@code Path}, these values are combined.
* </li>
* <li>ex. if the method with {@link Delegate} has a {@code Path} annotation, and the returnval interface also has
* {@code Path}, these values are combined.</li>
* </ul>
* <li>if {@link RestAnnotationProcessor#delegationMap} contains a mapping for
* this, and the returnVal is properly assigned as a {@link ListenableFuture},
* it responds with an http implementation.</li>
* <li>if {@link RestAnnotationProcessor#delegationMap} contains a mapping for this, and the returnVal is properly
* assigned as a {@link ListenableFuture}, it responds with an http implementation.</li>
* <li>otherwise a RuntimeException is thrown with a message including:
* {@code method is intended solely to set constants}</li>
* </ol>
@ -102,51 +144,86 @@ public abstract class AsyncRestClientProxy extends AbstractInvocationHandler {
public static interface Factory {
Declaring declaring(Class<?> declaring);
Caller caller(ClassMethodArgs caller);
Caller caller(ClassInvokerArgs caller);
}
public final static class Declaring extends AsyncRestClientProxy {
@Inject
private Declaring(Injector injector, Function<ClassMethodArgsAndReturnVal, Optional<Object>> optionalConverter,
private Declaring(Injector injector, Function<ClassInvokerArgsAndReturnVal, Optional<Object>> optionalConverter,
HttpCommandExecutorService http, @Named(Constants.PROPERTY_USER_THREADS) ExecutorService userThreads,
@Named("async") LoadingCache<ClassMethodArgs, Object> delegateMap, RestAnnotationProcessor.Factory rap,
@Assisted Class<?> declaring) {
super(injector, optionalConverter, http, userThreads, delegateMap, rap.declaring(declaring), declaring);
@Named("async") LoadingCache<ClassInvokerArgs, Object> delegateMap, RestAnnotationProcessor.Factory rap,
ParseSax.Factory parserFactory, @Assisted Class<?> declaring) {
super(injector, optionalConverter, http, userThreads, delegateMap, rap.declaring(declaring), parserFactory,
declaring);
}
}
public final static class Caller extends AsyncRestClientProxy {
@Inject
private Caller(Injector injector, Function<ClassMethodArgsAndReturnVal, Optional<Object>> optionalConverter,
private Caller(Injector injector, Function<ClassInvokerArgsAndReturnVal, Optional<Object>> optionalConverter,
HttpCommandExecutorService http, @Named(Constants.PROPERTY_USER_THREADS) ExecutorService userThreads,
@Named("async") LoadingCache<ClassMethodArgs, Object> delegateMap, RestAnnotationProcessor.Factory rap,
@Assisted ClassMethodArgs caller) {
super(injector, optionalConverter, http, userThreads, delegateMap, rap.caller(caller), caller.getClazz());
@Named("async") LoadingCache<ClassInvokerArgs, Object> delegateMap, RestAnnotationProcessor.Factory rap,
ParseSax.Factory parserFactory, @Assisted ClassInvokerArgs caller) {
super(injector, optionalConverter, http, userThreads, delegateMap, rap.caller(caller), parserFactory, caller
.getClazz());
}
}
@Resource
private Logger logger = Logger.NULL;
private final Injector injector;
private final HttpCommandExecutorService http;
private final ExecutorService userThreads;
private final Function<ClassMethodArgsAndReturnVal, Optional<Object>> optionalConverter;
private final LoadingCache<ClassMethodArgs, Object> delegateMap;
private final Function<ClassInvokerArgsAndReturnVal, Optional<Object>> optionalConverter;
private final LoadingCache<ClassInvokerArgs, Object> delegateMap;
private final RestAnnotationProcessor annotationProcessor;
private final ParseSax.Factory parserFactory;
private final Class<?> declaring;
private static final LoadingCache<Class<?>, Cache<InvokerKey, Invokable<?, ?>>> delegationMapCache = CacheBuilder
.newBuilder().build(new CacheLoader<Class<?>, Cache<InvokerKey, Invokable<?, ?>>>() {
public Cache<InvokerKey, Invokable<?, ?>> load(Class<?> declaring) throws ExecutionException {
Cache<InvokerKey, Invokable<?, ?>> delegationMap = CacheBuilder.newBuilder()
.<InvokerKey, Invokable<?, ?>> build();
for (Method method : difference(ImmutableSet.copyOf(declaring.getMethods()),
ImmutableSet.copyOf(Object.class.getMethods()))) {
Invokable<?, ?> invoker = Invokable.from(method);
if (isHttpMethod(invoker) || method.isAnnotationPresent(Delegate.class)) {
delegationMap.get(new InvokerKey(invoker), returning(invoker));
} else if (!method.getDeclaringClass().equals(declaring)) { // potentially overridden
} else if (method.isAnnotationPresent(Provides.class)) {
}
}
return delegationMap;
}
});
private Invokable<?, ?> getDelegateOrNull(Invokable<?, ?> in) {
return delegationMapCache.getUnchecked(declaring).getIfPresent(new InvokerKey(in));
}
private static boolean isHttpMethod(Invokable<?, ?> invoker) {
return invoker.isAnnotationPresent(Path.class) || tryFindHttpMethod(invoker).isPresent()
|| any(invoker.getParameters(), new Predicate<Parameter>() {
public boolean apply(Parameter in) {
return in.getType().isAssignableFrom(HttpRequest.class);
}
});
}
private AsyncRestClientProxy(Injector injector,
Function<ClassMethodArgsAndReturnVal, Optional<Object>> optionalConverter, HttpCommandExecutorService http,
ExecutorService userThreads, LoadingCache<ClassMethodArgs, Object> delegateMap,
RestAnnotationProcessor annotationProcessor, Class<?> declaring) {
Function<ClassInvokerArgsAndReturnVal, Optional<Object>> optionalConverter, HttpCommandExecutorService http,
ExecutorService userThreads, LoadingCache<ClassInvokerArgs, Object> delegateMap,
RestAnnotationProcessor annotationProcessor, ParseSax.Factory parserFactory, Class<?> declaring) {
this.injector = injector;
this.optionalConverter = optionalConverter;
this.http = http;
this.userThreads = userThreads;
this.delegateMap = delegateMap;
this.declaring = declaring;
this.annotationProcessor = annotationProcessor;
this.parserFactory = parserFactory;
this.declaring = declaring;
}
private static final Predicate<Annotation> isQualifierPresent = new Predicate<Annotation>() {
@ -172,44 +249,42 @@ public abstract class AsyncRestClientProxy extends AbstractInvocationHandler {
}
}
public boolean isRestCall(Method method) {
return annotationProcessor.getDelegateOrNull(method) != null
private boolean isRestCall(Method method) {
return getDelegateOrNull(Invokable.from(method)) != null
&& ListenableFuture.class.isAssignableFrom(method.getReturnType());
}
public Object propagateContextToDelegate(Method method, Object[] args) throws ExecutionException {
Class<?> asyncClass = Optionals2.returnTypeOrTypeOfOptional(method);
ClassMethodArgs cma = new ClassMethodArgs(asyncClass, method, args);
private Object propagateContextToDelegate(Method method, Object[] args) throws ExecutionException {
Class<?> asyncClass = returnTypeOrTypeOfOptional(method);
ClassInvokerArgs cma = ClassInvokerArgs.builder().clazz(asyncClass).invoker(method).args(args).build();
Object returnVal = delegateMap.get(cma);
if (Optionals2.isReturnTypeOptional(method)) {
ClassMethodArgsAndReturnVal cmar = ClassMethodArgsAndReturnVal.builder().fromClassMethodArgs(cma)
if (isReturnTypeOptional(method)) {
ClassInvokerArgsAndReturnVal cmar = ClassInvokerArgsAndReturnVal.builder().fromClassInvokerArgs(cma)
.returnVal(returnVal).build();
return optionalConverter.apply(cmar);
}
return returnVal;
}
public Object lookupValueFromGuice(Method method) {
private Object lookupValueFromGuice(Method method) {
try {
// TODO: tidy
Type genericReturnType = method.getGenericReturnType();
try {
Annotation qualifier = Iterables.find(ImmutableList.copyOf(method.getAnnotations()), isQualifierPresent);
Annotation qualifier = find(ImmutableList.copyOf(method.getAnnotations()), isQualifierPresent);
return getInstanceOfTypeWithQualifier(genericReturnType, qualifier);
} catch (ProvisionException e) {
throw Throwables.propagate(e.getCause());
throw propagate(e.getCause());
} catch (RuntimeException e) {
return instanceOfTypeOrPropagate(genericReturnType, e);
}
} catch (ProvisionException e) {
AuthorizationException aex = Throwables2.getFirstThrowableOfType(e, AuthorizationException.class);
AuthorizationException aex = getFirstThrowableOfType(e, AuthorizationException.class);
if (aex != null)
throw aex;
throw e;
}
}
// TODO: tidy
private Object instanceOfTypeOrPropagate(Type genericReturnType, RuntimeException e) {
try {
// look for an existing binding
@ -218,7 +293,7 @@ public abstract class AsyncRestClientProxy extends AbstractInvocationHandler {
return binding.getProvider().get();
// then, try looking via supplier
binding = injector.getExistingBinding(Key.get(Types.newParameterizedType(Supplier.class, genericReturnType)));
binding = injector.getExistingBinding(Key.get(newParameterizedType(Supplier.class, genericReturnType)));
if (binding != null)
return Supplier.class.cast(binding.getProvider().get()).get();
@ -229,7 +304,6 @@ public abstract class AsyncRestClientProxy extends AbstractInvocationHandler {
}
}
// TODO: tidy
private Object getInstanceOfTypeWithQualifier(Type genericReturnType, Annotation qualifier) {
// look for an existing binding
Binding<?> binding = injector.getExistingBinding(Key.get(genericReturnType, qualifier));
@ -237,40 +311,102 @@ public abstract class AsyncRestClientProxy extends AbstractInvocationHandler {
return binding.getProvider().get();
// then, try looking via supplier
binding = injector.getExistingBinding(Key.get(Types.newParameterizedType(Supplier.class, genericReturnType),
qualifier));
binding = injector
.getExistingBinding(Key.get(newParameterizedType(Supplier.class, genericReturnType), qualifier));
if (binding != null)
return Supplier.class.cast(binding.getProvider().get()).get();
// else try to create an instance
return injector.getInstance(Key.get(genericReturnType, qualifier));
}
@Deprecated
static Function<HttpResponse, ?> createResponseParser(ParseSax.Factory parserFactory, Injector injector,
Method method, HttpRequest request) {
return createResponseParser(parserFactory, injector, Invokable.from(method), request);
}
@SuppressWarnings("unchecked")
@VisibleForTesting
private static Function<HttpResponse, ?> createResponseParser(ParseSax.Factory parserFactory, Injector injector,
Invokable<?, ?> method, HttpRequest request) {
Function<HttpResponse, ?> transformer;
Class<? extends HandlerWithResult<?>> handler = getSaxResponseParserClassOrNull(method);
if (handler != null) {
transformer = parserFactory.create(injector.getInstance(handler));
} else {
transformer = getTransformerForMethod(method, injector);
}
if (transformer instanceof InvocationContext<?>) {
((InvocationContext<?>) transformer).setContext(request);
}
if (method.isAnnotationPresent(Transform.class)) {
Function<?, ?> wrappingTransformer = injector.getInstance(method.getAnnotation(Transform.class).value());
if (wrappingTransformer instanceof InvocationContext<?>) {
((InvocationContext<?>) wrappingTransformer).setContext(request);
}
transformer = compose(Function.class.cast(wrappingTransformer), transformer);
}
return transformer;
}
private static Class<? extends HandlerWithResult<?>> getSaxResponseParserClassOrNull(Invokable<?, ?> method) {
XMLResponseParser annotation = method.getAnnotation(XMLResponseParser.class);
if (annotation != null) {
return annotation.value();
}
return null;
}
// TODO: refactor this out of here
@VisibleForTesting
@SuppressWarnings({ "rawtypes", "unchecked" })
public static Function<HttpResponse, ?> getTransformerForMethod(Invokable<?, ?> method, Injector injector) {
Function<HttpResponse, ?> transformer;
if (method.isAnnotationPresent(SelectJson.class)) {
Type returnVal = getReturnTypeFor(method.getReturnType());
if (method.isAnnotationPresent(OnlyElement.class))
returnVal = newParameterizedType(Set.class, returnVal);
transformer = new ParseFirstJsonValueNamed(injector.getInstance(GsonWrapper.class),
TypeLiteral.get(returnVal), method.getAnnotation(SelectJson.class).value());
if (method.isAnnotationPresent(OnlyElement.class))
transformer = compose(new OnlyElementOrNull(), transformer);
} else {
transformer = injector.getInstance(getParserOrThrowException(method));
}
return transformer;
}
private ListenableFuture<?> createListenableFutureForHttpRequestMappedToMethodAndArgs(Method method, Object[] args)
throws ExecutionException {
method = annotationProcessor.getDelegateOrNull(method);
String name = method.getDeclaringClass().getSimpleName() + "." + method.getName();
return createListenableFutureForHttpRequestMappedToMethodAndArgs(method, Invokable.from(method),
args == null ? newArrayList(new Object[] { null }) : newArrayList(args));
}
private ListenableFuture<?> createListenableFutureForHttpRequestMappedToMethodAndArgs(Method method,
Invokable<?, ?> invoker, List<Object> args) throws ExecutionException {
String name = invoker.getDeclaringClass().getSimpleName() + "." + invoker.getName();
logger.trace(">> converting %s", name);
FutureFallback<?> fallback = fallbacks.getUnchecked(method);
FutureFallback<?> fallback = fallbacks.getUnchecked(invoker);
// in case there is an exception creating the request, we should at least pass in args
if (fallback instanceof InvocationContext) {
InvocationContext.class.cast(fallback).setContext((HttpRequest) null);
}
ListenableFuture<?> result;
try {
GeneratedHttpRequest request = annotationProcessor.createRequest(method, args);
GeneratedHttpRequest request = annotationProcessor.createRequest(method, invoker, newArrayList(args));
if (fallback instanceof InvocationContext) {
InvocationContext.class.cast(fallback).setContext(request);
}
logger.trace("<< converted %s to %s", name, request.getRequestLine());
Function<HttpResponse, ?> transformer = annotationProcessor.createResponseParser(method, request);
Function<HttpResponse, ?> transformer = createResponseParser(parserFactory, injector, invoker, request);
logger.trace("<< response from %s is parsed by %s", name, transformer.getClass().getSimpleName());
logger.debug(">> invoking %s", name);
result = transform(makeListenable(http.submit(new HttpCommand(request)), userThreads), transformer);
} catch (RuntimeException e) {
AuthorizationException aex = Throwables2.getFirstThrowableOfType(e, AuthorizationException.class);
AuthorizationException aex = getFirstThrowableOfType(e, AuthorizationException.class);
if (aex != null)
e = aex;
try {
@ -283,15 +419,16 @@ public abstract class AsyncRestClientProxy extends AbstractInvocationHandler {
return withFallback(result, fallback);
}
@Override
public String toString() {
return "Client Proxy for :" + declaring.getName();
}
private final LoadingCache<Method, FutureFallback<?>> fallbacks = CacheBuilder.newBuilder().build(
new CacheLoader<Method, FutureFallback<?>>() {
private final LoadingCache<Invokable<?, ?>, FutureFallback<?>> fallbacks = CacheBuilder.newBuilder().build(
new CacheLoader<Invokable<?, ?>, FutureFallback<?>>() {
@Override
public FutureFallback<?> load(Method key) throws Exception {
public FutureFallback<?> load(Invokable<?, ?> key) throws Exception {
Fallback annotation = key.getAnnotation(Fallback.class);
if (annotation != null) {
return injector.getInstance(annotation.value());
@ -301,4 +438,96 @@ public abstract class AsyncRestClientProxy extends AbstractInvocationHandler {
});
private static final TypeToken<ListenableFuture<Boolean>> futureBooleanLiteral = new TypeToken<ListenableFuture<Boolean>>() {
private static final long serialVersionUID = 1L;
};
private static final TypeToken<ListenableFuture<String>> futureStringLiteral = new TypeToken<ListenableFuture<String>>() {
private static final long serialVersionUID = 1L;
};
private static final TypeToken<ListenableFuture<Void>> futureVoidLiteral = new TypeToken<ListenableFuture<Void>>() {
private static final long serialVersionUID = 1L;
};
private static final TypeToken<ListenableFuture<URI>> futureURILiteral = new TypeToken<ListenableFuture<URI>>() {
private static final long serialVersionUID = 1L;
};
private static final TypeToken<ListenableFuture<InputStream>> futureInputStreamLiteral = new TypeToken<ListenableFuture<InputStream>>() {
private static final long serialVersionUID = 1L;
};
private static final TypeToken<ListenableFuture<HttpResponse>> futureHttpResponseLiteral = new TypeToken<ListenableFuture<HttpResponse>>() {
private static final long serialVersionUID = 1L;
};
@Deprecated
static Key<? extends Function<HttpResponse, ?>> getParserOrThrowException(Method method) {
return getParserOrThrowException(Invokable.from(method));
}
@SuppressWarnings("unchecked")
private static Key<? extends Function<HttpResponse, ?>> getParserOrThrowException(Invokable<?, ?> method) {
ResponseParser annotation = method.getAnnotation(ResponseParser.class);
if (annotation == null) {
if (method.getReturnType().equals(void.class) || method.getReturnType().equals(futureVoidLiteral)) {
return Key.get(ReleasePayloadAndReturn.class);
} else if (method.getReturnType().equals(boolean.class) || method.getReturnType().equals(Boolean.class)
|| method.getReturnType().equals(futureBooleanLiteral)) {
return Key.get(ReturnTrueIf2xx.class);
} else if (method.getReturnType().equals(InputStream.class)
|| method.getReturnType().equals(futureInputStreamLiteral)) {
return Key.get(ReturnInputStream.class);
} else if (method.getReturnType().equals(HttpResponse.class)
|| method.getReturnType().equals(futureHttpResponseLiteral)) {
return Key.get(Class.class.cast(IdentityFunction.class));
} else if (RestAnnotationProcessor.getAcceptHeadersOrNull(method).contains(APPLICATION_JSON)) {
return getJsonParserKeyForMethod(method);
} else if (RestAnnotationProcessor.getAcceptHeadersOrNull(method).contains(APPLICATION_XML)
|| method.isAnnotationPresent(JAXBResponseParser.class)) {
return getJAXBParserKeyForMethod(method);
} else if (method.getReturnType().equals(String.class) || method.getReturnType().equals(futureStringLiteral)) {
return Key.get(ReturnStringIf2xx.class);
} else if (method.getReturnType().equals(URI.class) || method.getReturnType().equals(futureURILiteral)) {
return Key.get(ParseURIFromListOrLocationHeaderIf20x.class);
} else {
throw new IllegalStateException("You must specify a ResponseParser annotation on: " + method.toString());
}
}
return Key.get(annotation.value());
}
@SuppressWarnings("unchecked")
private static Key<? extends Function<HttpResponse, ?>> getJAXBParserKeyForMethod(Invokable<?, ?> method) {
Optional<Type> configuredReturnVal = Optional.absent();
if (method.isAnnotationPresent(JAXBResponseParser.class)) {
Type configuredClass = method.getAnnotation(JAXBResponseParser.class).value();
configuredReturnVal = configuredClass.equals(NullType.class) ? Optional.<Type> absent() : Optional
.<Type> of(configuredClass);
}
Type returnVal = configuredReturnVal.or(getReturnTypeFor(method.getReturnType()));
Type parserType = newParameterizedType(ParseXMLWithJAXB.class, returnVal);
return (Key<? extends Function<HttpResponse, ?>>) Key.get(parserType);
}
@SuppressWarnings({ "unchecked" })
private static Key<? extends Function<HttpResponse, ?>> getJsonParserKeyForMethod(Invokable<?, ?> method) {
ParameterizedType parserType;
if (method.isAnnotationPresent(Unwrap.class)) {
parserType = newParameterizedType(UnwrapOnlyJsonValue.class, getReturnTypeFor(method.getReturnType()));
} else {
parserType = newParameterizedType(ParseJson.class, getReturnTypeFor(method.getReturnType()));
}
return (Key<? extends Function<HttpResponse, ?>>) Key.get(parserType);
}
private static Type getReturnTypeFor(TypeToken<?> typeToken) {
Type returnVal = typeToken.getType();
if (typeToken.getRawType().getTypeParameters().length == 0) {
returnVal = typeToken.getRawType();
} else if (typeToken.getRawType().equals(ListenableFuture.class)) {
ParameterizedType futureType = (ParameterizedType) typeToken.getType();
returnVal = futureType.getActualTypeArguments()[0];
if (returnVal instanceof WildcardType)
returnVal = WildcardType.class.cast(returnVal).getUpperBounds()[0];
}
return returnVal;
}
}

View File

@ -20,13 +20,13 @@ package org.jclouds.rest.internal;
import static com.google.common.reflect.Reflection.newProxy;
import org.jclouds.internal.ClassMethodArgs;
import org.jclouds.internal.ClassInvokerArgs;
import org.jclouds.rest.internal.AsyncRestClientProxy.Factory;
import com.google.common.cache.CacheLoader;
import com.google.inject.Inject;
public final class CreateAsyncClientForCaller extends CacheLoader<ClassMethodArgs, Object> {
public final class CreateAsyncClientForCaller extends CacheLoader<ClassInvokerArgs, Object> {
private final Factory factory;
@Inject
@ -35,7 +35,7 @@ public final class CreateAsyncClientForCaller extends CacheLoader<ClassMethodArg
}
@Override
public Object load(ClassMethodArgs from) {
public Object load(ClassInvokerArgs from) {
return newProxy(from.getClazz(), factory.caller(from));
}
}

View File

@ -27,7 +27,7 @@ import javax.inject.Inject;
import javax.inject.Named;
import org.jclouds.concurrent.internal.SyncProxy;
import org.jclouds.internal.ClassMethodArgs;
import org.jclouds.internal.ClassInvokerArgs;
import org.jclouds.util.Optionals2;
import com.google.common.cache.CacheLoader;
@ -39,22 +39,22 @@ import com.google.common.cache.LoadingCache;
*
* @author Adrian Cole
*/
public class CreateClientForCaller extends CacheLoader<ClassMethodArgs, Object> {
public class CreateClientForCaller extends CacheLoader<ClassInvokerArgs, Object> {
private final SyncProxy.Factory factory;
private final LoadingCache<ClassMethodArgs, Object> asyncMap;
private final LoadingCache<ClassInvokerArgs, Object> asyncMap;
private final Map<Class<?>, Class<?>> sync2Async;
@Inject
private CreateClientForCaller(SyncProxy.Factory factory,
@Named("async") LoadingCache<ClassMethodArgs, Object> asyncMap, Map<Class<?>, Class<?>> sync2Async) {
@Named("async") LoadingCache<ClassInvokerArgs, Object> asyncMap, Map<Class<?>, Class<?>> sync2Async) {
this.factory = factory;
this.asyncMap = asyncMap;
this.sync2Async = sync2Async;
}
@Override
public Object load(ClassMethodArgs from) {
Class<?> syncClass = Optionals2.returnTypeOrTypeOfOptional(from.getMethod());
public Object load(ClassInvokerArgs from) {
Class<?> syncClass = Optionals2.returnTypeOrTypeOfOptional(from.getInvoker());
Class<?> asyncClass = sync2Async.get(syncClass);
checkState(asyncClass != null, "configuration error, sync class " + syncClass + " not mapped to an async class");
Object asyncClient = asyncMap.getUnchecked(from);

View File

@ -28,13 +28,14 @@ import java.util.List;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpRequestFilter;
import org.jclouds.internal.ClassMethodArgs;
import org.jclouds.internal.ClassInvokerArgs;
import org.jclouds.io.Payload;
import org.jclouds.javax.annotation.Nullable;
import com.google.common.base.Optional;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.common.reflect.Invokable;
/**
* Represents a request generated from annotations
@ -54,9 +55,10 @@ public class GeneratedHttpRequest extends HttpRequest {
public static class Builder extends HttpRequest.Builder<Builder> {
protected Class<?> declaring;
protected Method javaMethod;
protected Invokable<?, ?> invoker;
// args can be null, so cannot use immutable list
protected List<Object> args = Lists.newArrayList();
protected Optional<ClassMethodArgs> caller = Optional.absent();
protected Optional<ClassInvokerArgs> caller = Optional.absent();
/**
* @see GeneratedHttpRequest#getDeclaring()
@ -69,11 +71,21 @@ public class GeneratedHttpRequest extends HttpRequest {
/**
* @see GeneratedHttpRequest#getJavaMethod()
*/
@Deprecated
public Builder javaMethod(Method javaMethod) {
this.javaMethod = checkNotNull(javaMethod, "javaMethod");
this.invoker(Invokable.from(javaMethod));
return this;
}
/**
* @see GeneratedHttpRequest#getInvoker()
*/
public Builder invoker(Invokable<?, ?> invoker) {
this.invoker = checkNotNull(invoker, "invoker");
return this;
}
/**
* @see GeneratedHttpRequest#getArgs()
*/
@ -100,20 +112,21 @@ public class GeneratedHttpRequest extends HttpRequest {
/**
* @see GeneratedHttpRequest#getCaller()
*/
public Builder caller(@Nullable ClassMethodArgs caller) {
public Builder caller(@Nullable ClassInvokerArgs caller) {
this.caller = Optional.fromNullable(caller);
return this;
}
public GeneratedHttpRequest build() {
return new GeneratedHttpRequest(method, endpoint, headers.build(), payload, declaring, javaMethod,
args, filters.build(), caller);
return new GeneratedHttpRequest(method, endpoint, headers.build(), payload, declaring, javaMethod, invoker,
args, filters.build(), caller);
}
public Builder fromGeneratedHttpRequest(GeneratedHttpRequest in) {
return super.fromHttpRequest(in)
.declaring(in.getDeclaring())
.javaMethod(in.getJavaMethod())
.invoker(in.invoker)
.args(in.getArgs())
.caller(in.getCaller().orNull());
}
@ -126,15 +139,17 @@ public class GeneratedHttpRequest extends HttpRequest {
private final Class<?> declaring;
private final Method javaMethod;
private final Invokable<?, ?> invoker;
private final List<Object> args;
private final Optional<ClassMethodArgs> caller;
private final Optional<ClassInvokerArgs> caller;
protected GeneratedHttpRequest(String method, URI endpoint, Multimap<String, String> headers,
@Nullable Payload payload, Class<?> declaring, Method javaMethod, Iterable<Object> args,
Iterable<HttpRequestFilter> filters, Optional<ClassMethodArgs> caller) {
@Nullable Payload payload, Class<?> declaring, Method javaMethod, Invokable<?, ?> invoker,
Iterable<Object> args, Iterable<HttpRequestFilter> filters, Optional<ClassInvokerArgs> caller) {
super(method, endpoint, headers, payload, filters);
this.declaring = checkNotNull(declaring, "declaring");
this.javaMethod = checkNotNull(javaMethod, "javaMethod");
this.invoker = checkNotNull(invoker, "invoker");
// TODO make immutable. ImmutableList.of() doesn't accept nulls
this.args = Lists.newArrayList(checkNotNull(args, "args"));
this.caller = checkNotNull(caller, "caller");
@ -144,15 +159,23 @@ public class GeneratedHttpRequest extends HttpRequest {
return declaring;
}
/**
* @deprecated see {@link #getInvoker()}
*/
@Deprecated
public Method getJavaMethod() {
return javaMethod;
}
public Invokable<?,?> getInvoker() {
return invoker;
}
public List<Object> getArgs() {
return Collections.unmodifiableList(args);
}
public Optional<ClassMethodArgs> getCaller() {
public Optional<ClassInvokerArgs> getCaller() {
return caller;
}
}

View File

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

View File

@ -24,18 +24,28 @@ import java.lang.reflect.Type;
import java.lang.reflect.WildcardType;
import com.google.common.base.Optional;
import com.google.common.reflect.Invokable;
import com.google.common.reflect.TypeToken;
/**
*
* @author Adrian Cole
*/
public class Optionals2 {
public static Class<?> returnTypeOrTypeOfOptional(Invokable<?, ?> method) {
TypeToken<?> type = method.getReturnType();
return returnTypeOrTypeOfOptional(type.getRawType(), type.getType());
}
public static Class<?> returnTypeOrTypeOfOptional(Method method) {
boolean optional = isReturnTypeOptional(method);
Class<?> syncClass;
if (optional) {
ParameterizedType futureType = ParameterizedType.class.cast(method.getGenericReturnType());
Class<?> syncClass = method.getReturnType();
Type genericType = method.getGenericReturnType();
return returnTypeOrTypeOfOptional(syncClass, genericType);
}
private static Class<?> returnTypeOrTypeOfOptional(Class<?> syncClass, Type genericType) {
if (syncClass.isAssignableFrom(Optional.class)) {
ParameterizedType futureType = ParameterizedType.class.cast(genericType);
// TODO: error checking in case this is a type, not a class.
Type t = futureType.getActualTypeArguments()[0];
if (t instanceof WildcardType) {
@ -43,14 +53,12 @@ public class Optionals2 {
}
syncClass = Class.class.cast(t);
} else {
syncClass = method.getReturnType();
}
return syncClass;
}
public static boolean isReturnTypeOptional(Method method) {
boolean optional = method.getReturnType().isAssignableFrom(Optional.class);
return optional;
return method.getReturnType().isAssignableFrom(Optional.class);
}
}

View File

@ -29,7 +29,7 @@ import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.jclouds.internal.ClassMethodArgs;
import org.jclouds.internal.ClassInvokerArgs;
import org.jclouds.rest.functions.AlwaysPresentImplicitOptionalConverter;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
@ -103,7 +103,7 @@ public class SyncProxyTest {
}
private Sync syncProxyForTimeouts(ImmutableMap<String, Long> timeouts) throws NoSuchMethodException {
LoadingCache<ClassMethodArgs, Object> cache = CacheBuilder.newBuilder().build(
LoadingCache<ClassInvokerArgs, Object> cache = CacheBuilder.newBuilder().build(
CacheLoader.from(Functions.<Object> constant(null)));
return newProxy(
Sync.class,

View File

@ -31,11 +31,12 @@ import org.jclouds.http.HttpResponse;
import org.jclouds.io.Payload;
import org.jclouds.io.Payloads;
import org.jclouds.json.config.GsonModule;
import org.jclouds.rest.internal.RestAnnotationProcessor;
import org.jclouds.rest.internal.AsyncRestClientProxy;
import org.testng.annotations.Test;
import com.google.common.base.Function;
import com.google.common.base.Throwables;
import com.google.common.reflect.Invokable;
import com.google.inject.Guice;
import com.google.inject.Injector;
@ -55,8 +56,8 @@ public abstract class BaseParserTest<T, G> {
@SuppressWarnings("unchecked")
protected Function<HttpResponse, T> parser(Injector i) {
try {
return (Function<HttpResponse, T>) RestAnnotationProcessor.getTransformerForMethod(getClass()
.getMethod("expected"), i);
return (Function<HttpResponse, T>) AsyncRestClientProxy.getTransformerForMethod(
Invokable.from(getClass().getMethod("expected")), i);
} catch (Exception e) {
throw Throwables.propagate(e);
}

View File

@ -25,7 +25,7 @@ import static org.testng.Assert.assertTrue;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
import org.jclouds.internal.ClassMethodArgsAndReturnVal;
import org.jclouds.internal.ClassInvokerArgsAndReturnVal;
import org.jclouds.rest.annotations.Delegate;
import org.jclouds.rest.annotations.SinceApiVersion;
import org.jclouds.rest.functions.PresentWhenApiVersionLexicographicallyAtOrAfterSinceApiVersion.Loader;
@ -120,7 +120,7 @@ public class PresentWhenApiVersionLexicographicallyAtOrAfterSinceApiVersionTest
}
public void testCacheIsFasterWhenNoAnnotationPresent() {
ClassMethodArgsAndReturnVal keyPairApi = getKeyPairApi();
ClassInvokerArgsAndReturnVal keyPairApi = getKeyPairApi();
ImplicitOptionalConverter fn = forApiVersion("2011-07-15");
Stopwatch watch = new Stopwatch().start();
fn.apply(keyPairApi);
@ -134,7 +134,7 @@ public class PresentWhenApiVersionLexicographicallyAtOrAfterSinceApiVersionTest
}
public void testCacheIsFasterWhenAnnotationPresent() {
ClassMethodArgsAndReturnVal floatingIpApi = getKeyPairApi();
ClassInvokerArgsAndReturnVal floatingIpApi = getKeyPairApi();
ImplicitOptionalConverter fn = forApiVersion("2011-07-15");
Stopwatch watch = new Stopwatch().start();
fn.apply(floatingIpApi);
@ -148,22 +148,22 @@ public class PresentWhenApiVersionLexicographicallyAtOrAfterSinceApiVersionTest
}
ClassMethodArgsAndReturnVal getFloatingIPApi() {
ClassInvokerArgsAndReturnVal getFloatingIPApi() {
return getApi("Tag", TagAsyncApi.class);
}
ClassMethodArgsAndReturnVal getKeyPairApi() {
ClassInvokerArgsAndReturnVal getKeyPairApi() {
return getApi("KeyPair", KeyPairAsyncApi.class);
}
ClassMethodArgsAndReturnVal getVpcApi() {
ClassInvokerArgsAndReturnVal getVpcApi() {
return getApi("Vpc", VpcAsyncApi.class);
}
ClassMethodArgsAndReturnVal getApi(String name, Class<?> type) {
ClassInvokerArgsAndReturnVal getApi(String name, Class<?> type) {
try {
return ClassMethodArgsAndReturnVal.builder().clazz(type)
.method(EC2AsyncApi.class.getDeclaredMethod("get" + name + "ApiForRegion", String.class))
return ClassInvokerArgsAndReturnVal.builder().clazz(type)
.invoker(EC2AsyncApi.class.getDeclaredMethod("get" + name + "ApiForRegion", String.class))
.args(new Object[] { "region" }).returnVal("present").build();
} catch (Exception e) {
throw propagate(e);

View File

@ -22,8 +22,6 @@ import static com.google.common.hash.Hashing.md5;
import static org.easymock.EasyMock.createMock;
import static org.eclipse.jetty.http.HttpHeaders.TRANSFER_ENCODING;
import static org.jclouds.io.ByteSources.asByteSource;
import static org.jclouds.rest.internal.RestAnnotationProcessor.createResponseParser;
import static org.jclouds.rest.internal.RestAnnotationProcessor.getSaxResponseParserClassOrNull;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNull;
@ -44,6 +42,7 @@ import org.jclouds.http.functions.ParseSax;
import org.jclouds.io.MutableContentMetadata;
import org.jclouds.javax.annotation.Nullable;
import org.jclouds.rest.annotations.Fallback;
import org.jclouds.rest.annotations.XMLResponseParser;
import org.jclouds.util.Strings2;
import org.testng.annotations.Test;
@ -171,11 +170,13 @@ public abstract class BaseRestApiTest {
}
protected void assertSaxResponseParserClassEquals(Method method, @Nullable Class<?> parserClass) {
assertEquals(getSaxResponseParserClassOrNull(method), parserClass);
XMLResponseParser annotation = method.getAnnotation(XMLResponseParser.class);
Class<?> expected = (annotation != null) ? annotation.value() :null;
assertEquals(expected, parserClass);
}
protected void assertResponseParserClassEquals(Method method, HttpRequest request, @Nullable Class<?> parserClass) {
assertEquals(createResponseParser(parserFactory, injector, method, request).getClass(), parserClass);
assertEquals(AsyncRestClientProxy.createResponseParser(parserFactory, injector, method, request).getClass(), parserClass);
}
protected RestAnnotationProcessor factory(Class<?> clazz) {

View File

@ -64,7 +64,6 @@ import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.xml.bind.annotation.XmlRootElement;
import org.eclipse.jetty.http.HttpHeaders;
import org.jclouds.ContextBuilder;
@ -81,7 +80,6 @@ import org.jclouds.http.functions.ParseFirstJsonValueNamed;
import org.jclouds.http.functions.ParseJson;
import org.jclouds.http.functions.ParseSax;
import org.jclouds.http.functions.ParseURIFromListOrLocationHeaderIf20x;
import org.jclouds.http.functions.ParseXMLWithJAXB;
import org.jclouds.http.functions.ReturnInputStream;
import org.jclouds.http.functions.ReturnStringIf2xx;
import org.jclouds.http.functions.ReturnTrueIf2xx;
@ -90,7 +88,7 @@ import org.jclouds.http.internal.PayloadEnclosingImpl;
import org.jclouds.http.options.BaseHttpRequestOptions;
import org.jclouds.http.options.GetOptions;
import org.jclouds.http.options.HttpRequestOptions;
import org.jclouds.internal.ClassMethodArgsAndReturnVal;
import org.jclouds.internal.ClassInvokerArgsAndReturnVal;
import org.jclouds.io.Payload;
import org.jclouds.io.PayloadEnclosing;
import org.jclouds.io.Payloads;
@ -106,7 +104,6 @@ import org.jclouds.rest.annotations.Endpoint;
import org.jclouds.rest.annotations.EndpointParam;
import org.jclouds.rest.annotations.FormParams;
import org.jclouds.rest.annotations.Headers;
import org.jclouds.rest.annotations.JAXBResponseParser;
import org.jclouds.rest.annotations.MapBinder;
import org.jclouds.rest.annotations.OnlyElement;
import org.jclouds.rest.annotations.OverrideRequestFilters;
@ -129,7 +126,6 @@ import org.jclouds.rest.config.AsyncClientProvider;
import org.jclouds.rest.config.RestClientModule;
import org.jclouds.rest.functions.ImplicitOptionalConverter;
import org.jclouds.util.Strings2;
import org.jclouds.xml.XMLParser;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
@ -181,7 +177,6 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
}).annotatedWith(Localhost2.class).toInstance(Suppliers.ofInstance(URI.create("http://localhost:1111")));
bind(IOExceptionRetryHandler.class).toInstance(IOExceptionRetryHandler.NEVER_RETRY);
}
}
@Path("/client/{jclouds.api-version}")
@ -394,7 +389,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
bind(ImplicitOptionalConverter.class).toInstance(new ImplicitOptionalConverter() {
@Override
public Optional<Object> apply(ClassMethodArgsAndReturnVal input) {
public Optional<Object> apply(ClassInvokerArgsAndReturnVal input) {
return Optional.absent();
}
@ -1177,7 +1172,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
assertResponseParserClassEquals(method, request, ParseJson.class);
// now test that it works!
Function<HttpResponse, View> parser = (Function<HttpResponse, View>) RestAnnotationProcessor
Function<HttpResponse, View> parser = (Function<HttpResponse, View>) AsyncRestClientProxy
.createResponseParser(parserFactory, injector, method, request);
assertEquals(parser.apply(HttpResponse.builder().statusCode(200).message("ok").payload("{ foo:\"bar\"}").build()).foo, "bar");
@ -1192,7 +1187,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
assertResponseParserClassEquals(method, request, ParseJson.class);
// now test that it works!
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) RestAnnotationProcessor
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) AsyncRestClientProxy
.createResponseParser(parserFactory, injector, method, request);
assertEquals(parser.apply(HttpResponse.builder().statusCode(200).message("ok").payload("{ foo:\"bar\"}").build()),
@ -1208,7 +1203,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
assertResponseParserClassEquals(method, request, ParseJson.class);
// now test that it works!
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) RestAnnotationProcessor
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) AsyncRestClientProxy
.createResponseParser(parserFactory, injector, method, request);
assertEquals(parser.apply(HttpResponse.builder().statusCode(200).message("ok").payload("{ foo:\"bar\"}").build()),
@ -1224,7 +1219,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
assertResponseParserClassEquals(method, request, ParseJson.class);
// now test that it works!
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) RestAnnotationProcessor
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) AsyncRestClientProxy
.createResponseParser(parserFactory, injector, method, request);
assertEquals(parser.apply(HttpResponse.builder().statusCode(200).message("ok").payload("{ foo:\"bar\"}").build()),
@ -1240,7 +1235,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class);
// now test that it works!
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) RestAnnotationProcessor
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) AsyncRestClientProxy
.createResponseParser(parserFactory, injector, method, request);
assertEquals(parser.apply(HttpResponse.builder().statusCode(200).message("ok").payload("{ foo:\"bar\"}").build()), "bar");
@ -1255,7 +1250,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
assertResponseParserClassEquals(method, request, ParseFirstJsonValueNamed.class);
// now test that it works!
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) RestAnnotationProcessor
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) AsyncRestClientProxy
.createResponseParser(parserFactory, injector, method, request);
assertEquals(parser.apply(HttpResponse.builder().statusCode(200).message("ok").payload("{ foo:\"bar\"}").build()), "bar");
@ -1276,7 +1271,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class);
// now test that it works!
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) RestAnnotationProcessor
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) AsyncRestClientProxy
.createResponseParser(parserFactory, injector, method, request);
assertEquals(parser.apply(HttpResponse.builder().statusCode(200).message("ok").payload("{ foo:\"bar\"}").build()), "bar");
@ -1291,7 +1286,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class);
// now test that it works!
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) RestAnnotationProcessor
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) AsyncRestClientProxy
.createResponseParser(parserFactory, injector, method, request);
assertEquals(parser.apply(HttpResponse.builder().statusCode(200).message("ok").payload("{\"runit\":[\"0.7.0\",\"0.7.1\"]}").build()),
@ -1306,7 +1301,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
assertResponseParserClassEquals(method, request, UnwrapOnlyJsonValue.class);
// now test that it works!
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) RestAnnotationProcessor
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) AsyncRestClientProxy
.createResponseParser(parserFactory, injector, method, request);
assertEquals(parser.apply(HttpResponse.builder().statusCode(200).message("ok").payload("{\"runit\":[\"0.7.0\",\"0.7.1\"]}").build()),
@ -1321,7 +1316,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
assertResponseParserClassEquals(method, request, ParseFirstJsonValueNamed.class);
// now test that it works!
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) RestAnnotationProcessor
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) AsyncRestClientProxy
.createResponseParser(parserFactory, injector, method, request);
assertEquals(parser.apply(HttpResponse.builder().statusCode(200).message("ok")
@ -1333,7 +1328,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
Method method = TestPut.class.getMethod("selectLongAddOne");
HttpRequest request = factory(TestPut.class).createRequest(method);
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) RestAnnotationProcessor
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) AsyncRestClientProxy
.createResponseParser(parserFactory, injector, method, request);
assertEquals(parser.apply(HttpResponse.builder().statusCode(200).message("ok")
@ -1476,13 +1471,6 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
@Path("/")
public void oneFormParamExtractor(@FormParam("one") @ParamParser(FirstCharacter.class) String one) {
}
@GET
@Path("/{path}")
@PathParam("path")
@ParamParser(FirstCharacterFirstElement.class)
public void onePathParamExtractorMethod(String path) {
}
}
@Test
@ -1531,15 +1519,6 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
assertEquals(e.getMessage(), "param{one} for method TestPath.oneFormParamExtractor");
}
}
@Test
public void testParamExtractorMethod() throws SecurityException, NoSuchMethodException {
Method method = TestPath.class.getMethod("onePathParamExtractorMethod", String.class);
HttpRequest request = factory(TestPath.class).createRequest(method, new Object[] { "localhost" });
assertEquals(request.getEndpoint().getPath(), "/l");
assertEquals(request.getMethod(), HttpMethod.GET);
assertEquals(request.getHeaders().size(), 0);
}
static class FirstCharacter implements Function<Object, String> {
public String apply(Object from) {
@ -1729,25 +1708,19 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
public interface TestTransformers {
@GET
int noTransformer();
ListenableFuture<Integer> noTransformer();
@GET
@ResponseParser(ReturnStringIf2xx.class)
void oneTransformer();
ListenableFuture<Void> oneTransformer();
@GET
@ResponseParser(ReturnStringIf200Context.class)
void oneTransformerWithContext();
@GET
InputStream inputStream();
ListenableFuture<Void> oneTransformerWithContext();
@GET
ListenableFuture<InputStream> futureInputStream();
@GET
URI uri();
@GET
ListenableFuture<URI> futureUri();
@ -1874,12 +1847,6 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
assertPayloadEquals(request, "whoops", "application/unknown", true);
}
public void testInputStream() throws SecurityException, NoSuchMethodException {
Method method = TestTransformers.class.getMethod("inputStream");
Class<? extends Function<HttpResponse, ?>> transformer = unwrap(factory(TestTransformers.class), method);
assertEquals(transformer, ReturnInputStream.class);
}
public void testInputStreamListenableFuture() throws SecurityException, NoSuchMethodException {
Method method = TestTransformers.class.getMethod("futureInputStream");
Class<? extends Function<HttpResponse, ?>> transformer = unwrap(factory(TestTransformers.class), method);
@ -1889,16 +1856,10 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
@SuppressWarnings("unchecked")
public static Class<? extends Function<HttpResponse, ?>> unwrap(RestAnnotationProcessor processor,
Method method) {
return (Class<? extends Function<HttpResponse, ?>>) RestAnnotationProcessor.getParserOrThrowException(method)
return (Class<? extends Function<HttpResponse, ?>>) AsyncRestClientProxy.getParserOrThrowException(method)
.getTypeLiteral().getRawType();
}
public void testURI() throws SecurityException, NoSuchMethodException {
Method method = TestTransformers.class.getMethod("uri");
Class<? extends Function<HttpResponse, ?>> transformer = unwrap(factory(TestTransformers.class), method);
assertEquals(transformer, ParseURIFromListOrLocationHeaderIf20x.class);
}
public void testURIListenableFuture() throws SecurityException, NoSuchMethodException {
Method method = TestTransformers.class.getMethod("futureUri");
Class<? extends Function<HttpResponse, ?>> transformer = unwrap(factory(TestTransformers.class), method);
@ -1921,7 +1882,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
@Test(expectedExceptions = { RuntimeException.class })
public void testNoTransformer() throws SecurityException, NoSuchMethodException {
Method method = TestTransformers.class.getMethod("noTransformer");
factory(TestTransformers.class).getParserOrThrowException(method);
AsyncRestClientProxy.getParserOrThrowException(method);
}
public void oneTransformerWithContext() throws SecurityException, NoSuchMethodException {
@ -1930,7 +1891,7 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
GeneratedHttpRequest request = GeneratedHttpRequest.builder()
.method("GET").endpoint("http://localhost").declaring(TestTransformers.class)
.javaMethod(method).args(new Object[] {}).build();
Function<HttpResponse, ?> transformer = processor.createResponseParser(method, request);
Function<HttpResponse, ?> transformer = AsyncRestClientProxy.createResponseParser(parserFactory, injector, method, request);
assertEquals(transformer.getClass(), ReturnStringIf200Context.class);
assertEquals(((ReturnStringIf200Context) transformer).request, request);
}
@ -2272,13 +2233,11 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
}
@SuppressWarnings("static-access")
@Test
@Test(expectedExceptions = IllegalStateException.class)
public void testTwoDifferentEndpointParams() throws SecurityException, NoSuchMethodException, ExecutionException {
Method method = TestEndpointParams.class.getMethod("twoEndpointParams", String.class, String.class);
URI uri = factory(TestEndpointParams.class).getEndpointInParametersOrNull(method,
factory(TestEndpointParams.class).getEndpointInParametersOrNull(method,
new Object[] { "robot", "egg" }, injector);
assertEquals(uri, URI.create("robot/egg"));
}
public interface TestPayload {

View File

@ -17,9 +17,9 @@
* under the License.
*/
package org.jclouds.http.apachehc;
import static com.google.common.hash.Hashing.md5;
import static com.google.common.io.BaseEncoding.base64;
import static org.jclouds.http.HttpUtils.filterOutContentHeaders;
import static org.jclouds.io.ByteSources.asByteSource;
import java.io.IOException;
@ -45,7 +45,6 @@ import org.jclouds.http.internal.HttpWire;
import org.jclouds.io.ContentMetadataCodec;
import org.jclouds.io.Payload;
import org.jclouds.io.Payloads;
import org.jclouds.rest.internal.RestAnnotationProcessor;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Multimap;
@ -108,7 +107,7 @@ public class ApacheHCHttpCommandExecutorService extends BaseHttpCommandExecutorS
return HttpResponse.builder().statusCode(apacheResponse.getStatusLine().getStatusCode())
.message(apacheResponse.getStatusLine().getReasonPhrase())
.payload(payload)
.headers(RestAnnotationProcessor.filterOutContentHeaders(headers)).build();
.headers(filterOutContentHeaders(headers)).build();
}
private org.apache.http.HttpResponse executeRequest(HttpUriRequest nativeRequest) throws IOException,

View File

@ -17,6 +17,7 @@
* under the License.
*/
package org.jclouds.gae;
import static org.jclouds.http.HttpUtils.filterOutContentHeaders;
import javax.inject.Singleton;
@ -24,7 +25,6 @@ import org.jclouds.http.HttpResponse;
import org.jclouds.io.ContentMetadataCodec;
import org.jclouds.io.Payload;
import org.jclouds.io.Payloads;
import org.jclouds.rest.internal.RestAnnotationProcessor;
import com.google.appengine.api.urlfetch.HTTPHeader;
import com.google.appengine.api.urlfetch.HTTPResponse;
@ -67,6 +67,6 @@ public class ConvertToJcloudsResponse implements Function<HTTPResponse, HttpResp
.statusCode(gaeResponse.getResponseCode())
.message(message)
.payload(payload)
.headers(RestAnnotationProcessor.filterOutContentHeaders(headers)).build();
.headers(filterOutContentHeaders(headers)).build();
}
}

View File

@ -19,11 +19,9 @@
package org.jclouds.googlecompute.functions.internal;
import com.google.common.annotations.Beta;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import static com.google.common.base.Predicates.instanceOf;
import static com.google.common.collect.Iterables.tryFind;
import org.jclouds.collect.IterableWithMarker;
import org.jclouds.collect.PagedIterable;
import org.jclouds.collect.PagedIterables;
@ -33,14 +31,16 @@ import org.jclouds.http.HttpRequest;
import org.jclouds.rest.InvocationContext;
import org.jclouds.rest.internal.GeneratedHttpRequest;
import java.util.Arrays;
import com.google.common.annotations.Beta;
import com.google.common.base.Function;
import com.google.common.base.Optional;
/**
* @author Adrian Cole
*/
@Beta
public abstract class BaseToPagedIterable<T, I extends BaseToPagedIterable<T, I>> implements
Function<ListPage<T>, PagedIterable<T>>, InvocationContext<I> {
Function<ListPage<T>, PagedIterable<T>>, InvocationContext<I> {
private GeneratedHttpRequest request;
@ -49,22 +49,21 @@ public abstract class BaseToPagedIterable<T, I extends BaseToPagedIterable<T, I>
if (input.nextMarker() == null)
return PagedIterables.of(input);
Optional<Object> project = Iterables.tryFind(Arrays.asList(request.getCaller().get().getArgs()),
Predicates.instanceOf(String.class));
Optional<Object> project = tryFind(request.getCaller().get().getArgs(), instanceOf(String.class));
Optional<Object> listOptions = Iterables.tryFind(request.getArgs(),
Predicates.instanceOf(ListOptions.class));
Optional<Object> listOptions = tryFind(request.getArgs(), instanceOf(ListOptions.class));
assert project.isPresent() : String.format("programming error, method %s should have a string param for the " +
"project", request.getCaller().get().getMethod());
assert project.isPresent() : String.format("programming error, method %s should have a string param for the "
+ "project", request.getCaller().get().getInvoker());
return PagedIterables.advance(input, fetchNextPage(project.get().toString(), (String) input.nextMarker().orNull(),
(ListOptions) listOptions.orNull()));
return PagedIterables.advance(
input,
fetchNextPage(project.get().toString(), (String) input.nextMarker().orNull(),
(ListOptions) listOptions.orNull()));
}
protected abstract Function<Object, IterableWithMarker<T>> fetchNextPage(String projectName, String marker,
ListOptions listOptions);
ListOptions listOptions);
@SuppressWarnings("unchecked")
@Override