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