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