EMMA Coverage Report (generated Wed Oct 26 13:47:17 EDT 2011)
[all classes][org.jclouds.rest.internal]

COVERAGE SUMMARY FOR SOURCE FILE [AsyncRestClientProxy.java]

nameclass, %method, %block, %line, %
AsyncRestClientProxy.java100% (2/2)67%  (6/9)72%  (268/370)62%  (43/69)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class AsyncRestClientProxy100% (1/1)57%  (4/7)72%  (260/362)62%  (42/68)
equals (Object): boolean 0%   (0/1)0%   (0/26)0%   (0/8)
hashCode (): int 0%   (0/1)0%   (0/4)0%   (0/1)
toString (): String 0%   (0/1)0%   (0/11)0%   (0/1)
invoke (Object, Method, Object []): Object 100% (1/1)76%  (82/108)76%  (16/21)
createListenableFuture (Method, Object []): ListenableFuture 100% (1/1)81%  (148/183)59%  (16/27)
<static initializer> 100% (1/1)100% (5/5)100% (1/1)
AsyncRestClientProxy (Injector, AsyncRestClientProxy$Factory, RestAnnotationP... 100% (1/1)100% (25/25)100% (9/9)
     
class AsyncRestClientProxy$1100% (1/1)100% (2/2)100% (8/8)100% (2/2)
AsyncRestClientProxy$1 (): void 100% (1/1)100% (3/3)100% (1/1)
apply (Annotation): boolean 100% (1/1)100% (5/5)100% (1/1)

1/**
2 * Licensed to jclouds, Inc. (jclouds) under one or more
3 * contributor license agreements.  See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership.  jclouds licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License.  You may obtain a copy of the License at
9 *
10 *   http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied.  See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19package org.jclouds.rest.internal;
20 
21/**
22 * Generates RESTful clients from appropriately annotated interfaces.
23 * 
24 * @author Adrian Cole
25 */
26import java.lang.annotation.Annotation;
27import java.lang.reflect.InvocationHandler;
28import java.lang.reflect.Method;
29import java.util.NoSuchElementException;
30import java.util.concurrent.ExecutionException;
31 
32import javax.annotation.Resource;
33import javax.inject.Named;
34import javax.inject.Qualifier;
35import javax.inject.Singleton;
36 
37import org.jclouds.Constants;
38import org.jclouds.concurrent.ExceptionParsingListenableFuture;
39import org.jclouds.http.HttpRequest;
40import org.jclouds.http.HttpResponse;
41import org.jclouds.http.TransformingHttpCommand;
42import org.jclouds.internal.ClassMethodArgs;
43import org.jclouds.logging.Logger;
44import org.jclouds.rest.AuthorizationException;
45import org.jclouds.rest.InvocationContext;
46import org.jclouds.rest.annotations.Delegate;
47import org.jclouds.util.Throwables2;
48 
49import com.google.common.base.Function;
50import com.google.common.base.Predicate;
51import com.google.common.cache.Cache;
52import com.google.common.collect.ImmutableList;
53import com.google.common.collect.Iterables;
54import com.google.common.util.concurrent.Futures;
55import com.google.common.util.concurrent.ListenableFuture;
56import com.google.inject.Inject;
57import com.google.inject.Injector;
58import com.google.inject.Key;
59import com.google.inject.Provides;
60import com.google.inject.ProvisionException;
61import com.google.inject.TypeLiteral;
62 
63@Singleton
64public class AsyncRestClientProxy<T> implements InvocationHandler {
65   private final Injector injector;
66   private final RestAnnotationProcessor<T> annotationProcessor;
67   private final Class<T> declaring;
68   private final Factory commandFactory;
69 
70   /**
71    * maximum duration of an unwrapped http Request
72    */
73   @Inject(optional = true)
74   @Named(Constants.PROPERTY_REQUEST_TIMEOUT)
75   protected long requestTimeoutMilliseconds = 30000;
76 
77   @Resource
78   protected Logger logger = Logger.NULL;
79   private final Cache<ClassMethodArgs, Object> delegateMap;
80 
81   @SuppressWarnings("unchecked")
82   @Inject
83   public AsyncRestClientProxy(Injector injector, Factory factory, RestAnnotationProcessor<T> util,
84            TypeLiteral<T> typeLiteral, @Named("async") Cache<ClassMethodArgs, Object> delegateMap) {
85      this.injector = injector;
86      this.annotationProcessor = util;
87      this.declaring = (Class<T>) typeLiteral.getRawType();
88      this.commandFactory = factory;
89      this.delegateMap = delegateMap;
90   }
91 
92   private static final Predicate<Annotation> isQualifierPresent = new Predicate<Annotation>() {
93 
94      @Override
95      public boolean apply(Annotation input) {
96         return input.annotationType().isAnnotationPresent(Qualifier.class);
97      }
98 
99   };
100 
101   public Object invoke(Object o, Method method, Object[] args) throws Throwable {
102      if (method.getName().equals("equals")) {
103         return this.equals(o);
104      } else if (method.getName().equals("toString")) {
105         return this.toString();
106      } else if (method.getName().equals("hashCode")) {
107         return this.hashCode();
108      } else if (method.isAnnotationPresent(Provides.class)) {
109         try {
110            try {
111               Annotation qualifier = Iterables.find(ImmutableList.copyOf(method.getAnnotations()), isQualifierPresent);
112               return injector.getInstance(Key.get(method.getGenericReturnType(), qualifier));
113            } catch (NoSuchElementException e) {
114               return injector.getInstance(Key.get(method.getGenericReturnType()));
115            }
116         } catch (ProvisionException e) {
117            AuthorizationException aex = Throwables2.getFirstThrowableOfType(e, AuthorizationException.class);
118            if (aex != null)
119               throw aex;
120            throw e;
121         }
122      } else if (method.isAnnotationPresent(Delegate.class)) {
123         return delegateMap.get(new ClassMethodArgs(method.getReturnType(), method, args));
124      } else if (annotationProcessor.getDelegateOrNull(method) != null
125               && ListenableFuture.class.isAssignableFrom(method.getReturnType())) {
126         return createListenableFuture(method, args);
127      } else {
128         throw new RuntimeException("method is intended solely to set constants: " + method);
129      }
130   }
131 
132   @SuppressWarnings( { "unchecked", "rawtypes" })
133   private ListenableFuture<?> createListenableFuture(Method method, Object[] args) throws ExecutionException {
134      method = annotationProcessor.getDelegateOrNull(method);
135      logger.trace("Converting %s.%s", declaring.getSimpleName(), method.getName());
136      Function<Exception, ?> exceptionParser = annotationProcessor
137               .createExceptionParserOrThrowResourceNotFoundOn404IfNoAnnotation(method);
138      // in case there is an exception creating the request, we should at least
139      // pass in args
140      if (exceptionParser instanceof InvocationContext) {
141         ((InvocationContext) exceptionParser).setContext((HttpRequest) null);
142      }
143      ListenableFuture<?> result;
144      try {
145         GeneratedHttpRequest<T> request = annotationProcessor.createRequest(method, args);
146         if (exceptionParser instanceof InvocationContext) {
147            ((InvocationContext) exceptionParser).setContext(request);
148         }
149         logger.trace("Converted %s.%s to %s", declaring.getSimpleName(), method.getName(), request.getRequestLine());
150 
151         Function<HttpResponse, ?> transformer = annotationProcessor.createResponseParser(method, request);
152         logger.trace("Response from %s.%s is parsed by %s", declaring.getSimpleName(), method.getName(), transformer
153                  .getClass().getSimpleName());
154 
155         logger.debug("Invoking %s.%s", declaring.getSimpleName(), method.getName());
156         result = commandFactory.create(request, transformer).execute();
157 
158      } catch (RuntimeException e) {
159         AuthorizationException aex = Throwables2.getFirstThrowableOfType(e, AuthorizationException.class);
160         if (aex != null)
161            e = aex;
162         if (exceptionParser != null) {
163            try {
164               return Futures.immediateFuture(exceptionParser.apply(e));
165            } catch (Exception ex) {
166               return Futures.immediateFailedFuture(ex);
167            }
168         }
169         return Futures.immediateFailedFuture(e);
170      }
171 
172      if (exceptionParser != null) {
173         logger.trace("Exceptions from %s.%s are parsed by %s", declaring.getSimpleName(), method.getName(),
174                  exceptionParser.getClass().getSimpleName());
175         result = new ExceptionParsingListenableFuture(result, exceptionParser);
176      }
177      return result;
178   }
179 
180   public static interface Factory {
181      public TransformingHttpCommand<?> create(HttpRequest request, Function<HttpResponse, ?> transformer);
182   }
183 
184   @Override
185   public boolean equals(Object obj) {
186      if (obj == null || !(obj instanceof AsyncRestClientProxy<?>))
187         return false;
188      AsyncRestClientProxy<?> other = (AsyncRestClientProxy<?>) obj;
189      if (other == this)
190         return true;
191      if (other.declaring != this.declaring)
192         return false;
193      return super.equals(obj);
194   }
195 
196   @Override
197   public int hashCode() {
198      return declaring.hashCode();
199   }
200 
201   public String toString() {
202      return "Client Proxy for :" + declaring.getName();
203   }
204}

[all classes][org.jclouds.rest.internal]
EMMA 2.0.5312 (C) Vladimir Roubtsov