1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.jclouds.rest.internal;
20
21 import static com.google.common.base.Preconditions.checkArgument;
22 import static com.google.common.collect.Collections2.filter;
23 import static com.google.common.collect.Iterables.concat;
24 import static com.google.common.collect.Iterables.get;
25 import static com.google.common.collect.Iterables.transform;
26 import static com.google.common.collect.Lists.newLinkedList;
27 import static com.google.common.collect.Maps.filterValues;
28 import static com.google.common.collect.Maps.newHashMap;
29 import static com.google.common.collect.Sets.newTreeSet;
30 import static java.util.Arrays.asList;
31 import static javax.ws.rs.core.HttpHeaders.ACCEPT;
32 import static javax.ws.rs.core.HttpHeaders.CONTENT_TYPE;
33 import static javax.ws.rs.core.HttpHeaders.HOST;
34 import static org.jclouds.io.Payloads.newPayload;
35
36 import java.io.InputStream;
37 import java.lang.annotation.Annotation;
38 import java.lang.reflect.Array;
39 import java.lang.reflect.Method;
40 import java.lang.reflect.ParameterizedType;
41 import java.lang.reflect.Type;
42 import java.lang.reflect.WildcardType;
43 import java.net.URI;
44 import java.util.Collection;
45 import java.util.Collections;
46 import java.util.Comparator;
47 import java.util.List;
48 import java.util.Map;
49 import java.util.Set;
50 import java.util.SortedSet;
51 import java.util.Map.Entry;
52 import java.util.concurrent.ConcurrentMap;
53
54 import javax.annotation.Nullable;
55 import javax.annotation.Resource;
56 import javax.inject.Named;
57 import javax.inject.Provider;
58 import javax.ws.rs.Consumes;
59 import javax.ws.rs.FormParam;
60 import javax.ws.rs.HeaderParam;
61 import javax.ws.rs.HttpMethod;
62 import javax.ws.rs.MatrixParam;
63 import javax.ws.rs.Path;
64 import javax.ws.rs.PathParam;
65 import javax.ws.rs.Produces;
66 import javax.ws.rs.QueryParam;
67 import javax.ws.rs.core.MediaType;
68 import javax.ws.rs.core.UriBuilder;
69 import javax.ws.rs.core.UriBuilderException;
70
71 import org.jclouds.Constants;
72 import org.jclouds.functions.IdentityFunction;
73 import org.jclouds.http.HttpRequest;
74 import org.jclouds.http.HttpRequestFilter;
75 import org.jclouds.http.HttpResponse;
76 import org.jclouds.http.HttpUtils;
77 import org.jclouds.http.functions.ParseJson;
78 import org.jclouds.http.functions.ParseSax;
79 import org.jclouds.http.functions.ParseURIFromListOrLocationHeaderIf20x;
80 import org.jclouds.http.functions.ReleasePayloadAndReturn;
81 import org.jclouds.http.functions.ReturnInputStream;
82 import org.jclouds.http.functions.ReturnStringIf2xx;
83 import org.jclouds.http.functions.ReturnTrueIf2xx;
84 import org.jclouds.http.functions.UnwrapOnlyJsonValue;
85 import org.jclouds.http.functions.UnwrapOnlyJsonValueInSet;
86 import org.jclouds.http.functions.UnwrapOnlyNestedJsonValue;
87 import org.jclouds.http.functions.UnwrapOnlyNestedJsonValueInSet;
88 import org.jclouds.http.functions.ParseSax.HandlerWithResult;
89 import org.jclouds.http.options.HttpRequestOptions;
90 import org.jclouds.http.utils.ModifyRequest;
91 import org.jclouds.internal.ClassMethodArgs;
92 import org.jclouds.io.ContentMetadata;
93 import org.jclouds.io.Payload;
94 import org.jclouds.io.PayloadEnclosing;
95 import org.jclouds.io.Payloads;
96 import org.jclouds.io.payloads.MultipartForm;
97 import org.jclouds.io.payloads.Part;
98 import org.jclouds.io.payloads.Part.PartOptions;
99 import org.jclouds.logging.Logger;
100 import org.jclouds.rest.Binder;
101 import org.jclouds.rest.InputParamValidator;
102 import org.jclouds.rest.InvocationContext;
103 import org.jclouds.rest.annotations.BinderParam;
104 import org.jclouds.rest.annotations.Endpoint;
105 import org.jclouds.rest.annotations.EndpointParam;
106 import org.jclouds.rest.annotations.ExceptionParser;
107 import org.jclouds.rest.annotations.FormParams;
108 import org.jclouds.rest.annotations.Headers;
109 import org.jclouds.rest.annotations.MapBinder;
110 import org.jclouds.rest.annotations.MatrixParams;
111 import org.jclouds.rest.annotations.OverrideRequestFilters;
112 import org.jclouds.rest.annotations.ParamParser;
113 import org.jclouds.rest.annotations.PartParam;
114 import org.jclouds.rest.annotations.PayloadParam;
115 import org.jclouds.rest.annotations.PayloadParams;
116 import org.jclouds.rest.annotations.QueryParams;
117 import org.jclouds.rest.annotations.RequestFilters;
118 import org.jclouds.rest.annotations.ResponseParser;
119 import org.jclouds.rest.annotations.SkipEncoding;
120 import org.jclouds.rest.annotations.Unwrap;
121 import org.jclouds.rest.annotations.VirtualHost;
122 import org.jclouds.rest.annotations.WrapWith;
123 import org.jclouds.rest.annotations.XMLResponseParser;
124 import org.jclouds.rest.binders.BindMapToStringPayload;
125 import org.jclouds.rest.binders.BindToJsonPayloadWrappedWith;
126 import org.jclouds.rest.functions.MapHttp4xxCodesToExceptions;
127 import org.jclouds.util.Maps2;
128 import org.jclouds.util.Strings2;
129
130 import com.google.common.annotations.VisibleForTesting;
131 import com.google.common.base.Function;
132 import com.google.common.base.Predicate;
133 import com.google.common.base.Predicates;
134 import com.google.common.collect.ImmutableList;
135 import com.google.common.collect.ImmutableMultimap;
136 import com.google.common.collect.ImmutableSet;
137 import com.google.common.collect.Iterables;
138 import com.google.common.collect.LinkedHashMultimap;
139 import com.google.common.collect.LinkedListMultimap;
140 import com.google.common.collect.Lists;
141 import com.google.common.collect.MapMaker;
142 import com.google.common.collect.Multimap;
143 import com.google.common.collect.ImmutableSet.Builder;
144 import com.google.common.util.concurrent.ListenableFuture;
145 import com.google.inject.Inject;
146 import com.google.inject.Injector;
147 import com.google.inject.Key;
148 import com.google.inject.TypeLiteral;
149 import com.google.inject.util.Types;
150
151
152
153
154
155
156 public class RestAnnotationProcessor<T> {
157
158 @Resource
159 protected Logger logger = Logger.NULL;
160
161 private final Class<T> declaring;
162
163
164 static final Map<Method, Map<Integer, Set<Annotation>>> methodToIndexOfParamToBinderParamAnnotation = createMethodToIndexOfParamToAnnotation(BinderParam.class);
165 static final Map<Method, Map<Integer, Set<Annotation>>> methodToIndexOfParamToWrapWithAnnotation = createMethodToIndexOfParamToAnnotation(WrapWith.class);
166 static final Map<Method, Map<Integer, Set<Annotation>>> methodToIndexOfParamToHeaderParamAnnotations = createMethodToIndexOfParamToAnnotation(HeaderParam.class);
167 static final Map<Method, Map<Integer, Set<Annotation>>> methodToIndexOfParamToEndpointAnnotations = createMethodToIndexOfParamToAnnotation(Endpoint.class);
168 static final Map<Method, Map<Integer, Set<Annotation>>> methodToIndexOfParamToEndpointParamAnnotations = createMethodToIndexOfParamToAnnotation(EndpointParam.class);
169 static final Map<Method, Map<Integer, Set<Annotation>>> methodToIndexOfParamToMatrixParamAnnotations = createMethodToIndexOfParamToAnnotation(MatrixParam.class);
170 static final Map<Method, Map<Integer, Set<Annotation>>> methodToIndexOfParamToFormParamAnnotations = createMethodToIndexOfParamToAnnotation(FormParam.class);
171 static final Map<Method, Map<Integer, Set<Annotation>>> methodToIndexOfParamToQueryParamAnnotations = createMethodToIndexOfParamToAnnotation(QueryParam.class);
172 static final Map<Method, Map<Integer, Set<Annotation>>> methodToIndexOfParamToPathParamAnnotations = createMethodToIndexOfParamToAnnotation(PathParam.class);
173 static final Map<Method, Map<Integer, Set<Annotation>>> methodToIndexOfParamToPostParamAnnotations = createMethodToIndexOfParamToAnnotation(PayloadParam.class);
174 static final Map<Method, Map<Integer, Set<Annotation>>> methodToIndexOfParamToPartParamAnnotations = createMethodToIndexOfParamToAnnotation(PartParam.class);
175 static final Map<Method, Map<Integer, Set<Annotation>>> methodToIndexOfParamToParamParserAnnotations = createMethodToIndexOfParamToAnnotation(ParamParser.class);
176 static final Map<MethodKey, Method> delegationMap = newHashMap();
177
178 static Map<Method, Map<Integer, Set<Annotation>>> createMethodToIndexOfParamToAnnotation(
179 final Class<? extends Annotation> annotation) {
180 return new MapMaker().makeComputingMap(new Function<Method, Map<Integer, Set<Annotation>>>() {
181 public Map<Integer, Set<Annotation>> apply(Method method) {
182 return new MapMaker().makeComputingMap(new GetAnnotationsForMethodParameterIndex(method, annotation));
183 }
184 });
185 }
186
187 static class GetAnnotationsForMethodParameterIndex implements Function<Integer, Set<Annotation>> {
188 private final Method method;
189 private final Class<?> clazz;
190
191 protected GetAnnotationsForMethodParameterIndex(Method method, Class<?> clazz) {
192 this.method = method;
193 this.clazz = clazz;
194 }
195
196 public Set<Annotation> apply(final Integer index) {
197 return ImmutableSet.<Annotation> copyOf(filter(ImmutableList.copyOf(method.getParameterAnnotations()[index]),
198 new Predicate<Annotation>() {
199 public boolean apply(Annotation input) {
200 return input.annotationType().equals(clazz);
201 }
202 }));
203 }
204
205 }
206
207 private static final Class<? extends HttpRequestOptions[]> optionsVarArgsClass = new HttpRequestOptions[] {}
208 .getClass();
209
210 private static final Function<? super Entry<String, String>, ? extends Part> ENTRY_TO_PART = new Function<Entry<String, String>, Part>() {
211
212 @Override
213 public Part apply(Entry<String, String> from) {
214 return Part.create(from.getKey(), from.getValue());
215 }
216
217 };
218
219 static final Map<Method, Set<Integer>> methodToIndexesOfOptions = new MapMaker()
220 .makeComputingMap(new Function<Method, Set<Integer>>() {
221 public Set<Integer> apply(Method method) {
222 Builder<Integer> toReturn = ImmutableSet.<Integer> builder();
223 for (int index = 0; index < method.getParameterTypes().length; index++) {
224 Class<?> type = method.getParameterTypes()[index];
225 if (HttpRequestOptions.class.isAssignableFrom(type) || optionsVarArgsClass.isAssignableFrom(type))
226 toReturn.add(index);
227 }
228 return toReturn.build();
229 }
230 });
231
232 private final ParseSax.Factory parserFactory;
233 private final HttpUtils utils;
234 private final Provider<UriBuilder> uriBuilderProvider;
235 private final ConcurrentMap<Class<?>, Boolean> seedAnnotationCache;
236 private final String apiVersion;
237 private char[] skips;
238
239 @Inject
240 private InputParamValidator inputParamValidator;
241
242 @VisibleForTesting
243 Function<HttpResponse, ?> createResponseParser(Method method, HttpRequest request) {
244 return createResponseParser(parserFactory, injector, method, request);
245 }
246
247 @VisibleForTesting
248 public static Function<HttpResponse, ?> createResponseParser(ParseSax.Factory parserFactory, Injector injector,
249 Method method, HttpRequest request) {
250 Function<HttpResponse, ?> transformer;
251 Class<? extends HandlerWithResult<?>> handler = getSaxResponseParserClassOrNull(method);
252 if (handler != null) {
253 transformer = parserFactory.create(injector.getInstance(handler));
254 } else {
255 transformer = injector.getInstance(getParserOrThrowException(method));
256 }
257 if (transformer instanceof InvocationContext<?>) {
258 ((InvocationContext<?>) transformer).setContext(request);
259 }
260 return transformer;
261 }
262
263 @VisibleForTesting
264 Function<Exception, ?> createExceptionParserOrThrowResourceNotFoundOn404IfNoAnnotation(Method method) {
265 return createExceptionParserOrThrowResourceNotFoundOn404IfNoAnnotation(injector, method);
266 }
267
268 @VisibleForTesting
269 public static Function<Exception, ?> createExceptionParserOrThrowResourceNotFoundOn404IfNoAnnotation(
270 Injector injector, Method method) {
271 ExceptionParser annotation = method.getAnnotation(ExceptionParser.class);
272 if (annotation != null) {
273 return injector.getInstance(annotation.value());
274 }
275 return injector.getInstance(MapHttp4xxCodesToExceptions.class);
276 }
277
278 @SuppressWarnings("unchecked")
279 @Inject
280 public RestAnnotationProcessor(Injector injector, ConcurrentMap<Class<?>, Boolean> seedAnnotationCache,
281 @Named(Constants.PROPERTY_API_VERSION) String apiVersion, ParseSax.Factory parserFactory, HttpUtils utils,
282 TypeLiteral<T> typeLiteral) {
283 this.declaring = (Class<T>) typeLiteral.getRawType();
284 this.injector = injector;
285 this.parserFactory = parserFactory;
286 this.utils = utils;
287 this.uriBuilderProvider = injector.getProvider(UriBuilder.class);
288 this.seedAnnotationCache = seedAnnotationCache;
289 seedAnnotationCache.get(declaring);
290 if (declaring.isAnnotationPresent(SkipEncoding.class)) {
291 skips = declaring.getAnnotation(SkipEncoding.class).value();
292 } else {
293 skips = new char[] {};
294 }
295 this.apiVersion = apiVersion;
296 }
297
298 public Method getDelegateOrNull(Method in) {
299 return delegationMap.get(new MethodKey(in));
300 }
301
302 public static class MethodKey {
303
304 @Override
305 public int hashCode() {
306 final int prime = 31;
307 int result = 1;
308 result = prime * result + ((declaringPackage == null) ? 0 : declaringPackage.hashCode());
309 result = prime * result + ((name == null) ? 0 : name.hashCode());
310 result = prime * result + parametersTypeHashCode;
311 return result;
312 }
313
314 @Override
315 public boolean equals(Object obj) {
316 if (this == obj)
317 return true;
318 if (obj == null)
319 return false;
320 if (getClass() != obj.getClass())
321 return false;
322 MethodKey other = (MethodKey) obj;
323 if (declaringPackage == null) {
324 if (other.declaringPackage != null)
325 return false;
326 } else if (!declaringPackage.equals(other.declaringPackage))
327 return false;
328 if (name == null) {
329 if (other.name != null)
330 return false;
331 } else if (!name.equals(other.name))
332 return false;
333 if (parametersTypeHashCode != other.parametersTypeHashCode)
334 return false;
335 return true;
336 }
337
338 private final String name;
339 private final int parametersTypeHashCode;
340 private final Package declaringPackage;
341
342 public MethodKey(Method method) {
343 this.name = method.getName();
344 this.declaringPackage = method.getDeclaringClass().getPackage();
345 int parametersTypeHashCode = 0;
346 for (Class<?> param: method.getParameterTypes())
347 parametersTypeHashCode +=param.hashCode();
348 this.parametersTypeHashCode = parametersTypeHashCode;
349 }
350
351 }
352
353 final Injector injector;
354
355 private ClassMethodArgs caller;
356 private URI callerEndpoint;
357
358 public void setCaller(ClassMethodArgs caller) {
359 seedAnnotationCache.get(caller.getMethod().getDeclaringClass());
360 this.caller = caller;
361 try {
362 callerEndpoint = getEndpointFor(caller.getMethod(), caller.getArgs(), injector);
363 } catch (IllegalStateException e) {
364 }
365 }
366
367 public GeneratedHttpRequest<T> createRequest(Method method, Object... args) {
368 inputParamValidator.validateMethodParametersOrThrow(method, args);
369 ClassMethodArgs cma = logger.isTraceEnabled() ? new ClassMethodArgs(method.getDeclaringClass(), method, args)
370 : null;
371
372 URI endpoint = callerEndpoint;
373 try {
374 if (endpoint == null) {
375 endpoint = getEndpointFor(method, args, injector);
376 logger.trace("using endpoint %s for %s", endpoint, cma);
377 } else {
378 logger.trace("using endpoint %s from caller %s for %s", caller, endpoint, cma);
379 }
380 } catch (IllegalStateException e) {
381 logger.trace("looking up default endpoint for %s", cma);
382 endpoint = injector.getInstance(Key.get(URI.class, org.jclouds.location.Provider.class));
383 logger.trace("using default endpoint %s for %s", endpoint, cma);
384 }
385 GeneratedHttpRequest.Builder<T> requestBuilder;
386 HttpRequest r = RestAnnotationProcessor.findHttpRequestInArgs(args);
387 if (r != null) {
388 requestBuilder = GeneratedHttpRequest.Builder.<T> from(r);
389 endpoint = r.getEndpoint();
390 } else {
391 requestBuilder = GeneratedHttpRequest.<T> builder();
392 requestBuilder.method(getHttpMethodOrConstantOrThrowException(method));
393 }
394
395 requestBuilder.declaring(declaring).javaMethod(method).args(args).skips(skips);
396 requestBuilder.filters(getFiltersIfAnnotated(method));
397
398 UriBuilder builder = uriBuilderProvider.get().uri(endpoint);
399
400 Multimap<String, String> tokenValues = LinkedHashMultimap.create();
401
402 tokenValues.put(Constants.PROPERTY_API_VERSION, apiVersion);
403
404 tokenValues.putAll(addPathAndGetTokens(declaring, method, args, builder));
405
406 Multimap<String, String> formParams = addFormParams(tokenValues.entries(), method, args);
407 Multimap<String, String> queryParams = addQueryParams(tokenValues.entries(), method, args);
408 Multimap<String, String> matrixParams = addMatrixParams(tokenValues.entries(), method, args);
409 Multimap<String, String> headers = buildHeaders(tokenValues.entries(), method, args);
410 if (r != null)
411 headers.putAll(r.getHeaders());
412
413 if (shouldAddHostHeader(method)) {
414 StringBuilder hostHeader = new StringBuilder(endpoint.getHost());
415 if (endpoint.getPort() != -1)
416 hostHeader.append(":").append(endpoint.getPort());
417 headers.put(HOST, hostHeader.toString());
418 }
419
420 Payload payload = null;
421 HttpRequestOptions options = findOptionsIn(method, args);
422 if (options != null) {
423 injector.injectMembers(options);
424 for (Entry<String, String> header : options.buildRequestHeaders().entries()) {
425 headers.put(header.getKey(), Strings2.replaceTokens(header.getValue(), tokenValues.entries()));
426 }
427 for (Entry<String, String> matrix : options.buildMatrixParameters().entries()) {
428 matrixParams.put(matrix.getKey(), Strings2.replaceTokens(matrix.getValue(), tokenValues.entries()));
429 }
430 for (Entry<String, String> query : options.buildQueryParameters().entries()) {
431 queryParams.put(query.getKey(), Strings2.replaceTokens(query.getValue(), tokenValues.entries()));
432 }
433 for (Entry<String, String> form : options.buildFormParameters().entries()) {
434 formParams.put(form.getKey(), Strings2.replaceTokens(form.getValue(), tokenValues.entries()));
435 }
436
437 String pathSuffix = options.buildPathSuffix();
438 if (pathSuffix != null) {
439 builder.path(pathSuffix);
440 }
441 String stringPayload = options.buildStringPayload();
442 if (stringPayload != null)
443 payload = Payloads.newStringPayload(stringPayload);
444 }
445
446 if (matrixParams.size() > 0) {
447 for (String key : matrixParams.keySet())
448 builder.matrixParam(key, Lists.newArrayList(matrixParams.get(key)).toArray());
449 }
450
451 if (queryParams.size() > 0) {
452 builder.replaceQuery(ModifyRequest.makeQueryLine(queryParams, null, skips));
453 }
454
455 requestBuilder.headers(filterOutContentHeaders(headers));
456
457 try {
458 requestBuilder.endpoint(builder.buildFromEncodedMap(Maps2.convertUnsafe(tokenValues)));
459 } catch (IllegalArgumentException e) {
460 throw new IllegalStateException(e);
461 } catch (UriBuilderException e) {
462 throw new IllegalStateException(e);
463 }
464
465 if (payload == null)
466 payload = findPayloadInArgs(args);
467 List<? extends Part> parts = getParts(method, args, concat(tokenValues.entries(), formParams.entries()));
468 if (parts.size() > 0) {
469 if (formParams.size() > 0) {
470 parts = newLinkedList(concat(transform(formParams.entries(), ENTRY_TO_PART), parts));
471 }
472 payload = new MultipartForm(BOUNDARY, parts);
473 } else if (formParams.size() > 0) {
474 payload = Payloads.newUrlEncodedFormPayload(formParams, skips);
475 } else if (headers.containsKey(CONTENT_TYPE)) {
476 if (payload == null)
477 payload = Payloads.newByteArrayPayload(new byte[] {});
478 payload.getContentMetadata().setContentType(Iterables.get(headers.get(CONTENT_TYPE), 0));
479 }
480 if (payload != null) {
481 requestBuilder.payload(payload);
482 }
483 GeneratedHttpRequest<T> request = requestBuilder.build();
484
485 org.jclouds.rest.MapBinder mapBinder = getMapPayloadBinderOrNull(method, args);
486 if (mapBinder != null) {
487 Map<String, String> mapParams = buildPostParams(method, args);
488 if (method.isAnnotationPresent(PayloadParams.class)) {
489 PayloadParams params = method.getAnnotation(PayloadParams.class);
490 addMapPayload(mapParams, params, headers.entries());
491 }
492 request = mapBinder.bindToRequest(request, mapParams);
493 } else {
494 request = decorateRequest(request);
495 }
496
497 if (request.getPayload() != null)
498 request.getPayload().getContentMetadata().setPropertiesFromHttpHeaders(headers);
499 utils.checkRequestHasRequiredProperties(request);
500 return request;
501 }
502
503 public static Multimap<String, String> filterOutContentHeaders(Multimap<String, String> headers) {
504
505 ImmutableMultimap.Builder<String, String> headersBuilder = ImmutableMultimap.builder();
506
507 for (String header : Iterables.filter(headers.keySet(), Predicates.notNull())) {
508 if (!ContentMetadata.HTTP_HEADERS.contains(header)) {
509 headersBuilder.putAll(header, headers.get(header));
510 }
511 }
512 return headersBuilder.build();
513 }
514
515 public static final String BOUNDARY = "--JCLOUDS--";
516
517 private Multimap<String, String> addPathAndGetTokens(Class<?> clazz, Method method, Object[] args, UriBuilder builder) {
518 if (clazz.isAnnotationPresent(Path.class))
519 builder.path(clazz);
520 if (method.isAnnotationPresent(Path.class))
521 builder.path(method);
522 return encodeValues(getPathParamKeyValues(method, args), skips);
523 }
524
525 public URI replaceQuery(URI in, String newQuery, @Nullable Comparator<Entry<String, String>> sorter) {
526 return replaceQuery(uriBuilderProvider, in, newQuery, sorter, skips);
527 }
528
529 public static URI replaceQuery(Provider<UriBuilder> uriBuilderProvider, URI in, String newQuery,
530 @Nullable Comparator<Entry<String, String>> sorter, char... skips) {
531 UriBuilder builder = uriBuilderProvider.get().uri(in);
532 builder.replaceQuery(ModifyRequest.makeQueryLine(ModifyRequest.parseQueryToMap(newQuery), sorter, skips));
533 return builder.build();
534 }
535
536 private Multimap<String, String> addMatrixParams(Collection<Entry<String, String>> tokenValues, Method method,
537 Object... args) {
538 Multimap<String, String> matrixMap = LinkedListMultimap.create();
539 if (declaring.isAnnotationPresent(MatrixParams.class)) {
540 MatrixParams matrix = declaring.getAnnotation(MatrixParams.class);
541 addMatrix(matrixMap, matrix, tokenValues);
542 }
543
544 if (method.isAnnotationPresent(MatrixParams.class)) {
545 MatrixParams matrix = method.getAnnotation(MatrixParams.class);
546 addMatrix(matrixMap, matrix, tokenValues);
547 }
548
549 for (Entry<String, String> matrix : getMatrixParamKeyValues(method, args).entries()) {
550 matrixMap.put(matrix.getKey(), Strings2.replaceTokens(matrix.getValue(), tokenValues));
551 }
552 return matrixMap;
553 }
554
555 private Multimap<String, String> addFormParams(Collection<Entry<String, String>> tokenValues, Method method,
556 Object... args) {
557 Multimap<String, String> formMap = LinkedListMultimap.create();
558 if (declaring.isAnnotationPresent(FormParams.class)) {
559 FormParams form = declaring.getAnnotation(FormParams.class);
560 addForm(formMap, form, tokenValues);
561 }
562
563 if (method.isAnnotationPresent(FormParams.class)) {
564 FormParams form = method.getAnnotation(FormParams.class);
565 addForm(formMap, form, tokenValues);
566 }
567
568 for (Entry<String, String> form : getFormParamKeyValues(method, args).entries()) {
569 formMap.put(form.getKey(), Strings2.replaceTokens(form.getValue(), tokenValues));
570 }
571 return formMap;
572 }
573
574 private Multimap<String, String> addQueryParams(Collection<Entry<String, String>> tokenValues, Method method,
575 Object... args) {
576 Multimap<String, String> queryMap = LinkedListMultimap.create();
577 if (declaring.isAnnotationPresent(QueryParams.class)) {
578 QueryParams query = declaring.getAnnotation(QueryParams.class);
579 addQuery(queryMap, query, tokenValues);
580 }
581
582 if (method.isAnnotationPresent(QueryParams.class)) {
583 QueryParams query = method.getAnnotation(QueryParams.class);
584 addQuery(queryMap, query, tokenValues);
585 }
586
587 for (Entry<String, String> query : getQueryParamKeyValues(method, args).entries()) {
588 queryMap.put(query.getKey(), Strings2.replaceTokens(query.getValue(), tokenValues));
589 }
590 return queryMap;
591 }
592
593 private void addForm(Multimap<String, String> formParams, FormParams form,
594 Collection<Entry<String, String>> tokenValues) {
595 for (int i = 0; i < form.keys().length; i++) {
596 if (form.values()[i].equals(FormParams.NULL)) {
597 formParams.removeAll(form.keys()[i]);
598 formParams.put(form.keys()[i], null);
599 } else {
600 formParams.put(form.keys()[i], Strings2.replaceTokens(form.values()[i], tokenValues));
601 }
602 }
603 }
604
605 private void addQuery(Multimap<String, String> queryParams, QueryParams query,
606 Collection<Entry<String, String>> tokenValues) {
607 for (int i = 0; i < query.keys().length; i++) {
608 if (query.values()[i].equals(QueryParams.NULL)) {
609 queryParams.removeAll(query.keys()[i]);
610 queryParams.put(query.keys()[i], null);
611 } else {
612 queryParams.put(query.keys()[i], Strings2.replaceTokens(query.values()[i], tokenValues));
613 }
614 }
615 }
616
617 private void addMatrix(Multimap<String, String> matrixParams, MatrixParams matrix,
618 Collection<Entry<String, String>> tokenValues) {
619 for (int i = 0; i < matrix.keys().length; i++) {
620 if (matrix.values()[i].equals(MatrixParams.NULL)) {
621 matrixParams.removeAll(matrix.keys()[i]);
622 matrixParams.put(matrix.keys()[i], null);
623 } else {
624 matrixParams.put(matrix.keys()[i], Strings2.replaceTokens(matrix.values()[i], tokenValues));
625 }
626 }
627 }
628
629 private void addMapPayload(Map<String, String> postParams, PayloadParams mapDefaults,
630 Collection<Entry<String, String>> tokenValues) {
631 for (int i = 0; i < mapDefaults.keys().length; i++) {
632 if (mapDefaults.values()[i].equals(PayloadParams.NULL)) {
633 postParams.put(mapDefaults.keys()[i], null);
634 } else {
635 postParams.put(mapDefaults.keys()[i], Strings2.replaceTokens(mapDefaults.values()[i], tokenValues));
636 }
637 }
638 }
639
640 @VisibleForTesting
641 List<HttpRequestFilter> getFiltersIfAnnotated(Method method) {
642 List<HttpRequestFilter> filters = Lists.newArrayList();
643 if (declaring.isAnnotationPresent(RequestFilters.class)) {
644 for (Class<? extends HttpRequestFilter> clazz : declaring.getAnnotation(RequestFilters.class).value()) {
645 HttpRequestFilter instance = injector.getInstance(clazz);
646 filters.add(instance);
647 logger.trace("adding filter %s from annotation on %s", instance, declaring.getName());
648 }
649 }
650 if (method.isAnnotationPresent(RequestFilters.class)) {
651 if (method.isAnnotationPresent(OverrideRequestFilters.class))
652 filters.clear();
653 for (Class<? extends HttpRequestFilter> clazz : method.getAnnotation(RequestFilters.class).value()) {
654 HttpRequestFilter instance = injector.getInstance(clazz);
655 filters.add(instance);
656 logger.trace("adding filter %s from annotation on %s", instance, method.getName());
657 }
658 }
659 return filters;
660 }
661
662 @VisibleForTesting
663 public static URI getEndpointInParametersOrNull(Method method, final Object[] args, Injector injector) {
664 Map<Integer, Set<Annotation>> map = indexWithAtLeastOneAnnotation(method,
665 methodToIndexOfParamToEndpointParamAnnotations);
666 if (map.size() >= 1 && args.length > 0) {
667 EndpointParam firstAnnotation = (EndpointParam) get(get(map.values(), 0), 0);
668 Function<Object, URI> parser = injector.getInstance(firstAnnotation.parser());
669
670 if (map.size() == 1) {
671 int index = map.keySet().iterator().next();
672 try {
673 URI returnVal = parser.apply(args[index]);
674 checkArgument(returnVal != null, String.format("endpoint for [%s] not configured for %s", args[index],
675 method));
676 return returnVal;
677 } catch (NullPointerException e) {
678 throw new IllegalArgumentException(String.format("argument at index %d on method %s", index, method), e);
679 }
680 } else {
681 SortedSet<Integer> keys = newTreeSet(map.keySet());
682 Iterable<Object> argsToParse = transform(keys, new Function<Integer, Object>() {
683
684 @Override
685 public Object apply(Integer from) {
686 return args[from];
687 }
688
689 });
690 try {
691 URI returnVal = parser.apply(argsToParse);
692 checkArgument(returnVal != null, String.format("endpoint for [%s] not configured for %s", argsToParse,
693 method));
694 return returnVal;
695 } catch (NullPointerException e) {
696 throw new IllegalArgumentException(String.format("illegal argument in [%s] for method %s", argsToParse,
697 method), e);
698 }
699 }
700 }
701 return null;
702 }
703
704 public static URI getEndpointFor(Method method, Object[] args, Injector injector) {
705 URI endpoint = getEndpointInParametersOrNull(method, args, injector);
706 if (endpoint == null) {
707 Endpoint annotation;
708 if (method.isAnnotationPresent(Endpoint.class)) {
709 annotation = method.getAnnotation(Endpoint.class);
710 } else if (method.getDeclaringClass().isAnnotationPresent(Endpoint.class)) {
711 annotation = method.getDeclaringClass().getAnnotation(Endpoint.class);
712 } else {
713 throw new IllegalStateException("no annotations on class or method: " + method);
714 }
715 return injector.getInstance(Key.get(URI.class, annotation.value()));
716 }
717 return endpoint;
718 }
719
720 public static final TypeLiteral<ListenableFuture<Boolean>> futureBooleanLiteral = new TypeLiteral<ListenableFuture<Boolean>>() {
721 };
722 public static final TypeLiteral<ListenableFuture<String>> futureStringLiteral = new TypeLiteral<ListenableFuture<String>>() {
723 };
724 public static final TypeLiteral<ListenableFuture<Void>> futureVoidLiteral = new TypeLiteral<ListenableFuture<Void>>() {
725 };
726 public static final TypeLiteral<ListenableFuture<URI>> futureURILiteral = new TypeLiteral<ListenableFuture<URI>>() {
727 };
728 public static final TypeLiteral<ListenableFuture<InputStream>> futureInputStreamLiteral = new TypeLiteral<ListenableFuture<InputStream>>() {
729 };
730 public static final TypeLiteral<ListenableFuture<HttpResponse>> futureHttpResponseLiteral = new TypeLiteral<ListenableFuture<HttpResponse>>() {
731 };
732
733 @SuppressWarnings( { "unchecked", "rawtypes" })
734 public static Key<? extends Function<HttpResponse, ?>> getParserOrThrowException(Method method) {
735 ResponseParser annotation = method.getAnnotation(ResponseParser.class);
736 if (annotation == null) {
737 if (method.getReturnType().equals(void.class)
738 || TypeLiteral.get(method.getGenericReturnType()).equals(futureVoidLiteral)) {
739 return Key.get(ReleasePayloadAndReturn.class);
740 } else if (method.getReturnType().equals(boolean.class) || method.getReturnType().equals(Boolean.class)
741 || TypeLiteral.get(method.getGenericReturnType()).equals(futureBooleanLiteral)) {
742 return Key.get(ReturnTrueIf2xx.class);
743 } else if (method.getReturnType().equals(InputStream.class)
744 || TypeLiteral.get(method.getGenericReturnType()).equals(futureInputStreamLiteral)) {
745 return Key.get(ReturnInputStream.class);
746 } else if (method.getReturnType().equals(HttpResponse.class)
747 || TypeLiteral.get(method.getGenericReturnType()).equals(futureHttpResponseLiteral)) {
748 return Key.get((Class) IdentityFunction.class);
749 } else if (getAcceptHeadersOrNull(method).contains(MediaType.APPLICATION_JSON)) {
750 return getJsonParserKeyForMethod(method);
751 } else if (method.getReturnType().equals(String.class)
752 || TypeLiteral.get(method.getGenericReturnType()).equals(futureStringLiteral)) {
753 return Key.get(ReturnStringIf2xx.class);
754 } else if (method.getReturnType().equals(URI.class)
755 || TypeLiteral.get(method.getGenericReturnType()).equals(futureURILiteral)) {
756 return Key.get(ParseURIFromListOrLocationHeaderIf20x.class);
757 } else {
758 throw new IllegalStateException("You must specify a ResponseParser annotation on: " + method.toString());
759 }
760 }
761 return Key.get(annotation.value());
762 }
763
764 public static Key<? extends Function<HttpResponse, ?>> getJsonParserKeyForMethod(Method method) {
765 Type returnVal = getReturnTypeForMethod(method);
766 return getJsonParserKeyForMethodAnType(method, returnVal);
767 }
768
769 public static Type getReturnTypeForMethod(Method method) {
770 Type returnVal;
771 if (method.getReturnType().getTypeParameters().length == 0) {
772 returnVal = method.getReturnType();
773 } else if (method.getReturnType().equals(ListenableFuture.class)) {
774 ParameterizedType futureType = ((ParameterizedType) method.getGenericReturnType());
775 returnVal = futureType.getActualTypeArguments()[0];
776 if (returnVal instanceof WildcardType)
777 returnVal = WildcardType.class.cast(returnVal).getUpperBounds()[0];
778 } else {
779 returnVal = method.getGenericReturnType();
780 }
781 return returnVal;
782 }
783
784 @SuppressWarnings( { "unchecked", "rawtypes" })
785 public static Key<? extends Function<HttpResponse, ?>> getJsonParserKeyForMethodAnType(Method method, Type returnVal) {
786 ParameterizedType parserType;
787 if (method.isAnnotationPresent(Unwrap.class)) {
788 int depth = method.getAnnotation(Unwrap.class).depth();
789 Class edgeCollection = method.getAnnotation(Unwrap.class).edgeCollection();
790 if (depth == 1 && edgeCollection == Map.class)
791 parserType = Types.newParameterizedType(UnwrapOnlyJsonValue.class, returnVal);
792 else if (depth == 2 && edgeCollection == Map.class)
793 parserType = Types.newParameterizedType(UnwrapOnlyNestedJsonValue.class, returnVal);
794 else if (depth == 2 && edgeCollection == Set.class)
795 parserType = Types.newParameterizedType(UnwrapOnlyJsonValueInSet.class, returnVal);
796 else if (depth == 3 && edgeCollection == Set.class)
797 parserType = Types.newParameterizedType(UnwrapOnlyNestedJsonValueInSet.class, returnVal);
798 else
799 throw new IllegalStateException(String.format("depth(%d) edgeCollection(%s) not yet supported for @Unwrap",
800 depth, edgeCollection));
801 } else {
802 parserType = Types.newParameterizedType(ParseJson.class, returnVal);
803 }
804 return (Key<? extends Function<HttpResponse, ?>>) Key.get(parserType);
805 }
806
807 public static Class<? extends HandlerWithResult<?>> getSaxResponseParserClassOrNull(Method method) {
808 XMLResponseParser annotation = method.getAnnotation(XMLResponseParser.class);
809 if (annotation != null) {
810 return annotation.value();
811 }
812 return null;
813 }
814
815 public org.jclouds.rest.MapBinder getMapPayloadBinderOrNull(Method method, Object... args) {
816 if (args != null) {
817 for (Object arg : args) {
818 if (arg instanceof Object[]) {
819 Object[] postBinders = (Object[]) arg;
820 if (postBinders.length == 0) {
821 } else if (postBinders.length == 1) {
822 if (postBinders[0] instanceof org.jclouds.rest.MapBinder) {
823 org.jclouds.rest.MapBinder binder = (org.jclouds.rest.MapBinder) postBinders[0];
824 injector.injectMembers(binder);
825 return binder;
826 }
827 } else {
828 if (postBinders[0] instanceof org.jclouds.rest.MapBinder) {
829 throw new IllegalArgumentException("we currently do not support multiple varargs postBinders in: "
830 + method.getName());
831 }
832 }
833 } else if (arg instanceof org.jclouds.rest.MapBinder) {
834 org.jclouds.rest.MapBinder binder = (org.jclouds.rest.MapBinder) arg;
835 injector.injectMembers(binder);
836 return binder;
837 }
838 }
839 }
840 if (method.isAnnotationPresent(MapBinder.class)) {
841 return injector.getInstance(method.getAnnotation(MapBinder.class).value());
842 } else if (method.isAnnotationPresent(org.jclouds.rest.annotations.Payload.class)) {
843 return injector.getInstance(BindMapToStringPayload.class);
844 }
845 return null;
846 }
847
848 public static Set<String> getHttpMethods(Method method) {
849 Builder<String> methodsBuilder = ImmutableSet.<String> builder();
850 for (Annotation annotation : method.getAnnotations()) {
851 HttpMethod http = annotation.annotationType().getAnnotation(HttpMethod.class);
852 if (http != null)
853 methodsBuilder.add(http.value());
854 }
855 Set<String> methods = methodsBuilder.build();
856 return (methods.size() == 0) ? null : methods;
857 }
858
859 public String getHttpMethodOrConstantOrThrowException(Method method) {
860 Set<String> requests = getHttpMethods(method);
861 if (requests == null || requests.size() != 1) {
862 throw new IllegalStateException(
863 "You must use at least one, but no more than one http method or pathparam annotation on: "
864 + method.toString());
865 }
866 return requests.iterator().next();
867 }
868
869 public boolean shouldAddHostHeader(Method method) {
870 if (declaring.isAnnotationPresent(VirtualHost.class) || method.isAnnotationPresent(VirtualHost.class)) {
871 return true;
872 }
873 return false;
874 }
875
876 private static final Predicate<Set<?>> notEmpty = new Predicate<Set<?>>() {
877 public boolean apply(Set<?> input) {
878 return input.size() >= 1;
879 }
880 };
881
882 public GeneratedHttpRequest<T> decorateRequest(GeneratedHttpRequest<T> request) {
883 OUTER: for (Entry<Integer, Set<Annotation>> entry : concat(
884 filterValues(methodToIndexOfParamToBinderParamAnnotation.get(request.getJavaMethod()), notEmpty)
885 .entrySet(),
886 filterValues(methodToIndexOfParamToWrapWithAnnotation.get(request.getJavaMethod()), notEmpty).entrySet())) {
887 boolean shouldBreak = false;
888 Annotation annotation = Iterables.get(entry.getValue(), 0);
889 Binder binder;
890 if (annotation instanceof BinderParam)
891 binder = injector.getInstance(BinderParam.class.cast(annotation).value());
892 else
893 binder = injector.getInstance(BindToJsonPayloadWrappedWith.Factory.class).create(
894 WrapWith.class.cast(annotation).value());
895 if (request.getArgs().size() >= entry.getKey() + 1 && request.getArgs().get(entry.getKey()) != null) {
896 Object input;
897 Class<?> parameterType = request.getJavaMethod().getParameterTypes()[entry.getKey()];
898 Class<? extends Object> argType = request.getArgs().get(entry.getKey()).getClass();
899 if (!argType.isArray() && request.getJavaMethod().isVarArgs() && parameterType.isArray()) {
900 int arrayLength = request.getArgs().size() - request.getJavaMethod().getParameterTypes().length + 1;
901 if (arrayLength == 0)
902 break OUTER;
903 input = (Object[]) Array.newInstance(request.getArgs().get(entry.getKey()).getClass(), arrayLength);
904 System.arraycopy(request.getArgs().toArray(), entry.getKey(), input, 0, arrayLength);
905 shouldBreak = true;
906 } else if (argType.isArray() && request.getJavaMethod().isVarArgs() && parameterType.isArray()) {
907 input = request.getArgs().get(entry.getKey());
908 } else {
909 input = request.getArgs().get(entry.getKey());
910 if (input.getClass().isArray()) {
911 Object[] payloadArray = (Object[]) input;
912 input = payloadArray.length > 0 ? payloadArray[0] : null;
913 }
914 }
915 if (input != null) {
916 request = binder.bindToRequest(request, input);
917 }
918 if (shouldBreak)
919 break OUTER;
920 }
921 }
922
923 return request;
924 }
925
926 public static Map<Integer, Set<Annotation>> indexWithOnlyOneAnnotation(Method method, String description,
927 Map<Method, Map<Integer, Set<Annotation>>> toRefine) {
928 Map<Integer, Set<Annotation>> indexToPayloadAnnotation = indexWithAtLeastOneAnnotation(method, toRefine);
929 if (indexToPayloadAnnotation.size() > 1) {
930 throw new IllegalStateException(String.format(
931 "You must not specify more than one %s annotation on: %s; found %s", description, method.toString(),
932 indexToPayloadAnnotation));
933 }
934 return indexToPayloadAnnotation;
935 }
936
937 private static Map<Integer, Set<Annotation>> indexWithAtLeastOneAnnotation(Method method,
938 Map<Method, Map<Integer, Set<Annotation>>> toRefine) {
939 Map<Integer, Set<Annotation>> indexToPayloadAnnotation = filterValues(toRefine.get(method),
940 new Predicate<Set<Annotation>>() {
941 public boolean apply(Set<Annotation> input) {
942 return input.size() == 1;
943 }
944 });
945 return indexToPayloadAnnotation;
946 }
947
948 private HttpRequestOptions findOptionsIn(Method method, Object... args) {
949 for (int index : methodToIndexesOfOptions.get(method)) {
950 if (args.length >= index + 1) {
951 if (args[index] instanceof Object[]) {
952 Object[] options = (Object[]) args[index];
953 if (options.length == 0) {
954 } else if (options.length == 1) {
955 if (options[0] instanceof HttpRequestOptions) {
956 HttpRequestOptions binder = (HttpRequestOptions) options[0];
957 injector.injectMembers(binder);
958 return binder;
959 }
960 } else {
961 if (options[0] instanceof HttpRequestOptions) {
962 throw new IllegalArgumentException("we currently do not support multiple varargs options in: "
963 + method.getName());
964 }
965 }
966 } else {
967 return (HttpRequestOptions) args[index];
968 }
969 }
970 }
971 return null;
972 }
973
974 public Multimap<String, String> buildHeaders(Collection<Entry<String, String>> tokenValues, Method method,
975 final Object... args) {
976 Multimap<String, String> headers = LinkedHashMultimap.create();
977 addHeaderIfAnnotationPresentOnMethod(headers, method, tokenValues);
978 Map<Integer, Set<Annotation>> indexToHeaderParam = methodToIndexOfParamToHeaderParamAnnotations.get(method);
979 for (Entry<Integer, Set<Annotation>> entry : indexToHeaderParam.entrySet()) {
980 for (Annotation key : entry.getValue()) {
981 String value = args[entry.getKey()].toString();
982 value = Strings2.replaceTokens(value, tokenValues);
983 headers.put(((HeaderParam) key).value(), value);
984 }
985 }
986 addProducesIfPresentOnTypeOrMethod(headers, method);
987 addConsumesIfPresentOnTypeOrMethod(headers, method);
988 return headers;
989 }
990
991 void addConsumesIfPresentOnTypeOrMethod(Multimap<String, String> headers, Method method) {
992 List<String> accept = getAcceptHeadersOrNull(method);
993 if (accept.size() > 0)
994 headers.replaceValues(ACCEPT, accept);
995 }
996
997 private static List<String> getAcceptHeadersOrNull(Method method) {
998 List<String> accept = Collections.emptyList();
999 if (method.getDeclaringClass().isAnnotationPresent(Consumes.class)) {
1000 Consumes header = method.getDeclaringClass().getAnnotation(Consumes.class);
1001 accept = asList(header.value());
1002 }
1003 if (method.isAnnotationPresent(Consumes.class)) {
1004 Consumes header = method.getAnnotation(Consumes.class);
1005 accept = asList(header.value());
1006 }
1007 return accept;
1008 }
1009
1010 void addProducesIfPresentOnTypeOrMethod(Multimap<String, String> headers, Method method) {
1011 if (declaring.isAnnotationPresent(Produces.class)) {
1012 Produces header = declaring.getAnnotation(Produces.class);
1013 headers.replaceValues(CONTENT_TYPE, asList(header.value()));
1014 }
1015 if (method.isAnnotationPresent(Produces.class)) {
1016 Produces header = method.getAnnotation(Produces.class);
1017 headers.replaceValues(CONTENT_TYPE, asList(header.value()));
1018 }
1019 }
1020
1021 public void addHeaderIfAnnotationPresentOnMethod(Multimap<String, String> headers, Method method,
1022 Collection<Entry<String, String>> tokenValues) {
1023 if (declaring.isAnnotationPresent(Headers.class)) {
1024 Headers header = declaring.getAnnotation(Headers.class);
1025 addHeader(headers, header, tokenValues);
1026 }
1027 if (method.isAnnotationPresent(Headers.class)) {
1028 Headers header = method.getAnnotation(Headers.class);
1029 addHeader(headers, header, tokenValues);
1030 }
1031 }
1032
1033 private void addHeader(Multimap<String, String> headers, Headers header,
1034 Collection<Entry<String, String>> tokenValues) {
1035 for (int i = 0; i < header.keys().length; i++) {
1036 String value = header.values()[i];
1037 value = Strings2.replaceTokens(value, tokenValues);
1038 headers.put(header.keys()[i], value);
1039 }
1040
1041 }
1042
1043 List<? extends Part> getParts(Method method, Object[] args, Iterable<Entry<String, String>> iterable) {
1044 List<Part> parts = newLinkedList();
1045 Map<Integer, Set<Annotation>> indexToPartParam = methodToIndexOfParamToPartParamAnnotations.get(method);
1046 for (Entry<Integer, Set<Annotation>> entry : indexToPartParam.entrySet()) {
1047 for (Annotation key : entry.getValue()) {
1048 PartParam param = (PartParam) key;
1049 PartOptions options = new PartOptions();
1050 if (!PartParam.NO_CONTENT_TYPE.equals(param.contentType()))
1051 options.contentType(param.contentType());
1052 if (!PartParam.NO_FILENAME.equals(param.filename()))
1053 options.filename(Strings2.replaceTokens(param.filename(), iterable));
1054 Part part = Part.create(param.name(), newPayload(args[entry.getKey()]), options);
1055 parts.add(part);
1056 }
1057 }
1058 return parts;
1059 }
1060
1061 public static HttpRequest findHttpRequestInArgs(Object[] args) {
1062 if (args == null)
1063 return null;
1064 for (int i = 0; i < args.length; i++)
1065 if (args[i] instanceof HttpRequest)
1066 return HttpRequest.class.cast(args[i]);
1067 return null;
1068 }
1069
1070 public static Payload findPayloadInArgs(Object[] args) {
1071 if (args == null)
1072 return null;
1073 for (int i = 0; i < args.length; i++)
1074 if (args[i] instanceof Payload)
1075 return Payload.class.cast(args[i]);
1076 else if (args[i] instanceof PayloadEnclosing)
1077 return PayloadEnclosing.class.cast(args[i]).getPayload();
1078 return null;
1079 }
1080
1081 private Multimap<String, String> getPathParamKeyValues(Method method, Object... args) {
1082 Multimap<String, String> pathParamValues = LinkedHashMultimap.create();
1083 Map<Integer, Set<Annotation>> indexToPathParam = methodToIndexOfParamToPathParamAnnotations.get(method);
1084
1085 Map<Integer, Set<Annotation>> indexToParamExtractor = methodToIndexOfParamToParamParserAnnotations.get(method);
1086 for (Entry<Integer, Set<Annotation>> entry : indexToPathParam.entrySet()) {
1087 for (Annotation key : entry.getValue()) {
1088 Set<Annotation> extractors = indexToParamExtractor.get(entry.getKey());
1089 String paramKey = ((PathParam) key).value();
1090 String paramValue;
1091 if (extractors != null && extractors.size() > 0) {
1092 ParamParser extractor = (ParamParser) extractors.iterator().next();
1093 paramValue = injector.getInstance(extractor.value()).apply(args[entry.getKey()]);
1094 } else {
1095 paramValue = args[entry.getKey()].toString();
1096 }
1097 pathParamValues.put(paramKey, paramValue);
1098 }
1099 }
1100
1101 if (method.isAnnotationPresent(PathParam.class) && method.isAnnotationPresent(ParamParser.class)) {
1102 String paramKey = method.getAnnotation(PathParam.class).value();
1103 String paramValue = injector.getInstance(method.getAnnotation(ParamParser.class).value()).apply(args);
1104 pathParamValues.put(paramKey, paramValue);
1105
1106 }
1107 return pathParamValues;
1108 }
1109
1110 private Multimap<String, String> encodeValues(Multimap<String, String> unencoded, char... skips) {
1111 Multimap<String, String> encoded = LinkedHashMultimap.create();
1112 for (Entry<String, String> entry : unencoded.entries()) {
1113 encoded.put(entry.getKey(), Strings2.urlEncode(entry.getValue(), skips));
1114 }
1115 return encoded;
1116 }
1117
1118 private Multimap<String, String> getMatrixParamKeyValues(Method method, Object... args) {
1119 Multimap<String, String> matrixParamValues = LinkedHashMultimap.create();
1120 Map<Integer, Set<Annotation>> indexToMatrixParam = methodToIndexOfParamToMatrixParamAnnotations.get(method);
1121
1122 Map<Integer, Set<Annotation>> indexToParamExtractor = methodToIndexOfParamToParamParserAnnotations.get(method);
1123 for (Entry<Integer, Set<Annotation>> entry : indexToMatrixParam.entrySet()) {
1124 for (Annotation key : entry.getValue()) {
1125 Set<Annotation> extractors = indexToParamExtractor.get(entry.getKey());
1126 String paramKey = ((MatrixParam) key).value();
1127 String paramValue;
1128 if (extractors != null && extractors.size() > 0) {
1129 ParamParser extractor = (ParamParser) extractors.iterator().next();
1130 paramValue = injector.getInstance(extractor.value()).apply(args[entry.getKey()]);
1131 } else {
1132 paramValue = args[entry.getKey()].toString();
1133 }
1134 matrixParamValues.put(paramKey, paramValue);
1135 }
1136 }
1137
1138 if (method.isAnnotationPresent(MatrixParam.class) && method.isAnnotationPresent(ParamParser.class)) {
1139 String paramKey = method.getAnnotation(MatrixParam.class).value();
1140 String paramValue = injector.getInstance(method.getAnnotation(ParamParser.class).value()).apply(args);
1141 matrixParamValues.put(paramKey, paramValue);
1142
1143 }
1144 return matrixParamValues;
1145 }
1146
1147 private Multimap<String, String> getFormParamKeyValues(Method method, Object... args) {
1148 Multimap<String, String> formParamValues = LinkedHashMultimap.create();
1149 Map<Integer, Set<Annotation>> indexToFormParam = methodToIndexOfParamToFormParamAnnotations.get(method);
1150
1151 Map<Integer, Set<Annotation>> indexToParamExtractor = methodToIndexOfParamToParamParserAnnotations.get(method);
1152 for (Entry<Integer, Set<Annotation>> entry : indexToFormParam.entrySet()) {
1153 for (Annotation key : entry.getValue()) {
1154 Set<Annotation> extractors = indexToParamExtractor.get(entry.getKey());
1155 String paramKey = ((FormParam) key).value();
1156 String paramValue;
1157 if (extractors != null && extractors.size() > 0) {
1158 ParamParser extractor = (ParamParser) extractors.iterator().next();
1159 paramValue = injector.getInstance(extractor.value()).apply(args[entry.getKey()]);
1160 } else {
1161 paramValue = args[entry.getKey()].toString();
1162 }
1163 formParamValues.put(paramKey, paramValue);
1164 }
1165 }
1166
1167 if (method.isAnnotationPresent(FormParam.class) && method.isAnnotationPresent(ParamParser.class)) {
1168 String paramKey = method.getAnnotation(FormParam.class).value();
1169 String paramValue = injector.getInstance(method.getAnnotation(ParamParser.class).value()).apply(args);
1170 formParamValues.put(paramKey, paramValue);
1171
1172 }
1173 return formParamValues;
1174 }
1175
1176 private Multimap<String, String> getQueryParamKeyValues(Method method, Object... args) {
1177 Multimap<String, String> queryParamValues = LinkedHashMultimap.create();
1178 Map<Integer, Set<Annotation>> indexToQueryParam = methodToIndexOfParamToQueryParamAnnotations.get(method);
1179
1180 Map<Integer, Set<Annotation>> indexToParamExtractor = methodToIndexOfParamToParamParserAnnotations.get(method);
1181 for (Entry<Integer, Set<Annotation>> entry : indexToQueryParam.entrySet()) {
1182 for (Annotation key : entry.getValue()) {
1183 Set<Annotation> extractors = indexToParamExtractor.get(entry.getKey());
1184 String paramKey = ((QueryParam) key).value();
1185 String paramValue;
1186 if (extractors != null && extractors.size() > 0) {
1187 ParamParser extractor = (ParamParser) extractors.iterator().next();
1188 paramValue = injector.getInstance(extractor.value()).apply(args[entry.getKey()]);
1189 } else {
1190 paramValue = args[entry.getKey()].toString();
1191 }
1192 queryParamValues.put(paramKey, paramValue);
1193 }
1194 }
1195
1196 if (method.isAnnotationPresent(QueryParam.class) && method.isAnnotationPresent(ParamParser.class)) {
1197 String paramKey = method.getAnnotation(QueryParam.class).value();
1198 String paramValue = injector.getInstance(method.getAnnotation(ParamParser.class).value()).apply(args);
1199 queryParamValues.put(paramKey, paramValue);
1200
1201 }
1202 return queryParamValues;
1203 }
1204
1205 private Map<String, String> buildPostParams(Method method, Object... args) {
1206 Map<String, String> postParams = newHashMap();
1207 Map<Integer, Set<Annotation>> indexToPathParam = methodToIndexOfParamToPostParamAnnotations.get(method);
1208 Map<Integer, Set<Annotation>> indexToParamExtractor = methodToIndexOfParamToParamParserAnnotations.get(method);
1209 for (Entry<Integer, Set<Annotation>> entry : indexToPathParam.entrySet()) {
1210 for (Annotation key : entry.getValue()) {
1211 Set<Annotation> extractors = indexToParamExtractor.get(entry.getKey());
1212 String paramKey = ((PayloadParam) key).value();
1213 String paramValue;
1214 if (extractors != null && extractors.size() > 0) {
1215 ParamParser extractor = (ParamParser) extractors.iterator().next();
1216 paramValue = injector.getInstance(extractor.value()).apply(args[entry.getKey()]);
1217 } else {
1218 paramValue = args[entry.getKey()] != null ? args[entry.getKey()].toString() : null;
1219 }
1220 postParams.put(paramKey, paramValue);
1221
1222 }
1223 }
1224 return postParams;
1225 }
1226
1227 }