CheckedProviderMethodsModule.java revision ba8a4cdebee559cd1b6ad4af2ef9f7f0d82d085d
1/** 2 * Copyright (C) 2010 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.throwingproviders; 18 19import com.google.inject.Binder; 20import com.google.inject.Key; 21import com.google.inject.Module; 22import com.google.inject.Provider; 23import com.google.inject.TypeLiteral; 24import com.google.inject.internal.Annotations; 25import com.google.inject.internal.Errors; 26import com.google.inject.internal.UniqueAnnotations; 27import com.google.inject.internal.util.ImmutableSet; 28import com.google.inject.internal.util.Lists; 29import static com.google.inject.internal.util.Preconditions.checkNotNull; 30import com.google.inject.spi.Dependency; 31import com.google.inject.spi.Message; 32import com.google.inject.util.Modules; 33import java.lang.annotation.Annotation; 34import java.lang.reflect.Member; 35import java.lang.reflect.Method; 36import java.util.List; 37import java.util.logging.Logger; 38 39/** 40 * Creates bindings to methods annotated with {@literal @}{@link CheckedProvides}. Use the scope 41 * and binding annotations on the provider method to configure the binding. 42 * 43 * @author sameb@google.com (Sam Berlin) 44 */ 45final class CheckedProviderMethodsModule implements Module { 46 private final Object delegate; 47 private final TypeLiteral<?> typeLiteral; 48 49 private CheckedProviderMethodsModule(Object delegate) { 50 this.delegate = checkNotNull(delegate, "delegate"); 51 this.typeLiteral = TypeLiteral.get(this.delegate.getClass()); 52 } 53 54 /** 55 * Returns a module which creates bindings for provider methods from the given module. 56 */ 57 static Module forModule(Module module) { 58 // avoid infinite recursion, since installing a module always installs itself 59 if (module instanceof CheckedProviderMethodsModule) { 60 return Modules.EMPTY_MODULE; 61 } 62 63 return new CheckedProviderMethodsModule(module); 64 } 65 66 public synchronized void configure(Binder binder) { 67 for (CheckedProviderMethod<?> throwingProviderMethod : getProviderMethods(binder)) { 68 throwingProviderMethod.configure(binder); 69 } 70 } 71 72 List<CheckedProviderMethod<?>> getProviderMethods(Binder binder) { 73 List<CheckedProviderMethod<?>> result = Lists.newArrayList(); 74 for (Class<?> c = delegate.getClass(); c != Object.class; c = c.getSuperclass()) { 75 for (Method method : c.getDeclaredMethods()) { 76 CheckedProvides checkedProvides = 77 (CheckedProvides)method.getAnnotation(CheckedProvides.class); 78 if(checkedProvides != null) { 79 result.add(createProviderMethod(binder, method, checkedProvides.value())); 80 } 81 } 82 } 83 return result; 84 } 85 86 <T> CheckedProviderMethod<T> createProviderMethod(Binder binder, final Method method, 87 Class<? extends CheckedProvider> throwingProvider) { 88 binder = binder.withSource(method); 89 Errors errors = new Errors(method); 90 91 // prepare the parameter providers 92 List<Dependency<?>> dependencies = Lists.newArrayList(); 93 List<Provider<?>> parameterProviders = Lists.newArrayList(); 94 List<TypeLiteral<?>> parameterTypes = typeLiteral.getParameterTypes(method); 95 Annotation[][] parameterAnnotations = method.getParameterAnnotations(); 96 for (int i = 0; i < parameterTypes.size(); i++) { 97 Key<?> key = getKey(errors, parameterTypes.get(i), method, parameterAnnotations[i]); 98 if(key.equals(Key.get(Logger.class))) { 99 // If it was a Logger, change the key to be unique & bind it to a 100 // provider that provides a logger with a proper name. 101 // This solves issue 482 (returning a new anonymous logger on every call exhausts memory) 102 Key<Logger> loggerKey = Key.get(Logger.class, UniqueAnnotations.create()); 103 binder.bind(loggerKey).toProvider(new LogProvider(method)); 104 key = loggerKey; 105 } 106 dependencies.add(Dependency.get(key)); 107 parameterProviders.add(binder.getProvider(key)); 108 } 109 110 @SuppressWarnings("unchecked") // Define T as the method's return type. 111 TypeLiteral<T> returnType = (TypeLiteral<T>) typeLiteral.getReturnType(method); 112 List<TypeLiteral<?>> exceptionTypes = typeLiteral.getExceptionTypes(method); 113 114 Key<T> key = getKey(errors, returnType, method, method.getAnnotations()); 115 Class<? extends Annotation> scopeAnnotation 116 = Annotations.findScopeAnnotation(errors, method.getAnnotations()); 117 118 for (Message message : errors.getMessages()) { 119 binder.addError(message); 120 } 121 122 return new CheckedProviderMethod<T>(key, method, delegate, ImmutableSet.copyOf(dependencies), 123 parameterProviders, scopeAnnotation, throwingProvider, exceptionTypes); 124 } 125 126 <T> Key<T> getKey(Errors errors, TypeLiteral<T> type, Member member, Annotation[] annotations) { 127 Annotation bindingAnnotation = Annotations.findBindingAnnotation(errors, member, annotations); 128 return bindingAnnotation == null ? Key.get(type) : Key.get(type, bindingAnnotation); 129 } 130 131 @Override public boolean equals(Object o) { 132 return o instanceof CheckedProviderMethodsModule 133 && ((CheckedProviderMethodsModule) o).delegate == delegate; 134 } 135 136 @Override public int hashCode() { 137 return delegate.hashCode(); 138 } 139 140 /** A provider that returns a logger based on the method name. */ 141 private static final class LogProvider implements Provider<Logger> { 142 private final String name; 143 144 public LogProvider(Method method) { 145 this.name = method.getDeclaringClass().getName() + "." + method.getName(); 146 } 147 148 public Logger get() { 149 return Logger.getLogger(name); 150 } 151 } 152} 153