• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (C) 2007 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.assistedinject;
18 
19 import static com.google.inject.internal.Annotations.getKey;
20 
21 import com.google.common.base.Objects;
22 import com.google.common.collect.ImmutableMap;
23 import com.google.common.collect.ImmutableSet;
24 import com.google.common.collect.Lists;
25 import com.google.common.collect.Maps;
26 import com.google.inject.ConfigurationException;
27 import com.google.inject.Inject;
28 import com.google.inject.Injector;
29 import com.google.inject.Key;
30 import com.google.inject.Provider;
31 import com.google.inject.TypeLiteral;
32 import com.google.inject.internal.BytecodeGen;
33 import com.google.inject.internal.Errors;
34 import com.google.inject.internal.ErrorsException;
35 import com.google.inject.spi.Dependency;
36 import com.google.inject.spi.HasDependencies;
37 import com.google.inject.spi.Message;
38 
39 import java.lang.annotation.Annotation;
40 import java.lang.reflect.Constructor;
41 import java.lang.reflect.InvocationHandler;
42 import java.lang.reflect.Method;
43 import java.lang.reflect.Proxy;
44 import java.lang.reflect.Type;
45 import java.util.List;
46 import java.util.Map;
47 import java.util.Set;
48 
49 /**
50  * <strong>Obsolete.</strong> Prefer {@link FactoryModuleBuilder} for its more concise API and
51  * additional capability.
52  *
53  * <p>Provides a factory that combines the caller's arguments with injector-supplied values to
54  * construct objects.
55  *
56  * <h3>Defining a factory</h3>
57  * Create an interface whose methods return the constructed type, or any of its supertypes. The
58  * method's parameters are the arguments required to build the constructed type.
59  * <pre>public interface PaymentFactory {
60  *   Payment create(Date startDate, Money amount);
61  * }</pre>
62  * You can name your factory methods whatever you like, such as <i>create</i>, <i>createPayment</i>
63  * or <i>newPayment</i>.
64  *
65  * <h3>Creating a type that accepts factory parameters</h3>
66  * {@code constructedType} is a concrete class with an {@literal @}{@link Inject}-annotated
67  * constructor. In addition to injector-supplied parameters, the constructor should have
68  * parameters that match each of the factory method's parameters. Each factory-supplied parameter
69  * requires an {@literal @}{@link Assisted} annotation. This serves to document that the parameter
70  * is not bound by your application's modules.
71  * <pre>public class RealPayment implements Payment {
72  *   {@literal @}Inject
73  *   public RealPayment(
74  *      CreditService creditService,
75  *      AuthService authService,
76  *      <strong>{@literal @}Assisted Date startDate</strong>,
77  *      <strong>{@literal @}Assisted Money amount</strong>) {
78  *     ...
79  *   }
80  * }</pre>
81  * Any parameter that permits a null value should also be annotated {@code @Nullable}.
82  *
83  * <h3>Configuring factories</h3>
84  * In your {@link com.google.inject.Module module}, bind the factory interface to the returned
85  * factory:
86  * <pre>bind(PaymentFactory.class).toProvider(
87  *     FactoryProvider.newFactory(PaymentFactory.class, RealPayment.class));</pre>
88  * As a side-effect of this binding, Guice will inject the factory to initialize it for use. The
89  * factory cannot be used until the injector has been initialized.
90  *
91  * <h3>Using the factory</h3>
92  * Inject your factory into your application classes. When you use the factory, your arguments
93  * will be combined with values from the injector to construct an instance.
94  * <pre>public class PaymentAction {
95  *   {@literal @}Inject private PaymentFactory paymentFactory;
96  *
97  *   public void doPayment(Money amount) {
98  *     Payment payment = paymentFactory.create(new Date(), amount);
99  *     payment.apply();
100  *   }
101  * }</pre>
102  *
103  * <h3>Making parameter types distinct</h3>
104  * The types of the factory method's parameters must be distinct. To use multiple parameters of
105  * the same type, use a named {@literal @}{@link Assisted} annotation to disambiguate the
106  * parameters. The names must be applied to the factory method's parameters:
107  *
108  * <pre>public interface PaymentFactory {
109  *   Payment create(
110  *       <strong>{@literal @}Assisted("startDate")</strong> Date startDate,
111  *       <strong>{@literal @}Assisted("dueDate")</strong> Date dueDate,
112  *       Money amount);
113  * } </pre>
114  * ...and to the concrete type's constructor parameters:
115  * <pre>public class RealPayment implements Payment {
116  *   {@literal @}Inject
117  *   public RealPayment(
118  *      CreditService creditService,
119  *      AuthService authService,
120  *      <strong>{@literal @}Assisted("startDate")</strong> Date startDate,
121  *      <strong>{@literal @}Assisted("dueDate")</strong> Date dueDate,
122  *      <strong>{@literal @}Assisted</strong> Money amount) {
123  *     ...
124  *   }
125  * }</pre>
126  *
127  * <h3>Values are created by Guice</h3>
128  * Returned factories use child injectors to create values. The values are eligible for method
129  * interception. In addition, {@literal @}{@literal Inject} members will be injected before they are
130  * returned.
131  *
132  * <h3>Backwards compatibility using {@literal @}AssistedInject</h3>
133  * Instead of the {@literal @}Inject annotation, you may annotate the constructed classes with
134  * {@literal @}{@link AssistedInject}. This triggers a limited backwards-compatability mode.
135  *
136  * <p>Instead of matching factory method arguments to constructor parameters using their names, the
137  * <strong>parameters are matched by their order</strong>. The first factory method argument is
138  * used for the first {@literal @}Assisted constructor parameter, etc.. Annotation names have no
139  * effect.
140  *
141  * <p>Returned values are <strong>not created by Guice</strong>. These types are not eligible for
142  * method interception. They do receive post-construction member injection.
143  *
144  * @param <F> The factory interface
145  *
146  * @author jmourits@google.com (Jerome Mourits)
147  * @author jessewilson@google.com (Jesse Wilson)
148  * @author dtm@google.com (Daniel Martin)
149  *
150  * @deprecated use {@link FactoryModuleBuilder} instead.
151  */
152 @Deprecated
153 public class FactoryProvider<F> implements Provider<F>, HasDependencies {
154 
155   /*
156    * This class implements the old @AssistedInject implementation that manually matches constructors
157    * to factory methods. The new child injector implementation lives in FactoryProvider2.
158    */
159 
160   private Injector injector;
161 
162   private final TypeLiteral<F> factoryType;
163   private final TypeLiteral<?> implementationType;
164   private final Map<Method, AssistedConstructor<?>> factoryMethodToConstructor;
165 
newFactory(Class<F> factoryType, Class<?> implementationType)166   public static <F> Provider<F> newFactory(Class<F> factoryType, Class<?> implementationType){
167     return newFactory(TypeLiteral.get(factoryType), TypeLiteral.get(implementationType));
168   }
169 
newFactory( TypeLiteral<F> factoryType, TypeLiteral<?> implementationType)170   public static <F> Provider<F> newFactory(
171       TypeLiteral<F> factoryType, TypeLiteral<?> implementationType) {
172     Map<Method, AssistedConstructor<?>> factoryMethodToConstructor
173         = createMethodMapping(factoryType, implementationType);
174 
175     if (!factoryMethodToConstructor.isEmpty()) {
176       return new FactoryProvider<F>(factoryType, implementationType, factoryMethodToConstructor);
177     } else {
178       BindingCollector collector = new BindingCollector();
179 
180       // Preserving backwards-compatibility:  Map all return types in a factory
181       // interface to the passed implementation type.
182       Errors errors = new Errors();
183       Key<?> implementationKey = Key.get(implementationType);
184 
185       try {
186         for (Method method : factoryType.getRawType().getMethods()) {
187           Key<?> returnType = getKey(factoryType.getReturnType(method), method,
188               method.getAnnotations(), errors);
189           if (!implementationKey.equals(returnType)) {
190             collector.addBinding(returnType, implementationType);
191           }
192       }
193       } catch (ErrorsException e) {
194         throw new ConfigurationException(e.getErrors().getMessages());
195       }
196 
197       return new FactoryProvider2<F>(Key.get(factoryType), collector);
198     }
199   }
200 
FactoryProvider(TypeLiteral<F> factoryType, TypeLiteral<?> implementationType, Map<Method, AssistedConstructor<?>> factoryMethodToConstructor)201   private FactoryProvider(TypeLiteral<F> factoryType,
202       TypeLiteral<?> implementationType,
203       Map<Method, AssistedConstructor<?>> factoryMethodToConstructor) {
204     this.factoryType = factoryType;
205     this.implementationType = implementationType;
206     this.factoryMethodToConstructor = factoryMethodToConstructor;
207     checkDeclaredExceptionsMatch();
208   }
209 
210   @Inject
setInjectorAndCheckUnboundParametersAreInjectable(Injector injector)211   void setInjectorAndCheckUnboundParametersAreInjectable(Injector injector) {
212     this.injector = injector;
213     for (AssistedConstructor<?> c : factoryMethodToConstructor.values()) {
214       for (Parameter p : c.getAllParameters()) {
215         if(!p.isProvidedByFactory() && !paramCanBeInjected(p, injector)) {
216           // this is lame - we're not using the proper mechanism to add an
217           // error to the injector. Throughout this class we throw exceptions
218           // to add errors, which isn't really the best way in Guice
219           throw newConfigurationException("Parameter of type '%s' is not injectable or annotated "
220                 + "with @Assisted for Constructor '%s'", p, c);
221         }
222       }
223     }
224   }
225 
checkDeclaredExceptionsMatch()226   private void checkDeclaredExceptionsMatch() {
227     for (Map.Entry<Method, AssistedConstructor<?>> entry : factoryMethodToConstructor.entrySet()) {
228       for (Class<?> constructorException : entry.getValue().getDeclaredExceptions()) {
229         if (!isConstructorExceptionCompatibleWithFactoryExeception(
230             constructorException, entry.getKey().getExceptionTypes())) {
231           throw newConfigurationException("Constructor %s declares an exception, but no compatible "
232               + "exception is thrown by the factory method %s", entry.getValue(), entry.getKey());
233         }
234       }
235     }
236   }
237 
isConstructorExceptionCompatibleWithFactoryExeception( Class<?> constructorException, Class<?>[] factoryExceptions)238   private boolean isConstructorExceptionCompatibleWithFactoryExeception(
239       Class<?> constructorException, Class<?>[] factoryExceptions) {
240     for (Class<?> factoryException : factoryExceptions) {
241       if (factoryException.isAssignableFrom(constructorException)) {
242         return true;
243       }
244     }
245     return false;
246   }
247 
paramCanBeInjected(Parameter parameter, Injector injector)248   private boolean paramCanBeInjected(Parameter parameter, Injector injector) {
249     return parameter.isBound(injector);
250   }
251 
createMethodMapping( TypeLiteral<?> factoryType, TypeLiteral<?> implementationType)252   private static Map<Method, AssistedConstructor<?>> createMethodMapping(
253       TypeLiteral<?> factoryType, TypeLiteral<?> implementationType) {
254     List<AssistedConstructor<?>> constructors = Lists.newArrayList();
255 
256     for (Constructor<?> constructor : implementationType.getRawType().getDeclaredConstructors()) {
257       if (constructor.isAnnotationPresent(AssistedInject.class)) {
258         AssistedConstructor<?> assistedConstructor = AssistedConstructor.create(
259             constructor, implementationType.getParameterTypes(constructor));
260         constructors.add(assistedConstructor);
261       }
262     }
263 
264     if (constructors.isEmpty()) {
265       return ImmutableMap.of();
266     }
267 
268     Method[] factoryMethods = factoryType.getRawType().getMethods();
269 
270     if (constructors.size() != factoryMethods.length) {
271       throw newConfigurationException("Constructor mismatch: %s has %s @AssistedInject "
272           + "constructors, factory %s has %s creation methods", implementationType,
273           constructors.size(), factoryType, factoryMethods.length);
274     }
275 
276     Map<ParameterListKey, AssistedConstructor<?>> paramsToConstructor = Maps.newHashMap();
277 
278     for (AssistedConstructor<?> c : constructors) {
279       if (paramsToConstructor.containsKey(c.getAssistedParameters())) {
280         throw new RuntimeException("Duplicate constructor, " + c);
281       }
282       paramsToConstructor.put(c.getAssistedParameters(), c);
283     }
284 
285     Map<Method, AssistedConstructor<?>> result = Maps.newHashMap();
286     for (Method method : factoryMethods) {
287       if (!method.getReturnType().isAssignableFrom(implementationType.getRawType())) {
288         throw newConfigurationException("Return type of method %s is not assignable from %s",
289             method, implementationType);
290       }
291 
292       List<Type> parameterTypes = Lists.newArrayList();
293       for (TypeLiteral<?> parameterType : factoryType.getParameterTypes(method)) {
294         parameterTypes.add(parameterType.getType());
295       }
296       ParameterListKey methodParams = new ParameterListKey(parameterTypes);
297 
298       if (!paramsToConstructor.containsKey(methodParams)) {
299         throw newConfigurationException("%s has no @AssistInject constructor that takes the "
300             + "@Assisted parameters %s in that order. @AssistInject constructors are %s",
301             implementationType, methodParams, paramsToConstructor.values());
302       }
303 
304       method.getParameterAnnotations();
305       for (Annotation[] parameterAnnotations : method.getParameterAnnotations()) {
306         for (Annotation parameterAnnotation : parameterAnnotations) {
307           if (parameterAnnotation.annotationType() == Assisted.class) {
308             throw newConfigurationException("Factory method %s has an @Assisted parameter, which "
309                 + "is incompatible with the deprecated @AssistedInject annotation. Please replace "
310                 + "@AssistedInject with @Inject on the %s constructor.",
311                 method, implementationType);
312           }
313         }
314       }
315 
316       AssistedConstructor<?> matchingConstructor = paramsToConstructor.remove(methodParams);
317 
318       result.put(method, matchingConstructor);
319     }
320     return result;
321   }
322 
getDependencies()323   public Set<Dependency<?>> getDependencies() {
324     List<Dependency<?>> dependencies = Lists.newArrayList();
325     for (AssistedConstructor<?> constructor : factoryMethodToConstructor.values()) {
326       for (Parameter parameter : constructor.getAllParameters()) {
327         if (!parameter.isProvidedByFactory()) {
328           dependencies.add(Dependency.get(parameter.getPrimaryBindingKey()));
329         }
330       }
331     }
332     return ImmutableSet.copyOf(dependencies);
333   }
334 
get()335   public F get() {
336     InvocationHandler invocationHandler = new InvocationHandler() {
337       public Object invoke(Object proxy, Method method, Object[] creationArgs) throws Throwable {
338         // pass methods from Object.class to the proxy
339         if (method.getDeclaringClass().equals(Object.class)) {
340           if ("equals".equals(method.getName())) {
341             return proxy == creationArgs[0];
342           } else if ("hashCode".equals(method.getName())) {
343             return System.identityHashCode(proxy);
344           } else {
345             return method.invoke(this, creationArgs);
346           }
347         }
348 
349         AssistedConstructor<?> constructor = factoryMethodToConstructor.get(method);
350         Object[] constructorArgs = gatherArgsForConstructor(constructor, creationArgs);
351         Object objectToReturn = constructor.newInstance(constructorArgs);
352         injector.injectMembers(objectToReturn);
353         return objectToReturn;
354       }
355 
356       public Object[] gatherArgsForConstructor(
357           AssistedConstructor<?> constructor,
358           Object[] factoryArgs) {
359         int numParams = constructor.getAllParameters().size();
360         int argPosition = 0;
361         Object[] result = new Object[numParams];
362 
363         for (int i = 0; i < numParams; i++) {
364           Parameter parameter = constructor.getAllParameters().get(i);
365           if (parameter.isProvidedByFactory()) {
366             result[i] = factoryArgs[argPosition];
367             argPosition++;
368           } else {
369             result[i] = parameter.getValue(injector);
370           }
371         }
372         return result;
373       }
374     };
375 
376     @SuppressWarnings("unchecked") // we imprecisely treat the class literal of T as a Class<T>
377     Class<F> factoryRawType = (Class<F>) (Class<?>) factoryType.getRawType();
378     return factoryRawType.cast(Proxy.newProxyInstance(BytecodeGen.getClassLoader(factoryRawType),
379         new Class[] { factoryRawType }, invocationHandler));
380   }
381 
382   @Override
hashCode()383   public int hashCode() {
384     return Objects.hashCode(factoryType, implementationType);
385   }
386 
387   @Override
equals(Object obj)388   public boolean equals(Object obj) {
389     if (!(obj instanceof FactoryProvider)) {
390       return false;
391     }
392     FactoryProvider<?> other = (FactoryProvider<?>) obj;
393     return factoryType.equals(other.factoryType)
394         && implementationType.equals(other.implementationType);
395   }
396 
newConfigurationException(String format, Object... args)397   private static ConfigurationException newConfigurationException(String format, Object... args) {
398     return new ConfigurationException(ImmutableSet.of(new Message(Errors.format(format, args))));
399   }
400 }
401