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