blob: f88c5078f8f0a4e2fe5f1a2e4291a10415aeed12 [file] [log] [blame]
sberlinee446ca2010-10-24 21:36:39 +00001/**
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
sberlinb7a02b02011-07-08 00:34:16 +000019import static com.google.common.base.Preconditions.checkNotNull;
20
21import com.google.common.collect.ImmutableSet;
22import com.google.common.collect.Lists;
sberlinee446ca2010-10-24 21:36:39 +000023import com.google.inject.Binder;
24import com.google.inject.Key;
25import com.google.inject.Module;
26import com.google.inject.Provider;
27import com.google.inject.TypeLiteral;
28import com.google.inject.internal.Annotations;
29import com.google.inject.internal.Errors;
30import com.google.inject.internal.UniqueAnnotations;
sberlinee446ca2010-10-24 21:36:39 +000031import com.google.inject.spi.Dependency;
32import com.google.inject.spi.Message;
33import com.google.inject.util.Modules;
sberlinb7a02b02011-07-08 00:34:16 +000034
sberlinee446ca2010-10-24 21:36:39 +000035import java.lang.annotation.Annotation;
36import java.lang.reflect.Member;
37import java.lang.reflect.Method;
38import java.util.List;
39import java.util.logging.Logger;
40
41/**
sberlin488284d2010-11-17 14:27:41 +000042 * Creates bindings to methods annotated with {@literal @}{@link CheckedProvides}. Use the scope
sberlinee446ca2010-10-24 21:36:39 +000043 * and binding annotations on the provider method to configure the binding.
44 *
45 * @author sameb@google.com (Sam Berlin)
46 */
sberlin@gmail.comba8a4cd2010-11-27 01:56:53 +000047final class CheckedProviderMethodsModule implements Module {
lukes117b2a62014-09-10 13:36:04 -070048 private static final Key<Logger> LOGGER_KEY = Key.get(Logger.class);
49
sberlinee446ca2010-10-24 21:36:39 +000050 private final Object delegate;
51 private final TypeLiteral<?> typeLiteral;
52
sberlin@gmail.comba8a4cd2010-11-27 01:56:53 +000053 private CheckedProviderMethodsModule(Object delegate) {
sberlinee446ca2010-10-24 21:36:39 +000054 this.delegate = checkNotNull(delegate, "delegate");
55 this.typeLiteral = TypeLiteral.get(this.delegate.getClass());
56 }
57
58 /**
59 * Returns a module which creates bindings for provider methods from the given module.
60 */
61 static Module forModule(Module module) {
62 // avoid infinite recursion, since installing a module always installs itself
sberlin@gmail.comba8a4cd2010-11-27 01:56:53 +000063 if (module instanceof CheckedProviderMethodsModule) {
sberlinee446ca2010-10-24 21:36:39 +000064 return Modules.EMPTY_MODULE;
65 }
66
sberlin@gmail.comba8a4cd2010-11-27 01:56:53 +000067 return new CheckedProviderMethodsModule(module);
sberlinee446ca2010-10-24 21:36:39 +000068 }
69
70 public synchronized void configure(Binder binder) {
sberlin@gmail.comba8a4cd2010-11-27 01:56:53 +000071 for (CheckedProviderMethod<?> throwingProviderMethod : getProviderMethods(binder)) {
sberlinee446ca2010-10-24 21:36:39 +000072 throwingProviderMethod.configure(binder);
73 }
74 }
75
sberlin@gmail.comba8a4cd2010-11-27 01:56:53 +000076 List<CheckedProviderMethod<?>> getProviderMethods(Binder binder) {
77 List<CheckedProviderMethod<?>> result = Lists.newArrayList();
sberlinee446ca2010-10-24 21:36:39 +000078 for (Class<?> c = delegate.getClass(); c != Object.class; c = c.getSuperclass()) {
79 for (Method method : c.getDeclaredMethods()) {
Sam Berlin8557c772012-03-01 14:21:26 -050080 CheckedProvides checkedProvides = method.getAnnotation(CheckedProvides.class);
sberlin488284d2010-11-17 14:27:41 +000081 if(checkedProvides != null) {
cnesterfb04cae2015-03-02 10:11:36 -080082 result.add(createProviderMethod(binder, method, checkedProvides));
sberlinee446ca2010-10-24 21:36:39 +000083 }
84 }
85 }
86 return result;
87 }
88
sberlin@gmail.comba8a4cd2010-11-27 01:56:53 +000089 <T> CheckedProviderMethod<T> createProviderMethod(Binder binder, final Method method,
cnesterfb04cae2015-03-02 10:11:36 -080090 CheckedProvides checkedProvides) {
91 @SuppressWarnings("rawtypes")
92 Class<? extends CheckedProvider> throwingProvider = checkedProvides.value();
sberlinee446ca2010-10-24 21:36:39 +000093 binder = binder.withSource(method);
94 Errors errors = new Errors(method);
95
96 // prepare the parameter providers
97 List<Dependency<?>> dependencies = Lists.newArrayList();
98 List<Provider<?>> parameterProviders = Lists.newArrayList();
99 List<TypeLiteral<?>> parameterTypes = typeLiteral.getParameterTypes(method);
100 Annotation[][] parameterAnnotations = method.getParameterAnnotations();
101 for (int i = 0; i < parameterTypes.size(); i++) {
102 Key<?> key = getKey(errors, parameterTypes.get(i), method, parameterAnnotations[i]);
lukes117b2a62014-09-10 13:36:04 -0700103 if (key.equals(LOGGER_KEY)) {
sberlinee446ca2010-10-24 21:36:39 +0000104 // If it was a Logger, change the key to be unique & bind it to a
105 // provider that provides a logger with a proper name.
106 // This solves issue 482 (returning a new anonymous logger on every call exhausts memory)
107 Key<Logger> loggerKey = Key.get(Logger.class, UniqueAnnotations.create());
108 binder.bind(loggerKey).toProvider(new LogProvider(method));
109 key = loggerKey;
110 }
111 dependencies.add(Dependency.get(key));
112 parameterProviders.add(binder.getProvider(key));
113 }
114
115 @SuppressWarnings("unchecked") // Define T as the method's return type.
116 TypeLiteral<T> returnType = (TypeLiteral<T>) typeLiteral.getReturnType(method);
117 List<TypeLiteral<?>> exceptionTypes = typeLiteral.getExceptionTypes(method);
118
119 Key<T> key = getKey(errors, returnType, method, method.getAnnotations());
120 Class<? extends Annotation> scopeAnnotation
121 = Annotations.findScopeAnnotation(errors, method.getAnnotations());
122
123 for (Message message : errors.getMessages()) {
124 binder.addError(message);
125 }
126
sberlin@gmail.comba8a4cd2010-11-27 01:56:53 +0000127 return new CheckedProviderMethod<T>(key, method, delegate, ImmutableSet.copyOf(dependencies),
cnesterfb04cae2015-03-02 10:11:36 -0800128 parameterProviders, scopeAnnotation, throwingProvider, exceptionTypes,
129 checkedProvides.scopeExceptions());
sberlinee446ca2010-10-24 21:36:39 +0000130 }
131
132 <T> Key<T> getKey(Errors errors, TypeLiteral<T> type, Member member, Annotation[] annotations) {
133 Annotation bindingAnnotation = Annotations.findBindingAnnotation(errors, member, annotations);
134 return bindingAnnotation == null ? Key.get(type) : Key.get(type, bindingAnnotation);
135 }
136
137 @Override public boolean equals(Object o) {
sberlin@gmail.comba8a4cd2010-11-27 01:56:53 +0000138 return o instanceof CheckedProviderMethodsModule
139 && ((CheckedProviderMethodsModule) o).delegate == delegate;
sberlinee446ca2010-10-24 21:36:39 +0000140 }
141
142 @Override public int hashCode() {
143 return delegate.hashCode();
144 }
145
146 /** A provider that returns a logger based on the method name. */
147 private static final class LogProvider implements Provider<Logger> {
148 private final String name;
149
150 public LogProvider(Method method) {
151 this.name = method.getDeclaringClass().getName() + "." + method.getName();
152 }
153
154 public Logger get() {
155 return Logger.getLogger(name);
156 }
157 }
158}