• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.inject.throwingproviders;
18 
19 import static com.google.common.base.Preconditions.checkNotNull;
20 
21 import com.google.common.base.Optional;
22 import com.google.common.collect.ImmutableList;
23 import com.google.common.collect.ImmutableSet;
24 import com.google.common.collect.Lists;
25 import com.google.inject.Binder;
26 import com.google.inject.Key;
27 import com.google.inject.Module;
28 import com.google.inject.Provider;
29 import com.google.inject.ProvisionException;
30 import com.google.inject.Scopes;
31 import com.google.inject.TypeLiteral;
32 import com.google.inject.binder.ScopedBindingBuilder;
33 import com.google.inject.internal.UniqueAnnotations;
34 import com.google.inject.spi.Dependency;
35 import com.google.inject.spi.ProviderWithDependencies;
36 import com.google.inject.util.Types;
37 import java.io.Serializable;
38 import java.lang.annotation.Annotation;
39 import java.lang.reflect.Constructor;
40 import java.lang.reflect.InvocationHandler;
41 import java.lang.reflect.Method;
42 import java.lang.reflect.ParameterizedType;
43 import java.lang.reflect.Proxy;
44 import java.lang.reflect.Type;
45 import java.util.List;
46 import java.util.Set;
47 
48 /**
49  * Builds a binding for a {@link CheckedProvider}.
50  *
51  * <p>You can use a fluent API and custom providers:
52  *
53  * <pre><code>ThrowingProviderBinder.create(binder())
54  *    .bind(RemoteProvider.class, Customer.class)
55  *    .to(RemoteCustomerProvider.class)
56  *    .in(RequestScope.class);
57  * </code></pre>
58  *
59  * or, you can use throwing provider methods:
60  *
61  * <pre><code>class MyModule extends AbstractModule {
62  *   configure() {
63  *     install(ThrowingProviderBinder.forModule(this));
64  *   }
65  *
66  *   {@literal @}CheckedProvides(RemoteProvider.class)
67  *   {@literal @}RequestScope
68  *   Customer provideCustomer(FlakyCustomerCreator creator) throws RemoteException {
69  *     return creator.getCustomerOrThrow();
70  *   }
71  * }
72  * </code></pre>
73  *
74  * You also can declare that a CheckedProvider construct a particular class whose constructor throws
75  * an exception:
76  *
77  * <pre><code>ThrowingProviderBinder.create(binder())
78  *    .bind(RemoteProvider.class, Customer.class)
79  *    .providing(CustomerImpl.class)
80  *    .in(RequestScope.class);
81  * </code></pre>
82  *
83  * @author jmourits@google.com (Jerome Mourits)
84  * @author jessewilson@google.com (Jesse Wilson)
85  * @author sameb@google.com (Sam Berlin)
86  */
87 public class ThrowingProviderBinder {
88 
89   private static final TypeLiteral<CheckedProvider<?>> CHECKED_PROVIDER_TYPE =
90       new TypeLiteral<CheckedProvider<?>>() {};
91 
92   private static final TypeLiteral<CheckedProviderMethod<?>> CHECKED_PROVIDER_METHOD_TYPE =
93       new TypeLiteral<CheckedProviderMethod<?>>() {};
94 
95   private final Binder binder;
96 
ThrowingProviderBinder(Binder binder)97   private ThrowingProviderBinder(Binder binder) {
98     this.binder = binder;
99   }
100 
create(Binder binder)101   public static ThrowingProviderBinder create(Binder binder) {
102     return new ThrowingProviderBinder(
103         binder.skipSources(
104             ThrowingProviderBinder.class, ThrowingProviderBinder.SecondaryBinder.class));
105   }
106 
107   /**
108    * Returns a module that installs {@literal @}{@link CheckedProvides} methods.
109    *
110    * @since 3.0
111    */
forModule(Module module)112   public static Module forModule(Module module) {
113     return CheckedProviderMethodsModule.forModule(module);
114   }
115 
116   /** @deprecated Use {@link #bind(Class, Class)} or {@link #bind(Class, TypeLiteral)} instead. */
117   @Deprecated
bind( Class<P> interfaceType, Type clazz)118   public <P extends CheckedProvider> SecondaryBinder<P, ?> bind(
119       Class<P> interfaceType, Type clazz) {
120     return new SecondaryBinder<P, Object>(interfaceType, clazz);
121   }
122 
123   /** @since 4.0 */
bind( Class<P> interfaceType, Class<T> clazz)124   public <P extends CheckedProvider, T> SecondaryBinder<P, T> bind(
125       Class<P> interfaceType, Class<T> clazz) {
126     return new SecondaryBinder<P, T>(interfaceType, clazz);
127   }
128 
129   /** @since 4.0 */
bind( Class<P> interfaceType, TypeLiteral<T> typeLiteral)130   public <P extends CheckedProvider, T> SecondaryBinder<P, T> bind(
131       Class<P> interfaceType, TypeLiteral<T> typeLiteral) {
132     return new SecondaryBinder<P, T>(interfaceType, typeLiteral.getType());
133   }
134 
135   public class SecondaryBinder<P extends CheckedProvider, T> {
136     private final Class<P> interfaceType;
137     private final Type valueType;
138     private final List<Class<? extends Throwable>> exceptionTypes;
139     private final boolean valid;
140 
141     private Class<? extends Annotation> annotationType;
142     private Annotation annotation;
143     private Key<P> interfaceKey;
144     private boolean scopeExceptions = true;
145 
SecondaryBinder(Class<P> interfaceType, Type valueType)146     public SecondaryBinder(Class<P> interfaceType, Type valueType) {
147       this.interfaceType = checkNotNull(interfaceType, "interfaceType");
148       this.valueType = checkNotNull(valueType, "valueType");
149       if (checkInterface()) {
150         this.exceptionTypes = getExceptionType(interfaceType);
151         valid = true;
152       } else {
153         valid = false;
154         this.exceptionTypes = ImmutableList.of();
155       }
156     }
157 
getExceptionTypes()158     List<Class<? extends Throwable>> getExceptionTypes() {
159       return exceptionTypes;
160     }
161 
getKey()162     Key<P> getKey() {
163       return interfaceKey;
164     }
165 
annotatedWith(Class<? extends Annotation> annotationType)166     public SecondaryBinder<P, T> annotatedWith(Class<? extends Annotation> annotationType) {
167       if (!(this.annotationType == null && this.annotation == null)) {
168         throw new IllegalStateException("Cannot set annotation twice");
169       }
170       this.annotationType = annotationType;
171       return this;
172     }
173 
annotatedWith(Annotation annotation)174     public SecondaryBinder<P, T> annotatedWith(Annotation annotation) {
175       if (!(this.annotationType == null && this.annotation == null)) {
176         throw new IllegalStateException("Cannot set annotation twice");
177       }
178       this.annotation = annotation;
179       return this;
180     }
181 
182     /**
183      * Determines if exceptions should be scoped. By default exceptions are scoped.
184      *
185      * @param scopeExceptions whether exceptions should be scoped.
186      * @since 4.0
187      */
scopeExceptions(boolean scopeExceptions)188     public SecondaryBinder<P, T> scopeExceptions(boolean scopeExceptions) {
189       this.scopeExceptions = scopeExceptions;
190       return this;
191     }
192 
to(P target)193     public ScopedBindingBuilder to(P target) {
194       Key<P> targetKey = Key.get(interfaceType, UniqueAnnotations.create());
195       binder.bind(targetKey).toInstance(target);
196       return to(targetKey);
197     }
198 
to(Class<? extends P> targetType)199     public ScopedBindingBuilder to(Class<? extends P> targetType) {
200       return to(Key.get(targetType));
201     }
202 
203     /** @since 4.0 */
providing(Class<? extends T> cxtorClass)204     public ScopedBindingBuilder providing(Class<? extends T> cxtorClass) {
205       return providing(TypeLiteral.get(cxtorClass));
206     }
207 
208     /** @since 4.0 */
209     @SuppressWarnings("unchecked") // safe because this is the cxtor of the literal
providing(TypeLiteral<? extends T> cxtorLiteral)210     public ScopedBindingBuilder providing(TypeLiteral<? extends T> cxtorLiteral) {
211       // Find a constructor that has @ThrowingInject.
212       Constructor<? extends T> cxtor =
213           CheckedProvideUtils.findThrowingConstructor(cxtorLiteral, binder);
214 
215       final Provider<T> typeProvider;
216       final Key<? extends T> typeKey;
217       // If we found an injection point, then bind the cxtor to a unique key
218       if (cxtor != null) {
219         // Validate the exceptions are consistent with the CheckedProvider interface.
220         CheckedProvideUtils.validateExceptions(
221             binder, cxtorLiteral.getExceptionTypes(cxtor), exceptionTypes, interfaceType);
222 
223         typeKey = Key.get(cxtorLiteral, UniqueAnnotations.create());
224         binder.bind(typeKey).toConstructor((Constructor) cxtor).in(Scopes.NO_SCOPE);
225         typeProvider = binder.getProvider((Key<T>) typeKey);
226       } else {
227         // never used, but need it assigned.
228         typeProvider = null;
229         typeKey = null;
230       }
231 
232       // Create a CheckedProvider that calls our cxtor
233       CheckedProvider<T> checkedProvider =
234           new CheckedProviderWithDependencies<T>() {
235             @Override
236             public T get() throws Exception {
237               try {
238                 return typeProvider.get();
239               } catch (ProvisionException pe) {
240                 // Rethrow the provision cause as the actual exception
241                 if (pe.getCause() instanceof Exception) {
242                   throw (Exception) pe.getCause();
243                 } else if (pe.getCause() instanceof Error) {
244                   throw (Error) pe.getCause();
245                 } else {
246                   // If this failed because of multiple reasons (ie, more than
247                   // one dependency failed due to scoping errors), then
248                   // the ProvisionException won't have a cause, so we need
249                   // to rethrow it as-is.
250                   throw pe;
251                 }
252               }
253             }
254 
255             @Override
256             public Set<Dependency<?>> getDependencies() {
257               return ImmutableSet.<Dependency<?>>of(Dependency.get(typeKey));
258             }
259           };
260 
261       Key<CheckedProvider<?>> targetKey =
262           Key.get(CHECKED_PROVIDER_TYPE, UniqueAnnotations.create());
263       binder.bind(targetKey).toInstance(checkedProvider);
264       return toInternal(targetKey);
265     }
266 
toProviderMethod(CheckedProviderMethod<?> target)267     ScopedBindingBuilder toProviderMethod(CheckedProviderMethod<?> target) {
268       Key<CheckedProviderMethod<?>> targetKey =
269           Key.get(CHECKED_PROVIDER_METHOD_TYPE, UniqueAnnotations.create());
270       binder.bind(targetKey).toInstance(target);
271 
272       return toInternal(targetKey);
273     }
274 
275     @SuppressWarnings("unchecked") // P only extends the raw type of CheckedProvider
to(Key<? extends P> targetKey)276     public ScopedBindingBuilder to(Key<? extends P> targetKey) {
277       checkNotNull(targetKey, "targetKey");
278       return toInternal((Key<? extends CheckedProvider<?>>) targetKey);
279     }
280 
toInternal(final Key<? extends CheckedProvider<?>> targetKey)281     private ScopedBindingBuilder toInternal(final Key<? extends CheckedProvider<?>> targetKey) {
282       final Key<Result> resultKey = Key.get(Result.class, UniqueAnnotations.create());
283       // Note that this provider will behave like the final provider Guice creates.
284       // It will especially do scoping if the user adds that.
285       final Provider<Result> resultProvider = binder.getProvider(resultKey);
286       final Provider<? extends CheckedProvider<?>> targetProvider = binder.getProvider(targetKey);
287       interfaceKey = createKey();
288 
289       // don't bother binding the proxy type if this is in an invalid state.
290       if (valid) {
291         binder
292             .bind(interfaceKey)
293             .toProvider(
294                 new ProviderWithDependencies<P>() {
295                   private final P instance =
296                       interfaceType.cast(
297                           Proxy.newProxyInstance(
298                               interfaceType.getClassLoader(),
299                               new Class<?>[] {interfaceType},
300                               new InvocationHandler() {
301                                 @Override
302                                 public Object invoke(Object proxy, Method method, Object[] args)
303                                     throws Throwable {
304                                   // Allow methods like .equals(..), .hashcode(..), .toString(..) to work.
305                                   if (method.getDeclaringClass() == Object.class) {
306                                     return method.invoke(this, args);
307                                   }
308 
309                                   if (scopeExceptions) {
310                                     return resultProvider.get().getOrThrow();
311                                   } else {
312                                     Result result;
313                                     try {
314                                       result = resultProvider.get();
315                                     } catch (ProvisionException pe) {
316                                       Throwable cause = pe.getCause();
317                                       if (cause instanceof ResultException) {
318                                         throw ((ResultException) cause).getCause();
319                                       } else {
320                                         throw pe;
321                                       }
322                                     }
323                                     return result.getOrThrow();
324                                   }
325                                 }
326                               }));
327 
328                   @Override
329                   public P get() {
330                     return instance;
331                   }
332 
333                   @Override
334                   public Set<Dependency<?>> getDependencies() {
335                     return ImmutableSet.<Dependency<?>>of(Dependency.get(resultKey));
336                   }
337                 });
338       }
339 
340       // The provider is unscoped, but the user may apply a scope to it through the
341       // ScopedBindingBuilder this returns.
342       return binder.bind(resultKey).toProvider(createResultProvider(targetKey, targetProvider));
343     }
344 
createResultProvider( final Key<? extends CheckedProvider<?>> targetKey, final Provider<? extends CheckedProvider<?>> targetProvider)345     private ProviderWithDependencies<Result> createResultProvider(
346         final Key<? extends CheckedProvider<?>> targetKey,
347         final Provider<? extends CheckedProvider<?>> targetProvider) {
348       return new ProviderWithDependencies<Result>() {
349         @Override
350         public Result get() {
351           try {
352             return Result.forValue(targetProvider.get().get());
353           } catch (Exception e) {
354             for (Class<? extends Throwable> exceptionType : exceptionTypes) {
355               if (exceptionType.isInstance(e)) {
356                 if (scopeExceptions) {
357                   return Result.forException(e);
358                 } else {
359                   throw new ResultException(e);
360                 }
361               }
362             }
363 
364             if (e instanceof RuntimeException) {
365               throw (RuntimeException) e;
366             } else {
367               // this should never happen
368               throw new RuntimeException(e);
369             }
370           }
371         }
372 
373         @Override
374         public Set<Dependency<?>> getDependencies() {
375           return ImmutableSet.<Dependency<?>>of(Dependency.get(targetKey));
376         }
377       };
378     }
379 
380     /**
381      * Returns the exception type declared to be thrown by the get method of {@code interfaceType}.
382      */
383     private List<Class<? extends Throwable>> getExceptionType(Class<P> interfaceType) {
384       try {
385         Method getMethod = interfaceType.getMethod("get");
386         List<TypeLiteral<?>> exceptionLiterals =
387             TypeLiteral.get(interfaceType).getExceptionTypes(getMethod);
388         List<Class<? extends Throwable>> results = Lists.newArrayList();
389         for (TypeLiteral<?> exLiteral : exceptionLiterals) {
390           results.add(exLiteral.getRawType().asSubclass(Throwable.class));
391         }
392         return results;
393       } catch (SecurityException e) {
394         throw new IllegalStateException("Not allowed to inspect exception types", e);
395       } catch (NoSuchMethodException e) {
396         throw new IllegalStateException("No 'get'method available", e);
397       }
398     }
399 
400     private boolean checkInterface() {
401       try {
402         ProviderChecker.checkInterface(interfaceType, Optional.of(valueType));
403         return true;
404       } catch (IllegalArgumentException e) {
405         binder.addError(e.getMessage());
406         return false;
407       }
408     }
409 
410     @SuppressWarnings({"unchecked"})
411     private Key<P> createKey() {
412       TypeLiteral<P> typeLiteral;
413       if (interfaceType.getTypeParameters().length == 1) {
414         ParameterizedType type =
415             Types.newParameterizedTypeWithOwner(
416                 interfaceType.getEnclosingClass(), interfaceType, valueType);
417         typeLiteral = (TypeLiteral<P>) TypeLiteral.get(type);
418       } else {
419         typeLiteral = TypeLiteral.get(interfaceType);
420       }
421 
422       if (annotation != null) {
423         return Key.get(typeLiteral, annotation);
424 
425       } else if (annotationType != null) {
426         return Key.get(typeLiteral, annotationType);
427 
428       } else {
429         return Key.get(typeLiteral);
430       }
431     }
432   }
433 
434   /**
435    * Represents the returned value from a call to {@link CheckedProvider#get()}. This is the value
436    * that will be scoped by Guice.
437    */
438   static class Result implements Serializable {
439     private static final long serialVersionUID = 0L;
440 
441     private final Object value;
442     private final Exception exception;
443 
444     private Result(Object value, Exception exception) {
445       this.value = value;
446       this.exception = exception;
447     }
448 
449     public static Result forValue(Object value) {
450       return new Result(value, null);
451     }
452 
453     public static Result forException(Exception e) {
454       return new Result(null, e);
455     }
456 
457     public Object getOrThrow() throws Exception {
458       if (exception != null) {
459         throw exception;
460       } else {
461         return value;
462       }
463     }
464   }
465 
466   /**
467    * RuntimeException class to wrap exceptions from the checked provider. The regular guice provider
468    * can throw it and the checked provider proxy extracts the underlying exception and rethrows it.
469    */
470   private static class ResultException extends RuntimeException {
471     ResultException(Exception cause) {
472       super(cause);
473     }
474   }
475 }
476