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

COVERAGE SUMMARY FOR SOURCE FILE [SyncProxy.java]

nameclass, %method, %block, %line, %
SyncProxy.java100% (1/1)80%  (8/10)86%  (335/389)76%  (52/68)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class SyncProxy100% (1/1)80%  (8/10)86%  (335/389)76%  (52/68)
equals (Object): boolean 0%   (0/1)0%   (0/26)0%   (0/8)
hashCode (): int 0%   (0/1)0%   (0/4)0%   (0/1)
invoke (Object, Method, Object []): Object 100% (1/1)82%  (97/119)70%  (14/20)
overrideTimeout (Method, Map): Long 100% (1/1)95%  (38/40)86%  (6/7)
<static initializer> 100% (1/1)100% (5/5)100% (1/1)
SyncProxy (Class, Object, Cache, Map, Map): void 100% (1/1)100% (128/128)100% (22/22)
convertToNanos (Timeout): long 100% (1/1)100% (9/9)100% (2/2)
getTimeout (Method, long, Map): Long 100% (1/1)100% (28/28)100% (5/5)
proxy (Class, Object, Cache, Map, Map): Object 100% (1/1)100% (18/18)100% (1/1)
toString (): String 100% (1/1)100% (12/12)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.concurrent.internal;
20 
21import static com.google.common.base.Preconditions.checkState;
22 
23import java.lang.reflect.InvocationHandler;
24import java.lang.reflect.Method;
25import java.lang.reflect.Proxy;
26import java.util.Arrays;
27import java.util.Map;
28import java.util.Set;
29import java.util.concurrent.ExecutionException;
30import java.util.concurrent.TimeUnit;
31 
32import javax.inject.Inject;
33import javax.inject.Named;
34 
35import com.google.common.collect.Iterables;
36import com.google.common.collect.Multimap;
37import org.jclouds.concurrent.Timeout;
38import org.jclouds.internal.ClassMethodArgs;
39import org.jclouds.rest.annotations.Delegate;
40import org.jclouds.util.Throwables2;
41 
42import com.google.common.cache.Cache;
43import com.google.common.collect.ImmutableSet;
44import com.google.common.collect.Maps;
45import com.google.common.util.concurrent.ListenableFuture;
46import com.google.inject.ProvisionException;
47 
48/**
49 * Generates RESTful clients from appropriately annotated interfaces.
50 * 
51 * @author Adrian Cole
52 */
53@SuppressWarnings("deprecation")
54public class SyncProxy implements InvocationHandler {
55 
56   @SuppressWarnings("unchecked")
57   public static <T> T proxy(Class<T> clazz, Object async,
58         @Named("sync") Cache<ClassMethodArgs, Object> delegateMap,
59         Map<Class<?>, Class<?>> sync2Async, Map<String, Long> timeouts) throws IllegalArgumentException, SecurityException,
60         NoSuchMethodException {
61      return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class<?>[] { clazz },
62              new SyncProxy(clazz, async, delegateMap, sync2Async, timeouts));
63   }
64 
65   private final Object delegate;
66   private final Class<?> declaring;
67   private final Map<Method, Method> methodMap;
68   private final Map<Method, Method> syncMethodMap;
69   private final Map<Method, Long> timeoutMap;
70   private final Cache<ClassMethodArgs, Object> delegateMap;
71   private final Map<Class<?>, Class<?>> sync2Async;
72   private static final Set<Method> objectMethods = ImmutableSet.of(Object.class.getMethods());
73 
74   @Inject
75   private SyncProxy(Class<?> declaring, Object async,
76         @Named("sync") Cache<ClassMethodArgs, Object> delegateMap, Map<Class<?>,
77           Class<?>> sync2Async, final Map<String, Long> timeouts)
78         throws SecurityException, NoSuchMethodException {
79      this.delegateMap = delegateMap;
80      this.delegate = async;
81      this.declaring = declaring;
82      this.sync2Async = sync2Async;
83      if (!declaring.isAnnotationPresent(Timeout.class)) {
84         throw new IllegalArgumentException(String.format("type %s does not specify a default @Timeout", declaring));
85      }
86      Timeout typeTimeout = declaring.getAnnotation(Timeout.class);
87      long typeNanos = convertToNanos(typeTimeout);
88 
89      methodMap = Maps.newHashMap();
90      syncMethodMap = Maps.newHashMap();
91      timeoutMap = Maps.newHashMap();
92      for (Method method : declaring.getMethods()) {
93         if (!objectMethods.contains(method)) {
94            Method delegatedMethod = delegate.getClass().getMethod(method.getName(), method.getParameterTypes());
95            if (!Arrays.equals(delegatedMethod.getExceptionTypes(), method.getExceptionTypes()))
96               throw new IllegalArgumentException(String.format(
97                     "method %s has different typed exceptions than delegated method %s", method, delegatedMethod));
98            if (delegatedMethod.getReturnType().isAssignableFrom(ListenableFuture.class)) {
99               timeoutMap.put(method, getTimeout(method, typeNanos, timeouts));
100               methodMap.put(method, delegatedMethod);
101            } else {
102               syncMethodMap.put(method, delegatedMethod);
103            }
104         }
105      }
106   }
107 
108   private Long getTimeout(Method method, long typeNanos, final Map<String,Long> timeouts) {
109      Long timeout = overrideTimeout(method, timeouts);
110      if (timeout == null && method.isAnnotationPresent(Timeout.class)) {
111         Timeout methodTimeout = method.getAnnotation(Timeout.class);
112         timeout = convertToNanos(methodTimeout);
113      }
114      return timeout != null ? timeout : typeNanos;
115 
116   }
117 
118   static long convertToNanos(Timeout timeout) {
119      long methodNanos = TimeUnit.NANOSECONDS.convert(timeout.duration(), timeout.timeUnit());
120      return methodNanos;
121   }
122 
123   public Object invoke(Object o, Method method, Object[] args) throws Throwable {
124      if (method.getName().equals("equals")) {
125         return this.equals(o);
126      } else if (method.getName().equals("hashCode")) {
127         return this.hashCode();
128      } else if (method.getName().equals("toString")) {
129         return this.toString();
130      } else if (method.isAnnotationPresent(Delegate.class)) {
131         Class<?> asyncClass = sync2Async.get(method.getReturnType());
132         checkState(asyncClass != null, "please configure corresponding async class for " + method.getReturnType()
133               + " in your RestClientModule");
134         Object returnVal = delegateMap.get(new ClassMethodArgs(asyncClass, method, args));
135         return returnVal;
136      } else if (syncMethodMap.containsKey(method)) {
137         return syncMethodMap.get(method).invoke(delegate, args);
138      } else {
139         try {
140            return ((ListenableFuture<?>) methodMap.get(method).invoke(delegate, args)).get(timeoutMap.get(method),
141                  TimeUnit.NANOSECONDS);
142         } catch (ProvisionException e) {
143            throw Throwables2.returnFirstExceptionIfInListOrThrowStandardExceptionOrCause(method.getExceptionTypes(), e);
144         } catch (ExecutionException e) {
145            throw Throwables2.returnFirstExceptionIfInListOrThrowStandardExceptionOrCause(method.getExceptionTypes(), e);
146         } catch (Exception e) {
147            throw Throwables2.returnFirstExceptionIfInListOrThrowStandardExceptionOrCause(method.getExceptionTypes(), e);
148         }
149      }
150   }
151 
152   // override timeout by values configured in properties(in ms)
153   private Long overrideTimeout(final Method method, final Map<String, Long> timeouts) {
154      if (timeouts == null) {
155         return null;
156      }
157      final String className = declaring.getSimpleName();
158      Long timeout = timeouts.get(className + "." + method.getName());
159      if (timeout == null) {
160         timeout = timeouts.get(className);
161      }
162      return timeout != null ? TimeUnit.MILLISECONDS.toNanos(timeout) : null;
163   }
164 
165   @Override
166   public boolean equals(Object obj) {
167      if (obj == null || !(obj instanceof SyncProxy))
168         return false;
169      SyncProxy other = (SyncProxy) obj;
170      if (other == this)
171         return true;
172      if (other.declaring != this.declaring)
173         return false;
174      return super.equals(obj);
175   }
176 
177   @Override
178   public int hashCode() {
179      return declaring.hashCode();
180   }
181 
182   public String toString() {
183      return "Sync Proxy for: " + delegate.getClass().getSimpleName();
184   }
185}

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