/** * Matches constructor parameters to method parameters for injection and records remaining parameters as required keys. */ private String[] extractConstructorParameters(Key<?> factoryKey, TypeLiteral<?> implementation, Constructor constructor, List<Key<?>> methodParams, Errors errors, Set<Dependency> dependencyCollector) throws ErrorsException { // Get parameters with annotations. List<TypeLiteral<?>> ctorParams = implementation.getParameterTypes(constructor); Annotation[][] ctorParamAnnotations = constructor.getParameterAnnotations(); int p = 0; String[] parameterNames = new String[ctorParams.size()]; for (TypeLiteral<?> ctorParam : ctorParams) { Key<?> ctorParamKey = getKey(ctorParam, constructor, ctorParamAnnotations[p], errors); if (ctorParamKey.getAnnotationType() == Assisted.class) { int location = methodParams.indexOf(ctorParamKey); // This should never happen since the constructor was already checked // in #[inject]constructorHasMatchingParams(..). Preconditions.checkState(location != -1); parameterNames[p] = ReflectUtil.formatParameterName(location); } else { dependencyCollector.add(new Dependency(factoryKey, ctorParamKey, false, true, constructor.toString())); } p++; } return parameterNames; }
@Override public Object resolve(Injectee injectee, ServiceHandle<?> serviceHandle) { if (injectee.getRequiredType() instanceof Class) { TypeLiteral<?> typeLiteral = TypeLiteral.get(injectee.getRequiredType()); Errors errors = new Errors(injectee.getParent()); Key<?> key; try { key = Annotations.getKey(typeLiteral, (Member) injectee.getParent(), injectee.getParent().getDeclaredAnnotations(), errors); } catch (ErrorsException e) { errors.merge(e.getErrors()); throw new ConfigurationException(errors.getMessages()); } return injector.getInstance(key); } throw new IllegalStateException("Can't process injection point: " + injectee.getRequiredType()); }
/** * Creates the scoped proxy object using the given provider. * * @param injector The injector. * @return The scoped proxy object. */ @SuppressWarnings("unchecked") public T create(Injector injector) { Preconditions.checkNotNull(injector, "injector"); Preconditions.checkState(this.dispatcher != null, "no provider set"); this.enhancer.setCallbackType(this.dispatcher.getClass()); final Class<T> enhancedClass = this.enhancer.createClass(); final Errors errors = new Errors(); final T proxyInstance = this.constructionStrategy .createInstance(enhancedClass, injector, errors); errors.throwProvisionExceptionIfErrorsExist(); final Factory factory = (Factory) proxyInstance; factory.setCallback(CALLBACK_INDEX, this.dispatcher); return proxyInstance; }
private ImmutableList<EventualProvider<?>> introspectProviders() { ImmutableList.Builder<EventualProvider<?>> builder = ImmutableList.builder(); // FIXME handle method overriding? for (Class<?> t : type.getTypes().classes().rawTypes()) { if (t != Object.class) { for (Method m : t.getDeclaredMethods()) { if (m.isAnnotationPresent(Eventually.Provides.class)) { Errors methodErrors = errors.withSource(StackTraceElements.forMember(m)); Invokable<T, Object> invokable = type.method(m); if (eligibilityVerified(invokable, methodErrors)) { builder.add(providerFor(invokable, methodErrors)); } } } } } return builder.build(); }
private boolean eligibilityVerified(Invokable<T, Object> method, Errors errors) { List<TypeToken<?>> primitiveTypes = Lists.newArrayList(); for (Parameter parameter : method.getParameters()) { if (parameter.getType().isPrimitive()) { primitiveTypes.add(parameter.getType()); } } if (method.getReturnType().isPrimitive() && !isVoid(method)) { primitiveTypes.add(method.getReturnType()); } if (!primitiveTypes.isEmpty()) { errors.addMessage("Incompartible eventual provider method '%s'" + "\n\tSignature has primitive types: %s." + " Please use boxed types instead", method.getName(), Joiner.on(", ").join(primitiveTypes)); } return primitiveTypes.isEmpty(); }
/** * Applies all scanners to the modules we've installed. We skip certain PrivateModules because * store them in more than one Modules map and only want to process them through one of the * maps. (They're stored in both maps to prevent a module from being installed more than once.) */ void scanForAnnotatedMethods() { for (ModuleAnnotatedMethodScanner scanner : scanners) { // Note: we must iterate over a copy of the modules because calling install(..) // will mutate modules, otherwise causing a ConcurrentModificationException. for (Map.Entry<Module, ModuleInfo> entry : Maps.newLinkedHashMap(modules).entrySet()) { Module module = entry.getKey(); ModuleInfo info = entry.getValue(); if (info.skipScanning) { continue; } moduleSource = entry.getValue().moduleSource; try { info.binder.install(ProviderMethodsModule.forModule(module, scanner)); } catch (RuntimeException e) { Collection<Message> messages = Errors.getMessagesFromThrowable(e); if (!messages.isEmpty()) { elements.addAll(messages); } else { addError(e); } } } } moduleSource = null; }
/** * Returns all static method and field injection points on {@code type}. * * @return a possibly empty set of injection points. The set has a specified iteration order. All * fields are returned and then all methods. Within the fields, supertype fields are returned * before subtype fields. Similarly, supertype methods are returned before subtype methods. * @throws ConfigurationException if there is a malformed injection point on {@code type}, such as * a field with multiple binding annotations. The exception's {@link * ConfigurationException#getPartialValue() partial value} is a {@code Set<InjectionPoint>} of * the valid injection points. */ public static Set<InjectionPoint> forStaticMethodsAndFields(TypeLiteral<?> type) { Errors errors = new Errors(); Set<InjectionPoint> result; if (type.getRawType().isInterface()) { errors.staticInjectionOnInterface(type.getRawType()); result = null; } else { result = getInjectionPoints(type, true, errors); } if (errors.hasErrors()) { throw new ConfigurationException(errors.getMessages()).withPartialValue(result); } return result; }
/** Returns true if the binding annotation is in the wrong place. */ private static boolean checkForMisplacedBindingAnnotations(Member member, Errors errors) { Annotation misplacedBindingAnnotation = Annotations.findBindingAnnotation( errors, member, ((AnnotatedElement) member).getAnnotations()); if (misplacedBindingAnnotation == null) { return false; } // don't warn about misplaced binding annotations on methods when there's a field with the same // name. In Scala, fields always get accessor methods (that we need to ignore). See bug 242. if (member instanceof Method) { try { if (member.getDeclaringClass().getDeclaredField(member.getName()) != null) { return false; } } catch (NoSuchFieldException ignore) { } } errors.misplacedBindingAnnotation(member, misplacedBindingAnnotation); return true; }
/** * Returns all static method and field injection points on {@code type}. * * @return a possibly empty set of injection points. The set has a specified iteration order. All * fields are returned and then all methods. Within the fields, supertype fields are returned * before subtype fields. Similarly, supertype methods are returned before subtype methods. * @throws ConfigurationException if there is a malformed injection point on {@code type}, such as * a field with multiple binding annotations. The exception's {@link * ConfigurationException#getPartialValue() partial value} is a {@code Set<InjectionPoint>} * of the valid injection points. */ public static Set<InjectionPoint> forStaticMethodsAndFields(TypeLiteral<?> type) { Errors errors = new Errors(); Set<InjectionPoint> result; if (type.getRawType().isInterface()) { errors.staticInjectionOnInterface(type.getRawType()); result = null; } else { result = getInjectionPoints(type, true, errors); } if (errors.hasErrors()) { throw new ConfigurationException(errors.getMessages()).withPartialValue(result); } return result; }
/** * Returns true if the binding annotation is in the wrong place. */ private static boolean checkForMisplacedBindingAnnotations(Member member, Errors errors) { Annotation misplacedBindingAnnotation = Annotations.findBindingAnnotation( errors, member, ((AnnotatedElement) member).getAnnotations()); if (misplacedBindingAnnotation == null) { return false; } // don't warn about misplaced binding annotations on methods when there's a field with the same // name. In Scala, fields always get accessor methods (that we need to ignore). See bug 242. if (member instanceof Method) { try { if (member.getDeclaringClass().getDeclaredField(member.getName()) != null) { return false; } } catch (NoSuchFieldException ignore) { } } errors.misplacedBindingAnnotation(member, misplacedBindingAnnotation); return true; }
private static boolean isValidMethod(InjectableMethod injectableMethod, Errors errors) { boolean result = true; if (injectableMethod.jsr330) { Method method = injectableMethod.method; if (Modifier.isAbstract(method.getModifiers())) { errors.cannotInjectAbstractMethod(method); result = false; } if (method.getTypeParameters().length > 0) { errors.cannotInjectMethodWithTypeParameters(method); result = false; } } return result; }
public void testConstructorRuntimeException() { Injector injector = Guice.createInjector(new AbstractModule() { @Override protected void configure() { bindConstant().annotatedWith(Names.named("runtime")).to(true); bind(Exploder.class).to(Explosion.class); bind(Tracer.class).to(TracerImpl.class); } }); try { injector.getInstance(Tracer.class); fail(); } catch(ProvisionException pe) { // Make sure our initial error message gives the user exception. Asserts.assertContains(pe.getMessage(), "1) Error injecting constructor", "java.lang.IllegalStateException: boom!"); assertEquals(1, pe.getErrorMessages().size()); assertEquals(IllegalStateException.class, pe.getCause().getClass()); assertEquals(IllegalStateException.class, Errors.getOnlyCause(pe.getErrorMessages()).getClass()); } }
public void testConstructorCheckedException() { Injector injector = Guice.createInjector(new AbstractModule() { @Override protected void configure() { bindConstant().annotatedWith(Names.named("runtime")).to(false); bind(Exploder.class).to(Explosion.class); bind(Tracer.class).to(TracerImpl.class); } }); try { injector.getInstance(Tracer.class); fail(); } catch(ProvisionException pe) { // Make sure our initial error message gives the user exception. Asserts.assertContains(pe.getMessage(), "1) Error injecting constructor", "java.io.IOException: boom!"); assertEquals(1, pe.getErrorMessages().size()); assertEquals(IOException.class, pe.getCause().getClass()); assertEquals(IOException.class, Errors.getOnlyCause(pe.getErrorMessages()).getClass()); } }
public void testCustomProvidersRuntimeException() { Injector injector = Guice.createInjector(new AbstractModule() { @Override protected void configure() { bind(Exploder.class).toProvider(new Provider<Exploder>() { public Exploder get() { return Explosion.createRuntime(); } }); bind(Tracer.class).to(TracerImpl.class); } }); try { injector.getInstance(Tracer.class); fail(); } catch(ProvisionException pe) { // Make sure our initial error message gives the user exception. Asserts.assertContains(pe.getMessage(), "1) Error in custom provider", "java.lang.IllegalStateException: boom!"); assertEquals(1, pe.getErrorMessages().size()); assertEquals(IllegalStateException.class, pe.getCause().getClass()); assertEquals(IllegalStateException.class, Errors.getOnlyCause(pe.getErrorMessages()).getClass()); } }
public void testProviderMethodRuntimeException() { Injector injector = Guice.createInjector(new AbstractModule() { @Override protected void configure() { bind(Tracer.class).to(TracerImpl.class); } @Provides Exploder exploder() { return Explosion.createRuntime(); } }); try { injector.getInstance(Tracer.class); fail(); } catch(ProvisionException pe) { // Make sure our initial error message gives the user exception. Asserts.assertContains(pe.getMessage(), "1) Error in custom provider", "java.lang.IllegalStateException: boom!"); assertEquals(1, pe.getErrorMessages().size()); assertEquals(IllegalStateException.class, pe.getCause().getClass()); assertEquals(IllegalStateException.class, Errors.getOnlyCause(pe.getErrorMessages()).getClass()); } }
public void testProviderMethodCheckedException() { Injector injector = Guice.createInjector(new AbstractModule() { @Override protected void configure() { bind(Tracer.class).to(TracerImpl.class); } @Provides Exploder exploder() throws IOException { return Explosion.createChecked(); } }); try { injector.getInstance(Tracer.class); fail(); } catch(ProvisionException pe) { pe.printStackTrace(); // Make sure our initial error message gives the user exception. Asserts.assertContains(pe.getMessage(), "1) Error in custom provider", "java.io.IOException: boom!"); assertEquals(1, pe.getErrorMessages().size()); assertEquals(IOException.class, pe.getCause().getClass()); assertEquals(IOException.class, Errors.getOnlyCause(pe.getErrorMessages()).getClass()); } }
public static int getCollectionSizeForParam(final Method method, final int paramIndex, final TestEach annotation, final GuiceRegistry registry) { if (annotation.value().length > 0) { return annotation.value().length; } else { final Errors errors = new Errors(method); final Collection<?> col = getGuiceCollectionForParam(method, paramIndex, registry, errors); errors.throwCreationExceptionIfErrorsExist(); return col.size(); } }
@Override protected void configure() { final Errors errors = new Errors(testClass); for (FrameworkField field : fields) { try { final Field f = field.getField(); final Key key = Annotations.getKey(TypeLiteral.get(f.getGenericType()), f, field.getAnnotations(), errors); bindMock(key, f.getType(), "Automock[" + field.getName() + "] " + key); } catch (ErrorsException e) { // Add it to the error list and hold them all until the end errors.merge(e.getErrors()); } } errors.throwConfigurationExceptionIfErrorsExist(); }
/** * Compute the value for a parameter annotated with * * @param errors * @param paramIndex * * @return * * @throws ErrorsException */ private Object getCollectionParamValue(final Errors errors, final int paramIndex, final TestEach annotation) throws ErrorsException { if (!(method instanceof TestEachFrameworkMethod)) throw new AssertionError("Required a parameterised FrameworkMethod but got " + method); // The index within the collection to use for this particular invocation final int collectionIndex = ((TestEachFrameworkMethod) method).getCollectionIndexForParameter(paramIndex); if (annotation.value() != null && annotation.value().length > 0) { final Class<?> desiredType = method.getMethod().getParameterTypes()[paramIndex]; final String val = annotation.value()[collectionIndex]; return convertParamType(val, desiredType); } else { return getGuiceCollectionParamValue(errors, paramIndex, collectionIndex); } }
public void install(Module module) { if (modules.add(module)) { Binder binder = this; if (module instanceof PrivateModule) { binder = binder.newPrivateBinder(); } try { module.configure(binder); } catch (RuntimeException e) { Collection<Message> messages = Errors.getMessagesFromThrowable(e); if (!messages.isEmpty()) { elements.addAll(messages); } else { addError(e); } } binder.install(ProviderMethodsModule.forModule(module)); } }
/** * Matching logic for {@literal @}{@link Inject} constructor and method parameters. * <p/> * This returns true if all assisted parameters required by the constructor are provided by the factory method. */ private boolean injectConstructorHasMatchingParams(TypeLiteral<?> type, Constructor<?> constructor, List<Key<?>> paramList, Errors errors) throws ErrorsException { List<TypeLiteral<?>> params = type.getParameterTypes(constructor); Annotation[][] paramAnnotations = constructor.getParameterAnnotations(); int p = 0; for (TypeLiteral<?> param : params) { Key<?> paramKey = getKey(param, constructor, paramAnnotations[p++], errors); if (paramKey.getAnnotationType() == Assisted.class && !paramList.contains(paramKey)) { return false; } } return true; }
/** * Returns a key similar to {@code key}, but with an {@literal @}Assisted binding annotation. * <p/> * This fails if another binding annotation is clobbered in the process. If the key already has the {@literal @}Assisted annotation, it is returned as-is to * preserve any String value. */ private <T> Key<T> assistKey(Method method, Key<T> key, Errors errors) throws ErrorsException { if (key.getAnnotationType() == null) { return Key.get(key.getTypeLiteral(), DEFAULT_ANNOTATION); } else if (key.getAnnotationType() == Assisted.class) { return key; } else { errors.withSource(method).addMessage( PrettyPrinter.format("Only @Assisted is allowed for factory parameters, but found @%s", key.getAnnotationType())); throw errors.toException(); } }
private <D> InjectableMethod(@Nullable TypeLiteral<D> targetType, @Nullable D target, Method method, @Nullable T result) { final Errors errors = new Errors(method); if(Members.isStatic(method)) { checkArgument(target == null); } else { checkArgument(target != null); } targetType = targetType(targetType, target, method); checkArgument(method.getDeclaringClass().isAssignableFrom(targetType.getRawType())); this.method = method; this.dependencies = ImmutableSet.copyOf(InjectionPoint.forMethod(method, targetType).getDependencies()); if(result != null) { this.result = result; this.providedKey = Keys.forInstance(result); } else { final TypeLiteral<T> returnType = (TypeLiteral<T>) targetType.getReturnType(method); if(!Void.class.equals(returnType.getRawType())) { final Annotation qualifier = Annotations.findBindingAnnotation(errors, method, method.getAnnotations()); this.result = null; this.providedKey = Keys.get(returnType, qualifier); } else { this.result = (T) this; this.providedKey = Keys.forInstance(this.result); } } this.scope = Annotations.findScopeAnnotation(errors, method.getAnnotations()); MethodHandle handle = MethodHandleUtils.privateUnreflect(method); if(target != null) { handle = handle.bindTo(target); } this.handle = handle; }
@Test public void testObjenesis() throws Exception { final Errors errors = new Errors(); final ClassWithConstructor inst = ConstructionStrategies.OBJENESIS .createInstance(ClassWithConstructor.class, mock(Injector.class), errors); assertNotNull(inst); assertEquals(0, errors.size()); }
@Test public void testNullValues() throws Exception { final Errors errors = new Errors(); final ClassWithConstructor inst = ConstructionStrategies.NULL_VALUES.createInstance( ClassWithConstructor.class, mock(Injector.class), errors); assertNotNull(inst); assertNull(inst.attribute); assertEquals(0, errors.size()); }
@Test public void testNullValuesMultipleConstructors() throws Exception { final Errors errors = new Errors(); final ClassWithMultipleConstructors inst = ConstructionStrategies.NULL_VALUES.createInstance( ClassWithMultipleConstructors.class, mock(Injector.class), errors); assertNull(inst); assertEquals(1, errors.size()); }
/** * When serialized, we eagerly convert sources to strings. This hurts our * formatting, but it guarantees that the receiving end will be able to read * the message. */ private Object writeReplace() throws ObjectStreamException { Object[] sourcesAsStrings = sources.toArray(); for (int i = 0; i < sourcesAsStrings.length; i++) { sourcesAsStrings[i] = Errors.convert(sourcesAsStrings[i]) .toString(); } return new Message(ImmutableList.copyOf(sourcesAsStrings), message, cause); }
Providers(@Nullable T providersInstance, Class<T> providerClass) { this.providersInstance = providersInstance; this.providersClass = providerClass; this.source = StackTraceElements.forType(providersClass); this.type = TypeToken.of(providersClass); this.errors = new Errors(source); this.scopeAnnotation = Annotations.findScopeAnnotation(errors, providersClass); this.providers = introspectProviders(); }
private EventualProvider<?> providerFor(Invokable<T, ?> method, Errors methodErrors) { Annotation[] annotations = method.getAnnotations(); verifyMethodAccessibility(methodErrors, method, source); @Nullable Annotation bindingAnnotation = Annotations.findBindingAnnotation(methodErrors, method, annotations); verifyAbsenseOfScopeAnnotation(methodErrors, annotations, source); List<Dependency<ListenableFuture<?>>> dependencies = Lists.newArrayListWithCapacity(method.getParameters().size()); for (Parameter parameter : method.getParameters()) { dependencies.add(extractDependency(methodErrors, parameter)); } Key<ListenableFuture<?>> bindingKey; boolean exposedBinding = method.isAnnotationPresent(Exposed.class); if (isVoid(method)) { bindingKey = futureKey(TypeToken.of(Boolean.class), new BlackholedAnnotation()); exposedBinding = false; } else { bindingKey = futureKey(method.getReturnType(), bindingAnnotation); } return new EventualProvider<>( method, exposedBinding, dependencies, bindingKey, scopeAnnotation, source); }
private void verifyAbsenseOfScopeAnnotation(Errors methodErrors, Annotation[] annotations, Object source) { @Nullable Class<? extends Annotation> methodScopeAnnotation = Annotations.findScopeAnnotation(methodErrors, annotations); if (methodScopeAnnotation != null) { methodErrors.addMessage( "Misplaced scope annotation @%s on method @%s %s." + "\n\tScope annotation will only be inherited from enclosing class %s", methodScopeAnnotation.getSimpleName(), Eventually.Provides.class.getSimpleName(), source, providersClass.getSimpleName()); } }