1/** 2 * Copyright (C) 2008 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 17package com.google.inject.assistedinject; 18 19import static com.google.common.base.Preconditions.checkState; 20import static com.google.common.collect.Iterables.getOnlyElement; 21 22import com.google.common.base.Objects; 23import com.google.common.collect.HashMultimap; 24import com.google.common.collect.ImmutableList; 25import com.google.common.collect.ImmutableMap; 26import com.google.common.collect.ImmutableSet; 27import com.google.common.collect.Iterables; 28import com.google.common.collect.Lists; 29import com.google.common.collect.Multimap; 30import com.google.common.collect.Sets; 31import com.google.inject.AbstractModule; 32import com.google.inject.Binder; 33import com.google.inject.Binding; 34import com.google.inject.ConfigurationException; 35import com.google.inject.Inject; 36import com.google.inject.Injector; 37import com.google.inject.Key; 38import com.google.inject.Module; 39import com.google.inject.Provider; 40import com.google.inject.ProvisionException; 41import com.google.inject.Scopes; 42import com.google.inject.TypeLiteral; 43import com.google.inject.internal.Annotations; 44import com.google.inject.internal.BytecodeGen; 45import com.google.inject.internal.Errors; 46import com.google.inject.internal.ErrorsException; 47import com.google.inject.internal.UniqueAnnotations; 48import com.google.inject.internal.util.Classes; 49import com.google.inject.spi.BindingTargetVisitor; 50import com.google.inject.spi.Dependency; 51import com.google.inject.spi.HasDependencies; 52import com.google.inject.spi.InjectionPoint; 53import com.google.inject.spi.Message; 54import com.google.inject.spi.ProviderInstanceBinding; 55import com.google.inject.spi.ProviderWithExtensionVisitor; 56import com.google.inject.spi.Toolable; 57import com.google.inject.util.Providers; 58 59import java.lang.annotation.Annotation; 60import java.lang.reflect.Constructor; 61import java.lang.reflect.InvocationHandler; 62import java.lang.reflect.InvocationTargetException; 63import java.lang.reflect.Method; 64import java.lang.reflect.Modifier; 65import java.lang.reflect.Proxy; 66import java.util.Arrays; 67import java.util.Collection; 68import java.util.Collections; 69import java.util.HashSet; 70import java.util.List; 71import java.util.Map; 72import java.util.Set; 73import java.util.logging.Level; 74import java.util.logging.Logger; 75 76/** 77 * The newer implementation of factory provider. This implementation uses a child injector to 78 * create values. 79 * 80 * @author jessewilson@google.com (Jesse Wilson) 81 * @author dtm@google.com (Daniel Martin) 82 * @author schmitt@google.com (Peter Schmitt) 83 * @author sameb@google.com (Sam Berlin) 84 */ 85final class FactoryProvider2 <F> implements InvocationHandler, 86 ProviderWithExtensionVisitor<F>, HasDependencies, AssistedInjectBinding<F> { 87 88 /** A constant annotation to denote the return value, instead of creating a new one each time. */ 89 static final Annotation RETURN_ANNOTATION = UniqueAnnotations.create(); 90 91 // use the logger under a well-known name, not FactoryProvider2 92 static final Logger logger = Logger.getLogger(AssistedInject.class.getName()); 93 94 /** if a factory method parameter isn't annotated, it gets this annotation. */ 95 static final Assisted DEFAULT_ANNOTATION = new Assisted() { 96 public String value() { 97 return ""; 98 } 99 100 public Class<? extends Annotation> annotationType() { 101 return Assisted.class; 102 } 103 104 @Override public boolean equals(Object o) { 105 return o instanceof Assisted && ((Assisted) o).value().isEmpty(); 106 } 107 108 @Override public int hashCode() { 109 return 127 * "value".hashCode() ^ "".hashCode(); 110 } 111 112 @Override public String toString() { 113 return "@" + Assisted.class.getName() + "(value=)"; 114 } 115 }; 116 117 /** All the data necessary to perform an assisted inject. */ 118 private static class AssistData implements AssistedMethod { 119 /** the constructor the implementation is constructed with. */ 120 final Constructor<?> constructor; 121 /** the return type in the factory method that the constructor is bound to. */ 122 final Key<?> returnType; 123 /** the parameters in the factory method associated with this data. */ 124 final ImmutableList<Key<?>> paramTypes; 125 /** the type of the implementation constructed */ 126 final TypeLiteral<?> implementationType; 127 128 /** All non-assisted dependencies required by this method. */ 129 final Set<Dependency<?>> dependencies; 130 /** The factory method associated with this data*/ 131 final Method factoryMethod; 132 133 /** true if {@link #isValidForOptimizedAssistedInject} returned true. */ 134 final boolean optimized; 135 /** the list of optimized providers, empty if not optimized. */ 136 final List<ThreadLocalProvider> providers; 137 /** used to perform optimized factory creations. */ 138 volatile Binding<?> cachedBinding; // TODO: volatile necessary? 139 140 AssistData(Constructor<?> constructor, Key<?> returnType, ImmutableList<Key<?>> paramTypes, 141 TypeLiteral<?> implementationType, Method factoryMethod, 142 Set<Dependency<?>> dependencies, 143 boolean optimized, List<ThreadLocalProvider> providers) { 144 this.constructor = constructor; 145 this.returnType = returnType; 146 this.paramTypes = paramTypes; 147 this.implementationType = implementationType; 148 this.factoryMethod = factoryMethod; 149 this.dependencies = dependencies; 150 this.optimized = optimized; 151 this.providers = providers; 152 } 153 154 @Override 155 public String toString() { 156 return Objects.toStringHelper(getClass()) 157 .add("ctor", constructor) 158 .add("return type", returnType) 159 .add("param type", paramTypes) 160 .add("implementation type", implementationType) 161 .add("dependencies", dependencies) 162 .add("factory method", factoryMethod) 163 .add("optimized", optimized) 164 .add("providers", providers) 165 .add("cached binding", cachedBinding) 166 .toString(); 167 } 168 169 public Set<Dependency<?>> getDependencies() { 170 return dependencies; 171 } 172 173 public Method getFactoryMethod() { 174 return factoryMethod; 175 } 176 177 public Constructor<?> getImplementationConstructor() { 178 return constructor; 179 } 180 181 public TypeLiteral<?> getImplementationType() { 182 return implementationType; 183 } 184 } 185 186 /** Mapping from method to the data about how the method will be assisted. */ 187 private final ImmutableMap<Method, AssistData> assistDataByMethod; 188 189 /** Mapping from method to method handle, for generated default methods. */ 190 private final ImmutableMap<Method, MethodHandleWrapper> methodHandleByMethod; 191 192 /** the hosting injector, or null if we haven't been initialized yet */ 193 private Injector injector; 194 195 /** the factory interface, implemented and provided */ 196 private final F factory; 197 198 /** The key that this is bound to. */ 199 private final Key<F> factoryKey; 200 201 /** The binding collector, for equality/hashing purposes. */ 202 private final BindingCollector collector; 203 204 /** 205 * @param factoryKey a key for a Java interface that defines one or more create methods. 206 * @param collector binding configuration that maps method return types to 207 * implementation types. 208 */ 209 FactoryProvider2(Key<F> factoryKey, BindingCollector collector) { 210 this.factoryKey = factoryKey; 211 this.collector = collector; 212 213 TypeLiteral<F> factoryType = factoryKey.getTypeLiteral(); 214 Errors errors = new Errors(); 215 216 @SuppressWarnings("unchecked") // we imprecisely treat the class literal of T as a Class<T> 217 Class<F> factoryRawType = (Class<F>) (Class<?>) factoryType.getRawType(); 218 219 try { 220 if(!factoryRawType.isInterface()) { 221 throw errors.addMessage("%s must be an interface.", factoryRawType).toException(); 222 } 223 224 Multimap<String, Method> defaultMethods = HashMultimap.create(); 225 Multimap<String, Method> otherMethods = HashMultimap.create(); 226 ImmutableMap.Builder<Method, AssistData> assistDataBuilder = ImmutableMap.builder(); 227 // TODO: also grab methods from superinterfaces 228 for (Method method : factoryRawType.getMethods()) { 229 // Skip default methods that java8 may have created. 230 if (isDefault(method) && (method.isBridge() || method.isSynthetic())) { 231 // Even synthetic default methods need the return type validation... 232 // unavoidable consequence of javac8. :-( 233 validateFactoryReturnType(errors, method.getReturnType(), factoryRawType); 234 defaultMethods.put(method.getName(), method); 235 continue; 236 } 237 otherMethods.put(method.getName(), method); 238 239 TypeLiteral<?> returnTypeLiteral = factoryType.getReturnType(method); 240 Key<?> returnType; 241 try { 242 returnType = Annotations.getKey(returnTypeLiteral, method, method.getAnnotations(), errors); 243 } catch(ConfigurationException ce) { 244 // If this was an error due to returnTypeLiteral not being specified, rephrase 245 // it as our factory not being specified, so it makes more sense to users. 246 if(isTypeNotSpecified(returnTypeLiteral, ce)) { 247 throw errors.keyNotFullySpecified(TypeLiteral.get(factoryRawType)).toException(); 248 } else { 249 throw ce; 250 } 251 } 252 validateFactoryReturnType(errors, returnType.getTypeLiteral().getRawType(), factoryRawType); 253 List<TypeLiteral<?>> params = factoryType.getParameterTypes(method); 254 Annotation[][] paramAnnotations = method.getParameterAnnotations(); 255 int p = 0; 256 List<Key<?>> keys = Lists.newArrayList(); 257 for (TypeLiteral<?> param : params) { 258 Key<?> paramKey = Annotations.getKey(param, method, paramAnnotations[p++], errors); 259 Class<?> underlylingType = paramKey.getTypeLiteral().getRawType(); 260 if (underlylingType.equals(Provider.class) 261 || underlylingType.equals(javax.inject.Provider.class)) { 262 errors.addMessage("A Provider may not be a type in a factory method of an AssistedInject." 263 + "\n Offending instance is parameter [%s] with key [%s] on method [%s]", 264 p, paramKey, method); 265 } 266 keys.add(assistKey(method, paramKey, errors)); 267 } 268 ImmutableList<Key<?>> immutableParamList = ImmutableList.copyOf(keys); 269 270 // try to match up the method to the constructor 271 TypeLiteral<?> implementation = collector.getBindings().get(returnType); 272 if(implementation == null) { 273 implementation = returnType.getTypeLiteral(); 274 } 275 Class<? extends Annotation> scope = 276 Annotations.findScopeAnnotation(errors, implementation.getRawType()); 277 if (scope != null) { 278 errors.addMessage("Found scope annotation [%s] on implementation class " 279 + "[%s] of AssistedInject factory [%s].\nThis is not allowed, please" 280 + " remove the scope annotation.", 281 scope, implementation.getRawType(), factoryType); 282 } 283 284 InjectionPoint ctorInjectionPoint; 285 try { 286 ctorInjectionPoint = 287 findMatchingConstructorInjectionPoint(method, returnType, implementation, immutableParamList); 288 } catch(ErrorsException ee) { 289 errors.merge(ee.getErrors()); 290 continue; 291 } 292 293 Constructor<?> constructor = (Constructor<?>) ctorInjectionPoint.getMember(); 294 List<ThreadLocalProvider> providers = Collections.emptyList(); 295 Set<Dependency<?>> deps = getDependencies(ctorInjectionPoint, implementation); 296 boolean optimized = false; 297 // Now go through all dependencies of the implementation and see if it is OK to 298 // use an optimized form of assistedinject2. The optimized form requires that 299 // all injections directly inject the object itself (and not a Provider of the object, 300 // or an Injector), because it caches a single child injector and mutates the Provider 301 // of the arguments in a ThreadLocal. 302 if(isValidForOptimizedAssistedInject(deps, implementation.getRawType(), factoryType)) { 303 ImmutableList.Builder<ThreadLocalProvider> providerListBuilder = ImmutableList.builder(); 304 for(int i = 0; i < params.size(); i++) { 305 providerListBuilder.add(new ThreadLocalProvider()); 306 } 307 providers = providerListBuilder.build(); 308 optimized = true; 309 } 310 311 AssistData data = new AssistData(constructor, 312 returnType, 313 immutableParamList, 314 implementation, 315 method, 316 removeAssistedDeps(deps), 317 optimized, 318 providers); 319 assistDataBuilder.put(method, data); 320 } 321 322 factory = factoryRawType.cast(Proxy.newProxyInstance( 323 BytecodeGen.getClassLoader(factoryRawType), new Class<?>[] {factoryRawType}, this)); 324 325 // Now go back through default methods. Try to use MethodHandles to make things 326 // work. If that doesn't work, fallback to trying to find compatible method 327 // signatures. 328 Map<Method, AssistData> dataSoFar = assistDataBuilder.build(); 329 ImmutableMap.Builder<Method, MethodHandleWrapper> methodHandleBuilder = ImmutableMap.builder(); 330 for (Map.Entry<String, Method> entry : defaultMethods.entries()) { 331 Method defaultMethod = entry.getValue(); 332 MethodHandleWrapper handle = MethodHandleWrapper.create(defaultMethod, factory); 333 if (handle != null) { 334 methodHandleBuilder.put(defaultMethod, handle); 335 } else { 336 boolean foundMatch = false; 337 for (Method otherMethod : otherMethods.get(defaultMethod.getName())) { 338 if (dataSoFar.containsKey(otherMethod) && isCompatible(defaultMethod, otherMethod)) { 339 if (foundMatch) { 340 errors.addMessage("Generated default method %s with parameters %s is" 341 + " signature-compatible with more than one non-default method." 342 + " Unable to create factory. As a workaround, remove the override" 343 + " so javac stops generating a default method.", 344 defaultMethod, Arrays.asList(defaultMethod.getParameterTypes())); 345 } else { 346 assistDataBuilder.put(defaultMethod, dataSoFar.get(otherMethod)); 347 foundMatch = true; 348 } 349 } 350 } 351 if (!foundMatch) { 352 throw new IllegalStateException("Can't find method compatible with: " + defaultMethod); 353 } 354 } 355 } 356 357 // If we generated any errors (from finding matching constructors, for instance), throw an exception. 358 if(errors.hasErrors()) { 359 throw errors.toException(); 360 } 361 362 assistDataByMethod = assistDataBuilder.build(); 363 methodHandleByMethod = methodHandleBuilder.build(); 364 } catch (ErrorsException e) { 365 throw new ConfigurationException(e.getErrors().getMessages()); 366 } 367 } 368 369 static boolean isDefault(Method method) { 370 // Per the javadoc, default methods are non-abstract, public, non-static. 371 // They're also in interfaces, but we can guarantee that already since we only act 372 // on interfaces. 373 return (method.getModifiers() & (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC)) 374 == Modifier.PUBLIC; 375 } 376 377 private boolean isCompatible(Method src, Method dst) { 378 if (!src.getReturnType().isAssignableFrom(dst.getReturnType())) { 379 return false; 380 } 381 Class<?>[] srcParams = src.getParameterTypes(); 382 Class<?>[] dstParams = dst.getParameterTypes(); 383 if (srcParams.length != dstParams.length) { 384 return false; 385 } 386 for (int i = 0; i < srcParams.length; i++) { 387 if (!srcParams[i].isAssignableFrom(dstParams[i])) { 388 return false; 389 } 390 } 391 return true; 392 } 393 394 public F get() { 395 return factory; 396 } 397 398 public Set<Dependency<?>> getDependencies() { 399 Set<Dependency<?>> combinedDeps = new HashSet<Dependency<?>>(); 400 for(AssistData data : assistDataByMethod.values()) { 401 combinedDeps.addAll(data.dependencies); 402 } 403 return ImmutableSet.copyOf(combinedDeps); 404 } 405 406 public Key<F> getKey() { 407 return factoryKey; 408 } 409 410 // Safe cast because values are typed to AssistedData, which is an AssistedMethod, and 411 // the collection is immutable. 412 @SuppressWarnings("unchecked") 413 public Collection<AssistedMethod> getAssistedMethods() { 414 return (Collection<AssistedMethod>) (Collection<?>) assistDataByMethod.values(); 415 } 416 417 @SuppressWarnings("unchecked") 418 public <T, V> V acceptExtensionVisitor(BindingTargetVisitor<T, V> visitor, 419 ProviderInstanceBinding<? extends T> binding) { 420 if (visitor instanceof AssistedInjectTargetVisitor) { 421 return ((AssistedInjectTargetVisitor<T, V>)visitor).visit((AssistedInjectBinding<T>)this); 422 } 423 return visitor.visit(binding); 424 } 425 426 private void validateFactoryReturnType(Errors errors, Class<?> returnType, Class<?> factoryType) { 427 if (Modifier.isPublic(factoryType.getModifiers()) 428 && !Modifier.isPublic(returnType.getModifiers())) { 429 errors.addMessage("%s is public, but has a method that returns a non-public type: %s. " 430 + "Due to limitations with java.lang.reflect.Proxy, this is not allowed. " 431 + "Please either make the factory non-public or the return type public.", 432 factoryType, returnType); 433 } 434 } 435 436 /** 437 * Returns true if the ConfigurationException is due to an error of TypeLiteral not being fully 438 * specified. 439 */ 440 private boolean isTypeNotSpecified(TypeLiteral<?> typeLiteral, ConfigurationException ce) { 441 Collection<Message> messages = ce.getErrorMessages(); 442 if (messages.size() == 1) { 443 Message msg = Iterables.getOnlyElement( 444 new Errors().keyNotFullySpecified(typeLiteral).getMessages()); 445 return msg.getMessage().equals(Iterables.getOnlyElement(messages).getMessage()); 446 } else { 447 return false; 448 } 449 } 450 451 /** 452 * Finds a constructor suitable for the method. If the implementation contained any constructors 453 * marked with {@link AssistedInject}, this requires all {@link Assisted} parameters to exactly 454 * match the parameters (in any order) listed in the method. Otherwise, if no 455 * {@link AssistedInject} constructors exist, this will default to looking for an 456 * {@literal @}{@link Inject} constructor. 457 */ 458 private <T> InjectionPoint findMatchingConstructorInjectionPoint( 459 Method method, Key<?> returnType, TypeLiteral<T> implementation, List<Key<?>> paramList) 460 throws ErrorsException { 461 Errors errors = new Errors(method); 462 if(returnType.getTypeLiteral().equals(implementation)) { 463 errors = errors.withSource(implementation); 464 } else { 465 errors = errors.withSource(returnType).withSource(implementation); 466 } 467 468 Class<?> rawType = implementation.getRawType(); 469 if (Modifier.isInterface(rawType.getModifiers())) { 470 errors.addMessage( 471 "%s is an interface, not a concrete class. Unable to create AssistedInject factory.", 472 implementation); 473 throw errors.toException(); 474 } else if (Modifier.isAbstract(rawType.getModifiers())) { 475 errors.addMessage( 476 "%s is abstract, not a concrete class. Unable to create AssistedInject factory.", 477 implementation); 478 throw errors.toException(); 479 } else if (Classes.isInnerClass(rawType)) { 480 errors.cannotInjectInnerClass(rawType); 481 throw errors.toException(); 482 } 483 484 Constructor<?> matchingConstructor = null; 485 boolean anyAssistedInjectConstructors = false; 486 // Look for AssistedInject constructors... 487 for (Constructor<?> constructor : rawType.getDeclaredConstructors()) { 488 if (constructor.isAnnotationPresent(AssistedInject.class)) { 489 anyAssistedInjectConstructors = true; 490 if (constructorHasMatchingParams(implementation, constructor, paramList, errors)) { 491 if (matchingConstructor != null) { 492 errors 493 .addMessage( 494 "%s has more than one constructor annotated with @AssistedInject" 495 + " that matches the parameters in method %s. Unable to create " 496 + "AssistedInject factory.", 497 implementation, method); 498 throw errors.toException(); 499 } else { 500 matchingConstructor = constructor; 501 } 502 } 503 } 504 } 505 506 if(!anyAssistedInjectConstructors) { 507 // If none existed, use @Inject. 508 try { 509 return InjectionPoint.forConstructorOf(implementation); 510 } catch(ConfigurationException e) { 511 errors.merge(e.getErrorMessages()); 512 throw errors.toException(); 513 } 514 } else { 515 // Otherwise, use it or fail with a good error message. 516 if(matchingConstructor != null) { 517 // safe because we got the constructor from this implementation. 518 @SuppressWarnings("unchecked") 519 InjectionPoint ip = InjectionPoint.forConstructor( 520 (Constructor<? super T>) matchingConstructor, implementation); 521 return ip; 522 } else { 523 errors.addMessage( 524 "%s has @AssistedInject constructors, but none of them match the" 525 + " parameters in method %s. Unable to create AssistedInject factory.", 526 implementation, method); 527 throw errors.toException(); 528 } 529 } 530 } 531 532 /** 533 * Matching logic for constructors annotated with AssistedInject. 534 * This returns true if and only if all @Assisted parameters in the 535 * constructor exactly match (in any order) all @Assisted parameters 536 * the method's parameter. 537 */ 538 private boolean constructorHasMatchingParams(TypeLiteral<?> type, 539 Constructor<?> constructor, List<Key<?>> paramList, Errors errors) 540 throws ErrorsException { 541 List<TypeLiteral<?>> params = type.getParameterTypes(constructor); 542 Annotation[][] paramAnnotations = constructor.getParameterAnnotations(); 543 int p = 0; 544 List<Key<?>> constructorKeys = Lists.newArrayList(); 545 for (TypeLiteral<?> param : params) { 546 Key<?> paramKey = Annotations.getKey(param, constructor, paramAnnotations[p++], 547 errors); 548 constructorKeys.add(paramKey); 549 } 550 // Require that every key exist in the constructor to match up exactly. 551 for (Key<?> key : paramList) { 552 // If it didn't exist in the constructor set, we can't use it. 553 if (!constructorKeys.remove(key)) { 554 return false; 555 } 556 } 557 // If any keys remain and their annotation is Assisted, we can't use it. 558 for (Key<?> key : constructorKeys) { 559 if (key.getAnnotationType() == Assisted.class) { 560 return false; 561 } 562 } 563 // All @Assisted params match up to the method's parameters. 564 return true; 565 } 566 567 /** Calculates all dependencies required by the implementation and constructor. */ 568 private Set<Dependency<?>> getDependencies(InjectionPoint ctorPoint, TypeLiteral<?> implementation) { 569 ImmutableSet.Builder<Dependency<?>> builder = ImmutableSet.builder(); 570 builder.addAll(ctorPoint.getDependencies()); 571 if (!implementation.getRawType().isInterface()) { 572 for (InjectionPoint ip : InjectionPoint.forInstanceMethodsAndFields(implementation)) { 573 builder.addAll(ip.getDependencies()); 574 } 575 } 576 return builder.build(); 577 } 578 579 /** Return all non-assisted dependencies. */ 580 private Set<Dependency<?>> removeAssistedDeps(Set<Dependency<?>> deps) { 581 ImmutableSet.Builder<Dependency<?>> builder = ImmutableSet.builder(); 582 for(Dependency<?> dep : deps) { 583 Class<?> annotationType = dep.getKey().getAnnotationType(); 584 if (annotationType == null || !annotationType.equals(Assisted.class)) { 585 builder.add(dep); 586 } 587 } 588 return builder.build(); 589 } 590 591 /** 592 * Returns true if all dependencies are suitable for the optimized version of AssistedInject. The 593 * optimized version caches the binding & uses a ThreadLocal Provider, so can only be applied if 594 * the assisted bindings are immediately provided. This looks for hints that the values may be 595 * lazily retrieved, by looking for injections of Injector or a Provider for the assisted values. 596 */ 597 private boolean isValidForOptimizedAssistedInject(Set<Dependency<?>> dependencies, 598 Class<?> implementation, TypeLiteral<?> factoryType) { 599 Set<Dependency<?>> badDeps = null; // optimization: create lazily 600 for (Dependency<?> dep : dependencies) { 601 if (isInjectorOrAssistedProvider(dep)) { 602 if (badDeps == null) { 603 badDeps = Sets.newHashSet(); 604 } 605 badDeps.add(dep); 606 } 607 } 608 if (badDeps != null && !badDeps.isEmpty()) { 609 logger.log(Level.WARNING, "AssistedInject factory {0} will be slow " 610 + "because {1} has assisted Provider dependencies or injects the Injector. " 611 + "Stop injecting @Assisted Provider<T> (instead use @Assisted T) " 612 + "or Injector to speed things up. (It will be a ~6500% speed bump!) " 613 + "The exact offending deps are: {2}", 614 new Object[] {factoryType, implementation, badDeps} ); 615 return false; 616 } 617 return true; 618 } 619 620 /** 621 * Returns true if the dependency is for {@link Injector} or if the dependency 622 * is a {@link Provider} for a parameter that is {@literal @}{@link Assisted}. 623 */ 624 private boolean isInjectorOrAssistedProvider(Dependency<?> dependency) { 625 Class<?> annotationType = dependency.getKey().getAnnotationType(); 626 if (annotationType != null && annotationType.equals(Assisted.class)) { // If it's assisted.. 627 if (dependency.getKey().getTypeLiteral().getRawType().equals(Provider.class)) { // And a Provider... 628 return true; 629 } 630 } else if (dependency.getKey().getTypeLiteral().getRawType().equals(Injector.class)) { // If it's the Injector... 631 return true; 632 } 633 return false; 634 } 635 636 /** 637 * Returns a key similar to {@code key}, but with an {@literal @}Assisted binding annotation. 638 * This fails if another binding annotation is clobbered in the process. If the key already has 639 * the {@literal @}Assisted annotation, it is returned as-is to preserve any String value. 640 */ 641 private <T> Key<T> assistKey(Method method, Key<T> key, Errors errors) throws ErrorsException { 642 if (key.getAnnotationType() == null) { 643 return Key.get(key.getTypeLiteral(), DEFAULT_ANNOTATION); 644 } else if (key.getAnnotationType() == Assisted.class) { 645 return key; 646 } else { 647 errors.withSource(method).addMessage( 648 "Only @Assisted is allowed for factory parameters, but found @%s", 649 key.getAnnotationType()); 650 throw errors.toException(); 651 } 652 } 653 654 /** 655 * At injector-creation time, we initialize the invocation handler. At this time we make sure 656 * all factory methods will be able to build the target types. 657 */ 658 @Inject @Toolable 659 void initialize(Injector injector) { 660 if (this.injector != null) { 661 throw new ConfigurationException(ImmutableList.of(new Message(FactoryProvider2.class, 662 "Factories.create() factories may only be used in one Injector!"))); 663 } 664 665 this.injector = injector; 666 667 for (Map.Entry<Method, AssistData> entry : assistDataByMethod.entrySet()) { 668 Method method = entry.getKey(); 669 AssistData data = entry.getValue(); 670 Object[] args; 671 if(!data.optimized) { 672 args = new Object[method.getParameterTypes().length]; 673 Arrays.fill(args, "dummy object for validating Factories"); 674 } else { 675 args = null; // won't be used -- instead will bind to data.providers. 676 } 677 getBindingFromNewInjector(method, args, data); // throws if the binding isn't properly configured 678 } 679 } 680 681 /** 682 * Creates a child injector that binds the args, and returns the binding for the method's result. 683 */ 684 public Binding<?> getBindingFromNewInjector( 685 final Method method, final Object[] args, final AssistData data) { 686 checkState(injector != null, 687 "Factories.create() factories cannot be used until they're initialized by Guice."); 688 689 final Key<?> returnType = data.returnType; 690 691 // We ignore any pre-existing binding annotation. 692 final Key<?> returnKey = Key.get(returnType.getTypeLiteral(), RETURN_ANNOTATION); 693 694 Module assistedModule = new AbstractModule() { 695 @Override 696 @SuppressWarnings({ 697 "unchecked", "rawtypes"}) // raw keys are necessary for the args array and return value 698 protected void configure() { 699 Binder binder = binder().withSource(method); 700 701 int p = 0; 702 if(!data.optimized) { 703 for (Key<?> paramKey : data.paramTypes) { 704 // Wrap in a Provider to cover null, and to prevent Guice from injecting the parameter 705 binder.bind((Key) paramKey).toProvider(Providers.of(args[p++])); 706 } 707 } else { 708 for (Key<?> paramKey : data.paramTypes) { 709 // Bind to our ThreadLocalProviders. 710 binder.bind((Key) paramKey).toProvider(data.providers.get(p++)); 711 } 712 } 713 714 Constructor constructor = data.constructor; 715 // Constructor *should* always be non-null here, 716 // but if it isn't, we'll end up throwing a fairly good error 717 // message for the user. 718 if(constructor != null) { 719 binder.bind(returnKey) 720 .toConstructor(constructor, (TypeLiteral)data.implementationType) 721 .in(Scopes.NO_SCOPE); // make sure we erase any scope on the implementation type 722 } 723 } 724 }; 725 726 Injector forCreate = injector.createChildInjector(assistedModule); 727 Binding<?> binding = forCreate.getBinding(returnKey); 728 // If we have providers cached in data, cache the binding for future optimizations. 729 if(data.optimized) { 730 data.cachedBinding = binding; 731 } 732 return binding; 733 } 734 735 /** 736 * When a factory method is invoked, we create a child injector that binds all parameters, then 737 * use that to get an instance of the return type. 738 */ 739 public Object invoke(Object proxy, final Method method, final Object[] args) throws Throwable { 740 // If we setup a method handle earlier for this method, call it. 741 // This is necessary for default methods that java8 creates, so we 742 // can call the default method implementation (and not our proxied version of it). 743 if (methodHandleByMethod.containsKey(method)) { 744 return methodHandleByMethod.get(method).invokeWithArguments(args); 745 } 746 747 if (method.getDeclaringClass().equals(Object.class)) { 748 if ("equals".equals(method.getName())) { 749 return proxy == args[0]; 750 } else if ("hashCode".equals(method.getName())) { 751 return System.identityHashCode(proxy); 752 } else { 753 return method.invoke(this, args); 754 } 755 } 756 757 AssistData data = assistDataByMethod.get(method); 758 checkState(data != null, "No data for method: %s", method); 759 Provider<?> provider; 760 if(data.cachedBinding != null) { // Try to get optimized form... 761 provider = data.cachedBinding.getProvider(); 762 } else { 763 provider = getBindingFromNewInjector(method, args, data).getProvider(); 764 } 765 try { 766 int p = 0; 767 for(ThreadLocalProvider tlp : data.providers) { 768 tlp.set(args[p++]); 769 } 770 return provider.get(); 771 } catch (ProvisionException e) { 772 // if this is an exception declared by the factory method, throw it as-is 773 if (e.getErrorMessages().size() == 1) { 774 Message onlyError = getOnlyElement(e.getErrorMessages()); 775 Throwable cause = onlyError.getCause(); 776 if (cause != null && canRethrow(method, cause)) { 777 throw cause; 778 } 779 } 780 throw e; 781 } finally { 782 for(ThreadLocalProvider tlp : data.providers) { 783 tlp.remove(); 784 } 785 } 786 } 787 788 @Override public String toString() { 789 return factory.getClass().getInterfaces()[0].getName(); 790 } 791 792 @Override 793 public int hashCode() { 794 return Objects.hashCode(factoryKey, collector); 795 } 796 797 @Override public boolean equals(Object obj) { 798 if (!(obj instanceof FactoryProvider2)) { 799 return false; 800 } 801 FactoryProvider2<?> other = (FactoryProvider2<?>) obj; 802 return factoryKey.equals(other.factoryKey) && Objects.equal(collector, other.collector); 803 } 804 805 /** Returns true if {@code thrown} can be thrown by {@code invoked} without wrapping. */ 806 static boolean canRethrow(Method invoked, Throwable thrown) { 807 if (thrown instanceof Error || thrown instanceof RuntimeException) { 808 return true; 809 } 810 811 for (Class<?> declared : invoked.getExceptionTypes()) { 812 if (declared.isInstance(thrown)) { 813 return true; 814 } 815 } 816 817 return false; 818 } 819 820 // not <T> because we'll never know and this is easier than suppressing warnings. 821 private static class ThreadLocalProvider extends ThreadLocal<Object> implements Provider<Object> { 822 @Override 823 protected Object initialValue() { 824 throw new IllegalStateException( 825 "Cannot use optimized @Assisted provider outside the scope of the constructor." 826 + " (This should never happen. If it does, please report it.)"); 827 } 828 } 829 830 /** Wrapper around MethodHandles/MethodHandle, so we can compile+run on java6. */ 831 private static class MethodHandleWrapper { 832 static final int ALL_MODES = Modifier.PRIVATE 833 | Modifier.STATIC /* package */ 834 | Modifier.PUBLIC 835 | Modifier.PROTECTED; 836 837 static final Method unreflectSpecial; 838 static final Method bindTo; 839 static final Method invokeWithArguments; 840 static final Constructor<?> lookupCxtor; 841 static final boolean valid; 842 843 static { 844 Method unreflectSpecialTmp = null; 845 Method bindToTmp = null; 846 Method invokeWithArgumentsTmp = null; 847 boolean validTmp = false; 848 Constructor<?> lookupCxtorTmp = null; 849 try { 850 Class<?> lookupClass = Class.forName("java.lang.invoke.MethodHandles$Lookup"); 851 unreflectSpecialTmp = lookupClass.getMethod("unreflectSpecial", Method.class, Class.class); 852 Class<?> methodHandleClass = Class.forName("java.lang.invoke.MethodHandle"); 853 bindToTmp = methodHandleClass.getMethod("bindTo", Object.class); 854 invokeWithArgumentsTmp = methodHandleClass.getMethod("invokeWithArguments", Object[].class); 855 lookupCxtorTmp = lookupClass.getDeclaredConstructor(Class.class, int.class); 856 lookupCxtorTmp.setAccessible(true); 857 validTmp = true; 858 } catch (Exception invalid) { 859 // Ignore the exception, store the values & exit early in create(..) if invalid. 860 } 861 862 // Store refs to later. 863 valid = validTmp; 864 unreflectSpecial = unreflectSpecialTmp; 865 bindTo = bindToTmp; 866 invokeWithArguments = invokeWithArgumentsTmp; 867 lookupCxtor = lookupCxtorTmp; 868 } 869 870 static MethodHandleWrapper create(Method method, Object proxy) { 871 if (!valid) { 872 return null; 873 } 874 try { 875 Class<?> declaringClass = method.getDeclaringClass(); 876 // Note: this isn't a public API, but we need to use it in order to call default methods. 877 Object lookup = lookupCxtor.newInstance(declaringClass, ALL_MODES); 878 method.setAccessible(true); 879 // These are part of the public API, but we use reflection since we run on java6 880 // and they were introduced in java7. 881 lookup = unreflectSpecial.invoke(lookup, method, declaringClass); 882 Object handle = bindTo.invoke(lookup, proxy); 883 return new MethodHandleWrapper(handle); 884 } catch (InvocationTargetException ite) { 885 return null; 886 } catch (IllegalAccessException iae) { 887 return null; 888 } catch (InstantiationException ie) { 889 return null; 890 } 891 } 892 893 final Object handle; 894 895 MethodHandleWrapper(Object handle) { 896 this.handle = handle; 897 } 898 899 Object invokeWithArguments(Object[] args) throws Exception { 900 // We must cast the args to an object so the Object[] is the first param, 901 // as opposed to each individual varargs param. 902 return invokeWithArguments.invoke(handle, (Object) args); 903 } 904 905 @Override public String toString() { 906 return handle.toString(); 907 } 908 } 909} 910