mirror of https://github.com/apache/jclouds.git
Merge pull request #1059 from jclouds/delegate-with-paths
Delegate with paths
This commit is contained in:
commit
b4fd69287f
|
@ -64,11 +64,11 @@ public class CreateClientForCaller extends CacheLoader<ClassMethodArgs, Object>
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object load(ClassMethodArgs from) throws ExecutionException {
|
public Object load(ClassMethodArgs from) {
|
||||||
Class<?> syncClass = Optionals2.returnTypeOrTypeOfOptional(from.getMethod());
|
Class<?> syncClass = Optionals2.returnTypeOrTypeOfOptional(from.getMethod());
|
||||||
Class<?> asyncClass = sync2Async.get(syncClass);
|
Class<?> asyncClass = sync2Async.get(syncClass);
|
||||||
checkState(asyncClass != null, "configuration error, sync class " + syncClass + " not mapped to an async class");
|
checkState(asyncClass != null, "configuration error, sync class " + syncClass + " not mapped to an async class");
|
||||||
Object asyncClient = asyncMap.get(from);
|
Object asyncClient = asyncMap.getUnchecked(from);
|
||||||
checkState(asyncClient != null, "configuration error, sync client for " + from + " not found");
|
checkState(asyncClient != null, "configuration error, sync client for " + from + " not found");
|
||||||
Function<ClassMethodArgsAndReturnVal, Optional<Object>> optionalConverter = injector.getInstance(Key.get(new TypeLiteral<Function<ClassMethodArgsAndReturnVal, Optional<Object>>>() {
|
Function<ClassMethodArgsAndReturnVal, Optional<Object>> optionalConverter = injector.getInstance(Key.get(new TypeLiteral<Function<ClassMethodArgsAndReturnVal, Optional<Object>>>() {
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -50,7 +50,6 @@ import java.util.Map.Entry;
|
||||||
import java.util.NoSuchElementException;
|
import java.util.NoSuchElementException;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.SortedSet;
|
import java.util.SortedSet;
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import javax.inject.Provider;
|
import javax.inject.Provider;
|
||||||
|
@ -144,7 +143,6 @@ import com.google.common.base.Preconditions;
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
import com.google.common.base.Predicates;
|
import com.google.common.base.Predicates;
|
||||||
import com.google.common.base.Supplier;
|
import com.google.common.base.Supplier;
|
||||||
import com.google.common.base.Throwables;
|
|
||||||
import com.google.common.cache.Cache;
|
import com.google.common.cache.Cache;
|
||||||
import com.google.common.cache.CacheBuilder;
|
import com.google.common.cache.CacheBuilder;
|
||||||
import com.google.common.cache.CacheLoader;
|
import com.google.common.cache.CacheLoader;
|
||||||
|
@ -323,7 +321,7 @@ public class RestAnnotationProcessor<T> {
|
||||||
@Inject
|
@Inject
|
||||||
public RestAnnotationProcessor(Injector injector, LoadingCache<Class<?>, Boolean> seedAnnotationCache, Cache<MethodKey, Method> delegationMap,
|
public RestAnnotationProcessor(Injector injector, LoadingCache<Class<?>, Boolean> seedAnnotationCache, Cache<MethodKey, Method> delegationMap,
|
||||||
@ApiVersion String apiVersion, @BuildVersion String buildVersion, ParseSax.Factory parserFactory,
|
@ApiVersion String apiVersion, @BuildVersion String buildVersion, ParseSax.Factory parserFactory,
|
||||||
HttpUtils utils, ContentMetadataCodec contentMetadataCodec, TypeLiteral<T> typeLiteral) throws ExecutionException {
|
HttpUtils utils, ContentMetadataCodec contentMetadataCodec, TypeLiteral<T> typeLiteral) {
|
||||||
this.declaring = (Class<T>) typeLiteral.getRawType();
|
this.declaring = (Class<T>) typeLiteral.getRawType();
|
||||||
this.injector = injector;
|
this.injector = injector;
|
||||||
this.parserFactory = parserFactory;
|
this.parserFactory = parserFactory;
|
||||||
|
@ -331,7 +329,7 @@ public class RestAnnotationProcessor<T> {
|
||||||
this.contentMetadataCodec = contentMetadataCodec;
|
this.contentMetadataCodec = contentMetadataCodec;
|
||||||
this.uriBuilderProvider = injector.getProvider(UriBuilder.class);
|
this.uriBuilderProvider = injector.getProvider(UriBuilder.class);
|
||||||
this.seedAnnotationCache = seedAnnotationCache;
|
this.seedAnnotationCache = seedAnnotationCache;
|
||||||
seedAnnotationCache.get(declaring);
|
seedAnnotationCache.getUnchecked(declaring);
|
||||||
this.delegationMap = delegationMap;
|
this.delegationMap = delegationMap;
|
||||||
if (declaring.isAnnotationPresent(SkipEncoding.class)) {
|
if (declaring.isAnnotationPresent(SkipEncoding.class)) {
|
||||||
skips = declaring.getAnnotation(SkipEncoding.class).value();
|
skips = declaring.getAnnotation(SkipEncoding.class).value();
|
||||||
|
@ -382,59 +380,31 @@ public class RestAnnotationProcessor<T> {
|
||||||
final Injector injector;
|
final Injector injector;
|
||||||
|
|
||||||
private ClassMethodArgs caller;
|
private ClassMethodArgs caller;
|
||||||
private URI callerEndpoint;
|
|
||||||
|
|
||||||
public void setCaller(ClassMethodArgs caller) {
|
public void setCaller(ClassMethodArgs caller) {
|
||||||
try {
|
seedAnnotationCache.getUnchecked(caller.getMethod().getDeclaringClass());
|
||||||
seedAnnotationCache.get(caller.getMethod().getDeclaringClass());
|
|
||||||
} catch (ExecutionException e) {
|
|
||||||
Throwables.propagate(e);
|
|
||||||
}
|
|
||||||
this.caller = caller;
|
this.caller = caller;
|
||||||
try {
|
|
||||||
UriBuilder builder = uriBuilderProvider.get().uri(getEndpointFor(caller.getMethod(), caller.getArgs(), injector));
|
|
||||||
Multimap<String, String> tokenValues = addPathAndGetTokens(caller.getMethod().getDeclaringClass(), caller.getMethod(), caller.getArgs(), builder);
|
|
||||||
callerEndpoint = builder.buildFromEncodedMap(Maps2.convertUnsafe(tokenValues));
|
|
||||||
} catch (IllegalStateException e) {
|
|
||||||
} catch (ExecutionException e) {
|
|
||||||
Throwables.propagate(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public GeneratedHttpRequest createRequest(Method method, Object... args) {
|
public GeneratedHttpRequest createRequest(Method method, Object... args) {
|
||||||
try {
|
|
||||||
inputParamValidator.validateMethodParametersOrThrow(method, args);
|
inputParamValidator.validateMethodParametersOrThrow(method, args);
|
||||||
ClassMethodArgs cma = logger.isTraceEnabled() ? new ClassMethodArgs(method.getDeclaringClass(), method, args)
|
|
||||||
: null;
|
|
||||||
|
|
||||||
URI endpoint = callerEndpoint;
|
|
||||||
try {
|
|
||||||
if (endpoint == null) {
|
|
||||||
endpoint = getEndpointFor(method, args, injector);
|
|
||||||
logger.trace("using endpoint %s for %s", endpoint, cma);
|
|
||||||
} else {
|
|
||||||
logger.trace("using endpoint %s from caller %s for %s", caller, endpoint, cma);
|
|
||||||
}
|
|
||||||
} catch (IllegalStateException e) {
|
|
||||||
logger.trace("looking up default endpoint for %s", cma);
|
|
||||||
endpoint = injector.getInstance(Key.get(uriSupplierLiteral, org.jclouds.location.Provider.class)).get();
|
|
||||||
logger.trace("using default endpoint %s for %s", endpoint, cma);
|
|
||||||
}
|
|
||||||
GeneratedHttpRequest.Builder<?> requestBuilder;
|
|
||||||
HttpRequest r = RestAnnotationProcessor.findHttpRequestInArgs(args);
|
|
||||||
if (r != null) {
|
|
||||||
requestBuilder = GeneratedHttpRequest.builder().fromHttpRequest(r);
|
|
||||||
endpoint = r.getEndpoint();
|
|
||||||
} else {
|
|
||||||
requestBuilder = GeneratedHttpRequest.builder();
|
|
||||||
requestBuilder.method(getHttpMethodOrConstantOrThrowException(method));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (endpoint == null) {
|
Optional<URI> endpoint = findEndpoint(method, args);
|
||||||
|
|
||||||
|
if (!endpoint.isPresent()) {
|
||||||
throw new NoSuchElementException(String.format("no endpoint found for %s",
|
throw new NoSuchElementException(String.format("no endpoint found for %s",
|
||||||
new ClassMethodArgs(method.getDeclaringClass(), method, args)));
|
new ClassMethodArgs(method.getDeclaringClass(), method, args)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GeneratedHttpRequest.Builder<?> requestBuilder = GeneratedHttpRequest.builder();
|
||||||
|
HttpRequest r = RestAnnotationProcessor.findHttpRequestInArgs(args);
|
||||||
|
if (r != null) {
|
||||||
|
requestBuilder.fromHttpRequest(r);
|
||||||
|
} else {
|
||||||
|
requestBuilder.method(getHttpMethodOrConstantOrThrowException(method));
|
||||||
|
}
|
||||||
|
|
||||||
requestBuilder.declaring(declaring)
|
requestBuilder.declaring(declaring)
|
||||||
.javaMethod(method)
|
.javaMethod(method)
|
||||||
.args(args)
|
.args(args)
|
||||||
|
@ -442,13 +412,19 @@ public class RestAnnotationProcessor<T> {
|
||||||
.skips(skips)
|
.skips(skips)
|
||||||
.filters(getFiltersIfAnnotated(method));
|
.filters(getFiltersIfAnnotated(method));
|
||||||
|
|
||||||
UriBuilder builder = uriBuilderProvider.get().uri(endpoint);
|
UriBuilder builder = uriBuilderProvider.get().uri(endpoint.get());
|
||||||
|
|
||||||
Multimap<String, String> tokenValues = LinkedHashMultimap.create();
|
Multimap<String, String> tokenValues = LinkedHashMultimap.create();
|
||||||
|
|
||||||
tokenValues.put(Constants.PROPERTY_API_VERSION, apiVersion);
|
tokenValues.put(Constants.PROPERTY_API_VERSION, apiVersion);
|
||||||
tokenValues.put(Constants.PROPERTY_BUILD_VERSION, buildVersion);
|
tokenValues.put(Constants.PROPERTY_BUILD_VERSION, buildVersion);
|
||||||
|
|
||||||
|
// make sure any path from the caller is a prefix
|
||||||
|
if (caller != null) {
|
||||||
|
tokenValues.putAll(addPathAndGetTokens(caller.getMethod().getDeclaringClass(), caller.getMethod(),
|
||||||
|
caller.getArgs(), builder));
|
||||||
|
}
|
||||||
|
|
||||||
tokenValues.putAll(addPathAndGetTokens(declaring, method, args, builder));
|
tokenValues.putAll(addPathAndGetTokens(declaring, method, args, builder));
|
||||||
|
|
||||||
Multimap<String, String> formParams = addFormParams(tokenValues.entries(), method, args);
|
Multimap<String, String> formParams = addFormParams(tokenValues.entries(), method, args);
|
||||||
|
@ -459,9 +435,9 @@ public class RestAnnotationProcessor<T> {
|
||||||
headers.putAll(r.getHeaders());
|
headers.putAll(r.getHeaders());
|
||||||
|
|
||||||
if (shouldAddHostHeader(method)) {
|
if (shouldAddHostHeader(method)) {
|
||||||
StringBuilder hostHeader = new StringBuilder(endpoint.getHost());
|
StringBuilder hostHeader = new StringBuilder(endpoint.get().getHost());
|
||||||
if (endpoint.getPort() != -1)
|
if (endpoint.get().getPort() != -1)
|
||||||
hostHeader.append(":").append(endpoint.getPort());
|
hostHeader.append(":").append(endpoint.get().getPort());
|
||||||
headers.put(HOST, hostHeader.toString());
|
headers.put(HOST, hostHeader.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -546,9 +522,39 @@ public class RestAnnotationProcessor<T> {
|
||||||
}
|
}
|
||||||
utils.checkRequestHasRequiredProperties(request);
|
utils.checkRequestHasRequiredProperties(request);
|
||||||
return request;
|
return request;
|
||||||
} catch (ExecutionException e) {
|
|
||||||
throw Throwables.propagate(e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Optional<URI> findEndpoint(Method method, Object... args) {
|
||||||
|
ClassMethodArgs cma = logger.isTraceEnabled() ? new ClassMethodArgs(method.getDeclaringClass(), method, args)
|
||||||
|
: null;
|
||||||
|
Optional<URI> endpoint = Optional.absent();
|
||||||
|
|
||||||
|
HttpRequest r = RestAnnotationProcessor.findHttpRequestInArgs(args);
|
||||||
|
|
||||||
|
if (r != null) {
|
||||||
|
endpoint = Optional.fromNullable(r.getEndpoint());
|
||||||
|
if (endpoint.isPresent())
|
||||||
|
logger.trace("using endpoint %s from args for %s", endpoint, cma);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!endpoint.isPresent() && caller != null) {
|
||||||
|
endpoint = getEndpointFor(caller.getMethod(), caller.getArgs());
|
||||||
|
if (endpoint.isPresent())
|
||||||
|
logger.trace("using endpoint %s from caller %s for %s", endpoint, caller, cma);
|
||||||
|
}
|
||||||
|
if (!endpoint.isPresent()) {
|
||||||
|
endpoint = getEndpointFor(method, args);
|
||||||
|
if (endpoint.isPresent())
|
||||||
|
logger.trace("using endpoint %s for %s", endpoint, cma);
|
||||||
|
}
|
||||||
|
if (!endpoint.isPresent()) {
|
||||||
|
logger.trace("looking up default endpoint for %s", cma);
|
||||||
|
endpoint = Optional.fromNullable(injector.getInstance(
|
||||||
|
Key.get(uriSupplierLiteral, org.jclouds.location.Provider.class)).get());
|
||||||
|
if (endpoint.isPresent())
|
||||||
|
logger.trace("using default endpoint %s for %s", endpoint, cma);
|
||||||
|
}
|
||||||
|
return endpoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Multimap<String, String> filterOutContentHeaders(Multimap<String, String> headers) {
|
public static Multimap<String, String> filterOutContentHeaders(Multimap<String, String> headers) {
|
||||||
|
@ -562,7 +568,7 @@ public class RestAnnotationProcessor<T> {
|
||||||
|
|
||||||
public static final String BOUNDARY = "--JCLOUDS--";
|
public static final String BOUNDARY = "--JCLOUDS--";
|
||||||
|
|
||||||
private Multimap<String, String> addPathAndGetTokens(Class<?> clazz, Method method, Object[] args, UriBuilder builder) throws ExecutionException {
|
private Multimap<String, String> addPathAndGetTokens(Class<?> clazz, Method method, Object[] args, UriBuilder builder) {
|
||||||
if (clazz.isAnnotationPresent(Path.class))
|
if (clazz.isAnnotationPresent(Path.class))
|
||||||
builder.path(clazz);
|
builder.path(clazz);
|
||||||
if (method.isAnnotationPresent(Path.class))
|
if (method.isAnnotationPresent(Path.class))
|
||||||
|
@ -582,7 +588,7 @@ public class RestAnnotationProcessor<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private Multimap<String, String> addMatrixParams(Collection<Entry<String, String>> tokenValues, Method method,
|
private Multimap<String, String> addMatrixParams(Collection<Entry<String, String>> tokenValues, Method method,
|
||||||
Object... args) throws ExecutionException {
|
Object... args) {
|
||||||
Multimap<String, String> matrixMap = LinkedListMultimap.create();
|
Multimap<String, String> matrixMap = LinkedListMultimap.create();
|
||||||
if (declaring.isAnnotationPresent(MatrixParams.class)) {
|
if (declaring.isAnnotationPresent(MatrixParams.class)) {
|
||||||
MatrixParams matrix = declaring.getAnnotation(MatrixParams.class);
|
MatrixParams matrix = declaring.getAnnotation(MatrixParams.class);
|
||||||
|
@ -601,7 +607,7 @@ public class RestAnnotationProcessor<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private Multimap<String, String> addFormParams(Collection<Entry<String, String>> tokenValues, Method method,
|
private Multimap<String, String> addFormParams(Collection<Entry<String, String>> tokenValues, Method method,
|
||||||
Object... args) throws ExecutionException {
|
Object... args) {
|
||||||
Multimap<String, String> formMap = LinkedListMultimap.create();
|
Multimap<String, String> formMap = LinkedListMultimap.create();
|
||||||
if (declaring.isAnnotationPresent(FormParams.class)) {
|
if (declaring.isAnnotationPresent(FormParams.class)) {
|
||||||
FormParams form = declaring.getAnnotation(FormParams.class);
|
FormParams form = declaring.getAnnotation(FormParams.class);
|
||||||
|
@ -620,7 +626,7 @@ public class RestAnnotationProcessor<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private Multimap<String, String> addQueryParams(Collection<Entry<String, String>> tokenValues, Method method,
|
private Multimap<String, String> addQueryParams(Collection<Entry<String, String>> tokenValues, Method method,
|
||||||
Object... args) throws ExecutionException {
|
Object... args) {
|
||||||
Multimap<String, String> queryMap = LinkedListMultimap.create();
|
Multimap<String, String> queryMap = LinkedListMultimap.create();
|
||||||
if (declaring.isAnnotationPresent(QueryParams.class)) {
|
if (declaring.isAnnotationPresent(QueryParams.class)) {
|
||||||
QueryParams query = declaring.getAnnotation(QueryParams.class);
|
QueryParams query = declaring.getAnnotation(QueryParams.class);
|
||||||
|
@ -711,7 +717,7 @@ public class RestAnnotationProcessor<T> {
|
||||||
//TODO: change to LoadingCache<ClassMethodArgs, Optional<URI>> and move this logic to the CacheLoader.
|
//TODO: change to LoadingCache<ClassMethodArgs, Optional<URI>> and move this logic to the CacheLoader.
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public static URI getEndpointInParametersOrNull(Method method, final Object[] args, Injector injector)
|
public static URI getEndpointInParametersOrNull(Method method, final Object[] args, Injector injector)
|
||||||
throws ExecutionException {
|
{
|
||||||
Map<Integer, Set<Annotation>> map = indexWithAtLeastOneAnnotation(method,
|
Map<Integer, Set<Annotation>> map = indexWithAtLeastOneAnnotation(method,
|
||||||
methodToIndexOfParamToEndpointParamAnnotations);
|
methodToIndexOfParamToEndpointParamAnnotations);
|
||||||
if (map.size() >= 1 && args.length > 0) {
|
if (map.size() >= 1 && args.length > 0) {
|
||||||
|
@ -756,7 +762,7 @@ public class RestAnnotationProcessor<T> {
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: change to LoadingCache<ClassMethodArgs, URI> and move this logic to the CacheLoader.
|
// TODO: change to LoadingCache<ClassMethodArgs, URI> and move this logic to the CacheLoader.
|
||||||
public static URI getEndpointFor(Method method, Object[] args, Injector injector) throws ExecutionException {
|
private Optional<URI> getEndpointFor(Method method, Object[] args) {
|
||||||
URI endpoint = getEndpointInParametersOrNull(method, args, injector);
|
URI endpoint = getEndpointInParametersOrNull(method, args, injector);
|
||||||
if (endpoint == null) {
|
if (endpoint == null) {
|
||||||
Endpoint annotation;
|
Endpoint annotation;
|
||||||
|
@ -765,16 +771,18 @@ public class RestAnnotationProcessor<T> {
|
||||||
} else if (method.getDeclaringClass().isAnnotationPresent(Endpoint.class)) {
|
} else if (method.getDeclaringClass().isAnnotationPresent(Endpoint.class)) {
|
||||||
annotation = method.getDeclaringClass().getAnnotation(Endpoint.class);
|
annotation = method.getDeclaringClass().getAnnotation(Endpoint.class);
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalStateException("no annotations on class or method: " + method);
|
logger.trace("no annotations on class or method: %s", method);
|
||||||
|
return Optional.absent();
|
||||||
}
|
}
|
||||||
endpoint = injector.getInstance(Key.get(uriSupplierLiteral, annotation.value())).get();
|
endpoint = injector.getInstance(Key.get(uriSupplierLiteral, annotation.value())).get();
|
||||||
}
|
}
|
||||||
URI providerEndpoint = injector.getInstance(Key.get(uriSupplierLiteral, org.jclouds.location.Provider.class))
|
URI providerEndpoint = injector.getInstance(Key.get(uriSupplierLiteral, org.jclouds.location.Provider.class))
|
||||||
.get();
|
.get();
|
||||||
return addHostIfMissing(endpoint, providerEndpoint);
|
return Optional.fromNullable(addHostIfMissing(endpoint, providerEndpoint));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static URI addHostIfMissing(URI original, URI withHost) {
|
@VisibleForTesting
|
||||||
|
static URI addHostIfMissing(URI original, URI withHost) {
|
||||||
checkNotNull(withHost, "URI withHost cannot be null");
|
checkNotNull(withHost, "URI withHost cannot be null");
|
||||||
checkArgument(withHost.getHost() != null, "URI withHost must have host:" + withHost);
|
checkArgument(withHost.getHost() != null, "URI withHost must have host:" + withHost);
|
||||||
|
|
||||||
|
@ -950,13 +958,13 @@ public class RestAnnotationProcessor<T> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public GeneratedHttpRequest decorateRequest(GeneratedHttpRequest request) throws NegativeArraySizeException,
|
public GeneratedHttpRequest decorateRequest(GeneratedHttpRequest request) throws NegativeArraySizeException {
|
||||||
ExecutionException {
|
Iterable<Entry<Integer, Set<Annotation>>> binderOrWrapWith = concat(
|
||||||
OUTER: for (Entry<Integer, Set<Annotation>> entry : concat(//
|
filterValues(methodToIndexOfParamToBinderParamAnnotation.getUnchecked(request.getJavaMethod()).asMap(),
|
||||||
filterValues(methodToIndexOfParamToBinderParamAnnotation.get(request.getJavaMethod()).asMap(), notEmpty)
|
notEmpty).entrySet(),
|
||||||
.entrySet(), //
|
filterValues(methodToIndexOfParamToWrapWithAnnotation.getUnchecked(request.getJavaMethod()).asMap(),
|
||||||
filterValues(methodToIndexOfParamToWrapWithAnnotation.get(request.getJavaMethod()).asMap(), notEmpty)
|
notEmpty).entrySet());
|
||||||
.entrySet())) {
|
OUTER: for (Entry<Integer, Set<Annotation>> entry : binderOrWrapWith) {
|
||||||
boolean shouldBreak = false;
|
boolean shouldBreak = false;
|
||||||
Annotation annotation = Iterables.get(entry.getValue(), 0);
|
Annotation annotation = Iterables.get(entry.getValue(), 0);
|
||||||
Binder binder;
|
Binder binder;
|
||||||
|
@ -1018,7 +1026,7 @@ public class RestAnnotationProcessor<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Map<Integer, Set<Annotation>> indexWithOnlyOneAnnotation(Method method, String description,
|
public static Map<Integer, Set<Annotation>> indexWithOnlyOneAnnotation(Method method, String description,
|
||||||
LoadingCache<Method, LoadingCache<Integer, Set<Annotation>>> toRefine) throws ExecutionException {
|
LoadingCache<Method, LoadingCache<Integer, Set<Annotation>>> toRefine) {
|
||||||
Map<Integer, Set<Annotation>> indexToPayloadAnnotation = indexWithAtLeastOneAnnotation(method, toRefine);
|
Map<Integer, Set<Annotation>> indexToPayloadAnnotation = indexWithAtLeastOneAnnotation(method, toRefine);
|
||||||
if (indexToPayloadAnnotation.size() > 1) {
|
if (indexToPayloadAnnotation.size() > 1) {
|
||||||
throw new IllegalStateException(String.format(
|
throw new IllegalStateException(String.format(
|
||||||
|
@ -1029,8 +1037,8 @@ public class RestAnnotationProcessor<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Map<Integer, Set<Annotation>> indexWithAtLeastOneAnnotation(Method method,
|
private static Map<Integer, Set<Annotation>> indexWithAtLeastOneAnnotation(Method method,
|
||||||
LoadingCache<Method, LoadingCache<Integer, Set<Annotation>>> toRefine) throws ExecutionException {
|
LoadingCache<Method, LoadingCache<Integer, Set<Annotation>>> toRefine) {
|
||||||
Map<Integer, Set<Annotation>> indexToPayloadAnnotation = filterValues(toRefine.get(method).asMap(),
|
Map<Integer, Set<Annotation>> indexToPayloadAnnotation = filterValues(toRefine.getUnchecked(method).asMap(),
|
||||||
new Predicate<Set<Annotation>>() {
|
new Predicate<Set<Annotation>>() {
|
||||||
public boolean apply(Set<Annotation> input) {
|
public boolean apply(Set<Annotation> input) {
|
||||||
return input.size() == 1;
|
return input.size() == 1;
|
||||||
|
@ -1040,9 +1048,9 @@ public class RestAnnotationProcessor<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: change to LoadingCache<ClassMethodArgs, HttpRequestOptions and move this logic to the CacheLoader.
|
//TODO: change to LoadingCache<ClassMethodArgs, HttpRequestOptions and move this logic to the CacheLoader.
|
||||||
private Set<HttpRequestOptions> findOptionsIn(Method method, Object... args) throws ExecutionException {
|
private Set<HttpRequestOptions> findOptionsIn(Method method, Object... args) {
|
||||||
ImmutableSet.Builder<HttpRequestOptions> result = ImmutableSet.builder();
|
ImmutableSet.Builder<HttpRequestOptions> result = ImmutableSet.builder();
|
||||||
for (int index : methodToIndexesOfOptions.get(method)) {
|
for (int index : methodToIndexesOfOptions.getUnchecked(method)) {
|
||||||
if (args.length >= index + 1) {// accommodate varargs
|
if (args.length >= index + 1) {// accommodate varargs
|
||||||
if (args[index] instanceof Object[]) {
|
if (args[index] instanceof Object[]) {
|
||||||
for (Object option : (Object[]) args[index]) {
|
for (Object option : (Object[]) args[index]) {
|
||||||
|
@ -1063,10 +1071,11 @@ public class RestAnnotationProcessor<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Multimap<String, String> buildHeaders(Collection<Entry<String, String>> tokenValues, Method method,
|
public Multimap<String, String> buildHeaders(Collection<Entry<String, String>> tokenValues, Method method,
|
||||||
final Object... args) throws ExecutionException {
|
final Object... args) {
|
||||||
Multimap<String, String> headers = LinkedHashMultimap.create();
|
Multimap<String, String> headers = LinkedHashMultimap.create();
|
||||||
addHeaderIfAnnotationPresentOnMethod(headers, method, tokenValues);
|
addHeaderIfAnnotationPresentOnMethod(headers, method, tokenValues);
|
||||||
LoadingCache<Integer, Set<Annotation>> indexToHeaderParam = methodToIndexOfParamToHeaderParamAnnotations.get(method);
|
LoadingCache<Integer, Set<Annotation>> indexToHeaderParam = methodToIndexOfParamToHeaderParamAnnotations
|
||||||
|
.getUnchecked(method);
|
||||||
for (Entry<Integer, Set<Annotation>> entry : indexToHeaderParam.asMap().entrySet()) {
|
for (Entry<Integer, Set<Annotation>> entry : indexToHeaderParam.asMap().entrySet()) {
|
||||||
for (Annotation key : entry.getValue()) {
|
for (Annotation key : entry.getValue()) {
|
||||||
String value = args[entry.getKey()].toString();
|
String value = args[entry.getKey()].toString();
|
||||||
|
@ -1131,9 +1140,10 @@ public class RestAnnotationProcessor<T> {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
List<? extends Part> getParts(Method method, Object[] args, Iterable<Entry<String, String>> iterable) throws ExecutionException {
|
List<? extends Part> getParts(Method method, Object[] args, Iterable<Entry<String, String>> iterable) {
|
||||||
List<Part> parts = newLinkedList();
|
List<Part> parts = newLinkedList();
|
||||||
LoadingCache<Integer, Set<Annotation>> indexToPartParam = methodToIndexOfParamToPartParamAnnotations.get(method);
|
LoadingCache<Integer, Set<Annotation>> indexToPartParam = methodToIndexOfParamToPartParamAnnotations
|
||||||
|
.getUnchecked(method);
|
||||||
for (Entry<Integer, Set<Annotation>> entry : indexToPartParam.asMap().entrySet()) {
|
for (Entry<Integer, Set<Annotation>> entry : indexToPartParam.asMap().entrySet()) {
|
||||||
for (Annotation key : entry.getValue()) {
|
for (Annotation key : entry.getValue()) {
|
||||||
PartParam param = (PartParam) key;
|
PartParam param = (PartParam) key;
|
||||||
|
@ -1172,14 +1182,14 @@ public class RestAnnotationProcessor<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: change to LoadingCache<ClassMethodArgs, Multimap<String,String> and move this logic to the CacheLoader.
|
//TODO: change to LoadingCache<ClassMethodArgs, Multimap<String,String> and move this logic to the CacheLoader.
|
||||||
private Multimap<String, String> getPathParamKeyValues(Method method, Object... args) throws ExecutionException {
|
private Multimap<String, String> getPathParamKeyValues(Method method, Object... args) {
|
||||||
Multimap<String, String> pathParamValues = LinkedHashMultimap.create();
|
Multimap<String, String> pathParamValues = LinkedHashMultimap.create();
|
||||||
LoadingCache<Integer, Set<Annotation>> indexToPathParam = methodToIndexOfParamToPathParamAnnotations.get(method);
|
LoadingCache<Integer, Set<Annotation>> indexToPathParam = methodToIndexOfParamToPathParamAnnotations.getUnchecked(method);
|
||||||
|
|
||||||
LoadingCache<Integer, Set<Annotation>> indexToParamExtractor = methodToIndexOfParamToParamParserAnnotations.get(method);
|
LoadingCache<Integer, Set<Annotation>> indexToParamExtractor = methodToIndexOfParamToParamParserAnnotations.getUnchecked(method);
|
||||||
for (Entry<Integer, Set<Annotation>> entry : indexToPathParam.asMap().entrySet()) {
|
for (Entry<Integer, Set<Annotation>> entry : indexToPathParam.asMap().entrySet()) {
|
||||||
for (Annotation key : entry.getValue()) {
|
for (Annotation key : entry.getValue()) {
|
||||||
Set<Annotation> extractors = indexToParamExtractor.get(entry.getKey());
|
Set<Annotation> extractors = indexToParamExtractor.getUnchecked(entry.getKey());
|
||||||
String paramKey = ((PathParam) key).value();
|
String paramKey = ((PathParam) key).value();
|
||||||
Optional<?> paramValue = getParamValue(method, args, extractors, entry, paramKey);
|
Optional<?> paramValue = getParamValue(method, args, extractors, entry, paramKey);
|
||||||
if (paramValue.isPresent())
|
if (paramValue.isPresent())
|
||||||
|
@ -1240,14 +1250,16 @@ public class RestAnnotationProcessor<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: change to LoadingCache<ClassMethodArgs, Multimap<String,String> and move this logic to the CacheLoader.
|
//TODO: change to LoadingCache<ClassMethodArgs, Multimap<String,String> and move this logic to the CacheLoader.
|
||||||
private Multimap<String, String> getMatrixParamKeyValues(Method method, Object... args) throws ExecutionException {
|
private Multimap<String, String> getMatrixParamKeyValues(Method method, Object... args) {
|
||||||
Multimap<String, String> matrixParamValues = LinkedHashMultimap.create();
|
Multimap<String, String> matrixParamValues = LinkedHashMultimap.create();
|
||||||
LoadingCache<Integer, Set<Annotation>> indexToMatrixParam = methodToIndexOfParamToMatrixParamAnnotations.get(method);
|
LoadingCache<Integer, Set<Annotation>> indexToMatrixParam = methodToIndexOfParamToMatrixParamAnnotations
|
||||||
|
.getUnchecked(method);
|
||||||
|
|
||||||
LoadingCache<Integer, Set<Annotation>> indexToParamExtractor = methodToIndexOfParamToParamParserAnnotations.get(method);
|
LoadingCache<Integer, Set<Annotation>> indexToParamExtractor = methodToIndexOfParamToParamParserAnnotations
|
||||||
|
.getUnchecked(method);
|
||||||
for (Entry<Integer, Set<Annotation>> entry : indexToMatrixParam.asMap().entrySet()) {
|
for (Entry<Integer, Set<Annotation>> entry : indexToMatrixParam.asMap().entrySet()) {
|
||||||
for (Annotation key : entry.getValue()) {
|
for (Annotation key : entry.getValue()) {
|
||||||
Set<Annotation> extractors = indexToParamExtractor.get(entry.getKey());
|
Set<Annotation> extractors = indexToParamExtractor.getUnchecked(entry.getKey());
|
||||||
String paramKey = ((MatrixParam) key).value();
|
String paramKey = ((MatrixParam) key).value();
|
||||||
Optional<?> paramValue = getParamValue(method, args, extractors, entry, paramKey);
|
Optional<?> paramValue = getParamValue(method, args, extractors, entry, paramKey);
|
||||||
if (paramValue.isPresent())
|
if (paramValue.isPresent())
|
||||||
|
@ -1266,14 +1278,16 @@ public class RestAnnotationProcessor<T> {
|
||||||
|
|
||||||
//TODO: change to LoadingCache<ClassMethodArgs, Multimap<String,String> and move this logic to the CacheLoader.
|
//TODO: change to LoadingCache<ClassMethodArgs, Multimap<String,String> and move this logic to the CacheLoader.
|
||||||
//take care to manage size of this cache
|
//take care to manage size of this cache
|
||||||
private Multimap<String, String> getFormParamKeyValues(Method method, Object... args) throws ExecutionException {
|
private Multimap<String, String> getFormParamKeyValues(Method method, Object... args) {
|
||||||
Multimap<String, String> formParamValues = LinkedHashMultimap.create();
|
Multimap<String, String> formParamValues = LinkedHashMultimap.create();
|
||||||
LoadingCache<Integer, Set<Annotation>> indexToFormParam = methodToIndexOfParamToFormParamAnnotations.get(method);
|
LoadingCache<Integer, Set<Annotation>> indexToFormParam = methodToIndexOfParamToFormParamAnnotations
|
||||||
|
.getUnchecked(method);
|
||||||
|
|
||||||
LoadingCache<Integer, Set<Annotation>> indexToParamExtractor = methodToIndexOfParamToParamParserAnnotations.get(method);
|
LoadingCache<Integer, Set<Annotation>> indexToParamExtractor = methodToIndexOfParamToParamParserAnnotations
|
||||||
|
.getUnchecked(method);
|
||||||
for (Entry<Integer, Set<Annotation>> entry : indexToFormParam.asMap().entrySet()) {
|
for (Entry<Integer, Set<Annotation>> entry : indexToFormParam.asMap().entrySet()) {
|
||||||
for (Annotation key : entry.getValue()) {
|
for (Annotation key : entry.getValue()) {
|
||||||
Set<Annotation> extractors = indexToParamExtractor.get(entry.getKey());
|
Set<Annotation> extractors = indexToParamExtractor.getUnchecked(entry.getKey());
|
||||||
String paramKey = ((FormParam) key).value();
|
String paramKey = ((FormParam) key).value();
|
||||||
Optional<?> paramValue = getParamValue(method, args, extractors, entry, paramKey);
|
Optional<?> paramValue = getParamValue(method, args, extractors, entry, paramKey);
|
||||||
if (paramValue.isPresent())
|
if (paramValue.isPresent())
|
||||||
|
@ -1291,14 +1305,16 @@ public class RestAnnotationProcessor<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: change to LoadingCache<ClassMethodArgs, Multimap<String,String> and move this logic to the CacheLoader.
|
//TODO: change to LoadingCache<ClassMethodArgs, Multimap<String,String> and move this logic to the CacheLoader.
|
||||||
private Multimap<String, String> getQueryParamKeyValues(Method method, Object... args) throws ExecutionException {
|
private Multimap<String, String> getQueryParamKeyValues(Method method, Object... args) {
|
||||||
Multimap<String, String> queryParamValues = LinkedHashMultimap.create();
|
Multimap<String, String> queryParamValues = LinkedHashMultimap.create();
|
||||||
LoadingCache<Integer, Set<Annotation>> indexToQueryParam = methodToIndexOfParamToQueryParamAnnotations.get(method);
|
LoadingCache<Integer, Set<Annotation>> indexToQueryParam = methodToIndexOfParamToQueryParamAnnotations
|
||||||
|
.getUnchecked(method);
|
||||||
|
|
||||||
LoadingCache<Integer, Set<Annotation>> indexToParamExtractor = methodToIndexOfParamToParamParserAnnotations.get(method);
|
LoadingCache<Integer, Set<Annotation>> indexToParamExtractor = methodToIndexOfParamToParamParserAnnotations
|
||||||
|
.getUnchecked(method);
|
||||||
for (Entry<Integer, Set<Annotation>> entry : indexToQueryParam.asMap().entrySet()) {
|
for (Entry<Integer, Set<Annotation>> entry : indexToQueryParam.asMap().entrySet()) {
|
||||||
for (Annotation key : entry.getValue()) {
|
for (Annotation key : entry.getValue()) {
|
||||||
Set<Annotation> extractors = indexToParamExtractor.get(entry.getKey());
|
Set<Annotation> extractors = indexToParamExtractor.getUnchecked(entry.getKey());
|
||||||
String paramKey = ((QueryParam) key).value();
|
String paramKey = ((QueryParam) key).value();
|
||||||
Optional<?> paramValue = getParamValue(method, args, extractors, entry, paramKey);
|
Optional<?> paramValue = getParamValue(method, args, extractors, entry, paramKey);
|
||||||
if (paramValue.isPresent())
|
if (paramValue.isPresent())
|
||||||
|
@ -1317,13 +1333,13 @@ public class RestAnnotationProcessor<T> {
|
||||||
|
|
||||||
//TODO: change to LoadingCache<ClassMethodArgs, Map<String,Object> and move this logic to the CacheLoader.
|
//TODO: change to LoadingCache<ClassMethodArgs, Map<String,Object> and move this logic to the CacheLoader.
|
||||||
//take care to manage size of this cache
|
//take care to manage size of this cache
|
||||||
private Map<String, Object> buildPostParams(Method method, Object... args) throws ExecutionException {
|
private Map<String, Object> buildPostParams(Method method, Object... args) {
|
||||||
Map<String, Object> postParams = newHashMap();
|
Map<String, Object> postParams = newHashMap();
|
||||||
LoadingCache<Integer, Set<Annotation>> indexToPathParam = methodToIndexOfParamToPostParamAnnotations.get(method);
|
LoadingCache<Integer, Set<Annotation>> indexToPathParam = methodToIndexOfParamToPostParamAnnotations.getUnchecked(method);
|
||||||
LoadingCache<Integer, Set<Annotation>> indexToParamExtractor = methodToIndexOfParamToParamParserAnnotations.get(method);
|
LoadingCache<Integer, Set<Annotation>> indexToParamExtractor = methodToIndexOfParamToParamParserAnnotations.getUnchecked(method);
|
||||||
for (Entry<Integer, Set<Annotation>> entry : indexToPathParam.asMap().entrySet()) {
|
for (Entry<Integer, Set<Annotation>> entry : indexToPathParam.asMap().entrySet()) {
|
||||||
for (Annotation key : entry.getValue()) {
|
for (Annotation key : entry.getValue()) {
|
||||||
Set<Annotation> extractors = indexToParamExtractor.get(entry.getKey());
|
Set<Annotation> extractors = indexToParamExtractor.getUnchecked(entry.getKey());
|
||||||
String paramKey = ((PayloadParam) key).value();
|
String paramKey = ((PayloadParam) key).value();
|
||||||
Optional<?> paramValue = getParamValue(method, args, extractors, entry, paramKey);
|
Optional<?> paramValue = getParamValue(method, args, extractors, entry, paramKey);
|
||||||
if (paramValue.isPresent())
|
if (paramValue.isPresent())
|
||||||
|
|
|
@ -0,0 +1,116 @@
|
||||||
|
/**
|
||||||
|
* 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.annotationparsing;
|
||||||
|
|
||||||
|
import static org.jclouds.providers.AnonymousProviderMetadata.forClientMappedToAsyncClientOnEndpoint;
|
||||||
|
import static org.testng.Assert.assertTrue;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import javax.ws.rs.HEAD;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.PathParam;
|
||||||
|
|
||||||
|
import org.jclouds.concurrent.Timeout;
|
||||||
|
import org.jclouds.http.HttpRequest;
|
||||||
|
import org.jclouds.http.HttpResponse;
|
||||||
|
import org.jclouds.providers.ProviderMetadata;
|
||||||
|
import org.jclouds.rest.ConfiguresRestClient;
|
||||||
|
import org.jclouds.rest.annotations.Delegate;
|
||||||
|
import org.jclouds.rest.annotations.ExceptionParser;
|
||||||
|
import org.jclouds.rest.config.RestClientModule;
|
||||||
|
import org.jclouds.rest.functions.ReturnFalseOnNotFoundOr404;
|
||||||
|
import org.jclouds.rest.internal.BaseRestClientExpectTest;
|
||||||
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
|
import com.google.inject.Module;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the ways that {@link Delegate}
|
||||||
|
*
|
||||||
|
* @author Adrian Cole
|
||||||
|
*/
|
||||||
|
@Test(groups = "unit", testName = "DelegateAnnotationExpectTest")
|
||||||
|
public class DelegateAnnotationExpectTest extends BaseRestClientExpectTest<DelegateAnnotationExpectTest.DelegatingApi> {
|
||||||
|
|
||||||
|
@Timeout(duration = 60, timeUnit = TimeUnit.SECONDS)
|
||||||
|
static interface DelegatingApi {
|
||||||
|
|
||||||
|
@Delegate
|
||||||
|
@Path("/projects/{project}")
|
||||||
|
DiskApi getDiskApiForProject(@PathParam("project") String projectName);
|
||||||
|
}
|
||||||
|
|
||||||
|
static interface DelegatingAsyncApi {
|
||||||
|
|
||||||
|
@Delegate
|
||||||
|
@Path("/projects/{project}")
|
||||||
|
DiskAsyncApi getDiskApiForProject(@PathParam("project") String projectName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Timeout(duration = 1, timeUnit = TimeUnit.SECONDS)
|
||||||
|
static interface DiskApi {
|
||||||
|
|
||||||
|
boolean exists(@PathParam("disk") String diskName);
|
||||||
|
}
|
||||||
|
|
||||||
|
static interface DiskAsyncApi {
|
||||||
|
|
||||||
|
@HEAD
|
||||||
|
@Path("/disks/{disk}")
|
||||||
|
@ExceptionParser(ReturnFalseOnNotFoundOr404.class)
|
||||||
|
public ListenableFuture<Boolean> exists(@PathParam("disk") String diskName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDelegatingCallTakesIntoConsiderationCallerAndCalleePath() {
|
||||||
|
|
||||||
|
DelegatingApi client = requestSendsResponse(
|
||||||
|
HttpRequest.builder().method("HEAD").endpoint("http://mock/projects/prod/disks/disk1").build(),
|
||||||
|
HttpResponse.builder().statusCode(200).build());
|
||||||
|
|
||||||
|
assertTrue(client.getDiskApiForProject("prod").exists("disk1"));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// crufty junk until we inspect delegating api classes for all their client
|
||||||
|
// mappings and make a test helper for random classes.
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ProviderMetadata createProviderMetadata() {
|
||||||
|
return forClientMappedToAsyncClientOnEndpoint(DelegatingApi.class, DelegatingAsyncApi.class, "http://mock/");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Module createModule() {
|
||||||
|
return new DelegatingRestClientModule();
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfiguresRestClient
|
||||||
|
static class DelegatingRestClientModule extends RestClientModule<DelegatingApi, DelegatingAsyncApi> {
|
||||||
|
|
||||||
|
public DelegatingRestClientModule() {
|
||||||
|
// right now, we have to define the delegates by hand as opposed to
|
||||||
|
// reflection looking for coordinated annotations
|
||||||
|
super(ImmutableMap.<Class<?>, Class<?>> of(DiskApi.class, DiskAsyncApi.class));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue