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.internal;
18 
19 import static com.google.common.base.Preconditions.checkState;
20 import static com.google.inject.internal.Annotations.findScopeAnnotation;
21 
22 import com.google.common.base.MoreObjects;
23 import com.google.common.base.Objects;
24 import com.google.common.collect.ImmutableSet;
25 import com.google.inject.Binder;
26 import com.google.inject.ConfigurationException;
27 import com.google.inject.Inject;
28 import com.google.inject.Key;
29 import com.google.inject.TypeLiteral;
30 import com.google.inject.internal.util.Classes;
31 import com.google.inject.spi.BindingTargetVisitor;
32 import com.google.inject.spi.ConstructorBinding;
33 import com.google.inject.spi.Dependency;
34 import com.google.inject.spi.InjectionPoint;
35 import java.lang.annotation.Annotation;
36 import java.lang.reflect.Constructor;
37 import java.lang.reflect.Method;
38 import java.lang.reflect.Modifier;
39 import java.util.List;
40 import java.util.Map;
41 import java.util.Set;
42 
43 final class ConstructorBindingImpl<T> extends BindingImpl<T>
44     implements ConstructorBinding<T>, DelayedInitialize {
45 
46   private final Factory<T> factory;
47   private final InjectionPoint constructorInjectionPoint;
48 
ConstructorBindingImpl( InjectorImpl injector, Key<T> key, Object source, InternalFactory<? extends T> scopedFactory, Scoping scoping, Factory<T> factory, InjectionPoint constructorInjectionPoint)49   private ConstructorBindingImpl(
50       InjectorImpl injector,
51       Key<T> key,
52       Object source,
53       InternalFactory<? extends T> scopedFactory,
54       Scoping scoping,
55       Factory<T> factory,
56       InjectionPoint constructorInjectionPoint) {
57     super(injector, key, source, scopedFactory, scoping);
58     this.factory = factory;
59     this.constructorInjectionPoint = constructorInjectionPoint;
60   }
61 
ConstructorBindingImpl( Key<T> key, Object source, Scoping scoping, InjectionPoint constructorInjectionPoint, Set<InjectionPoint> injectionPoints)62   public ConstructorBindingImpl(
63       Key<T> key,
64       Object source,
65       Scoping scoping,
66       InjectionPoint constructorInjectionPoint,
67       Set<InjectionPoint> injectionPoints) {
68     super(source, key, scoping);
69     this.factory = new Factory<>(false, key);
70     ConstructionProxy<T> constructionProxy =
71         new DefaultConstructionProxyFactory<T>(constructorInjectionPoint).create();
72     this.constructorInjectionPoint = constructorInjectionPoint;
73     factory.constructorInjector =
74         new ConstructorInjector<T>(injectionPoints, constructionProxy, null, null);
75   }
76 
77   /**
78    * @param constructorInjector the constructor to use, or {@code null} to use the default.
79    * @param failIfNotLinked true if this ConstructorBindingImpl's InternalFactory should only
80    *     succeed if retrieved from a linked binding
81    */
create( InjectorImpl injector, Key<T> key, InjectionPoint constructorInjector, Object source, Scoping scoping, Errors errors, boolean failIfNotLinked, boolean failIfNotExplicit)82   static <T> ConstructorBindingImpl<T> create(
83       InjectorImpl injector,
84       Key<T> key,
85       InjectionPoint constructorInjector,
86       Object source,
87       Scoping scoping,
88       Errors errors,
89       boolean failIfNotLinked,
90       boolean failIfNotExplicit)
91       throws ErrorsException {
92     int numErrors = errors.size();
93 
94     @SuppressWarnings("unchecked") // constructorBinding guarantees type is consistent
95     Class<? super T> rawType =
96         constructorInjector == null
97             ? key.getTypeLiteral().getRawType()
98             : (Class) constructorInjector.getDeclaringType().getRawType();
99 
100     // We can't inject abstract classes.
101     if (Modifier.isAbstract(rawType.getModifiers())) {
102       errors.missingImplementationWithHint(key, injector);
103     }
104 
105     // Error: Inner class.
106     if (Classes.isInnerClass(rawType)) {
107       errors.cannotInjectInnerClass(rawType);
108     }
109 
110     errors.throwIfNewErrors(numErrors);
111 
112     // Find a constructor annotated @Inject
113     if (constructorInjector == null) {
114       try {
115         constructorInjector = InjectionPoint.forConstructorOf(key.getTypeLiteral());
116         if (failIfNotExplicit && !hasAtInject((Constructor) constructorInjector.getMember())) {
117           errors.atInjectRequired(rawType);
118         }
119       } catch (ConfigurationException e) {
120         throw errors.merge(e.getErrorMessages()).toException();
121       }
122     }
123 
124     // if no scope is specified, look for a scoping annotation on the concrete class
125     if (!scoping.isExplicitlyScoped()) {
126       Class<?> annotatedType = constructorInjector.getMember().getDeclaringClass();
127       Class<? extends Annotation> scopeAnnotation = findScopeAnnotation(errors, annotatedType);
128       if (scopeAnnotation != null) {
129         scoping =
130             Scoping.makeInjectable(
131                 Scoping.forAnnotation(scopeAnnotation), injector, errors.withSource(rawType));
132       }
133     }
134 
135     errors.throwIfNewErrors(numErrors);
136 
137     Factory<T> factoryFactory = new Factory<>(failIfNotLinked, key);
138     InternalFactory<? extends T> scopedFactory =
139         Scoping.scope(key, injector, factoryFactory, source, scoping);
140 
141     return new ConstructorBindingImpl<T>(
142         injector, key, source, scopedFactory, scoping, factoryFactory, constructorInjector);
143   }
144 
145   /** Returns true if the inject annotation is on the constructor. */
hasAtInject(Constructor cxtor)146   private static boolean hasAtInject(Constructor cxtor) {
147     return cxtor.isAnnotationPresent(Inject.class)
148         || cxtor.isAnnotationPresent(javax.inject.Inject.class);
149   }
150 
151   @Override
152   @SuppressWarnings("unchecked") // the result type always agrees with the ConstructorInjector type
initialize(InjectorImpl injector, Errors errors)153   public void initialize(InjectorImpl injector, Errors errors) throws ErrorsException {
154     factory.constructorInjector =
155         (ConstructorInjector<T>) injector.constructors.get(constructorInjectionPoint, errors);
156     factory.provisionCallback = injector.provisionListenerStore.get(this);
157   }
158 
159   /** True if this binding has been initialized and is ready for use. */
isInitialized()160   boolean isInitialized() {
161     return factory.constructorInjector != null;
162   }
163 
164   /** Returns an injection point that can be used to clean up the constructor store. */
getInternalConstructor()165   InjectionPoint getInternalConstructor() {
166     if (factory.constructorInjector != null) {
167       return factory.constructorInjector.getConstructionProxy().getInjectionPoint();
168     } else {
169       return constructorInjectionPoint;
170     }
171   }
172 
173   /** Returns a set of dependencies that can be iterated over to clean up stray JIT bindings. */
getInternalDependencies()174   Set<Dependency<?>> getInternalDependencies() {
175     ImmutableSet.Builder<InjectionPoint> builder = ImmutableSet.builder();
176     if (factory.constructorInjector == null) {
177       builder.add(constructorInjectionPoint);
178       // If the below throws, it's OK -- we just ignore those dependencies, because no one
179       // could have used them anyway.
180       try {
181         builder.addAll(
182             InjectionPoint.forInstanceMethodsAndFields(
183                 constructorInjectionPoint.getDeclaringType()));
184       } catch (ConfigurationException ignored) {
185       }
186     } else {
187       builder.add(getConstructor()).addAll(getInjectableMembers());
188     }
189 
190     return Dependency.forInjectionPoints(builder.build());
191   }
192 
193   @Override
acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor)194   public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
195     checkState(factory.constructorInjector != null, "not initialized");
196     return visitor.visit(this);
197   }
198 
199   @Override
getConstructor()200   public InjectionPoint getConstructor() {
201     checkState(factory.constructorInjector != null, "Binding is not ready");
202     return factory.constructorInjector.getConstructionProxy().getInjectionPoint();
203   }
204 
205   @Override
getInjectableMembers()206   public Set<InjectionPoint> getInjectableMembers() {
207     checkState(factory.constructorInjector != null, "Binding is not ready");
208     return factory.constructorInjector.getInjectableMembers();
209   }
210 
211   /*if[AOP]*/
212   @Override
getMethodInterceptors()213   public Map<Method, List<org.aopalliance.intercept.MethodInterceptor>> getMethodInterceptors() {
214     checkState(factory.constructorInjector != null, "Binding is not ready");
215     return factory.constructorInjector.getConstructionProxy().getMethodInterceptors();
216   }
217   /*end[AOP]*/
218 
219   @Override
getDependencies()220   public Set<Dependency<?>> getDependencies() {
221     return Dependency.forInjectionPoints(
222         new ImmutableSet.Builder<InjectionPoint>()
223             .add(getConstructor())
224             .addAll(getInjectableMembers())
225             .build());
226   }
227 
228   @Override
withScoping(Scoping scoping)229   protected BindingImpl<T> withScoping(Scoping scoping) {
230     return new ConstructorBindingImpl<T>(
231         null, getKey(), getSource(), factory, scoping, factory, constructorInjectionPoint);
232   }
233 
234   @Override
withKey(Key<T> key)235   protected BindingImpl<T> withKey(Key<T> key) {
236     return new ConstructorBindingImpl<T>(
237         null, key, getSource(), factory, getScoping(), factory, constructorInjectionPoint);
238   }
239 
240   @Override
241   @SuppressWarnings("unchecked") // the raw constructor member and declaring type always agree
applyTo(Binder binder)242   public void applyTo(Binder binder) {
243     InjectionPoint constructor = getConstructor();
244     getScoping()
245         .applyTo(
246             binder
247                 .withSource(getSource())
248                 .bind(getKey())
249                 .toConstructor(
250                     (Constructor) getConstructor().getMember(),
251                     (TypeLiteral) constructor.getDeclaringType()));
252   }
253 
254   @Override
toString()255   public String toString() {
256     return MoreObjects.toStringHelper(ConstructorBinding.class)
257         .add("key", getKey())
258         .add("source", getSource())
259         .add("scope", getScoping())
260         .toString();
261   }
262 
263   @Override
equals(Object obj)264   public boolean equals(Object obj) {
265     if (obj instanceof ConstructorBindingImpl) {
266       ConstructorBindingImpl<?> o = (ConstructorBindingImpl<?>) obj;
267       return getKey().equals(o.getKey())
268           && getScoping().equals(o.getScoping())
269           && Objects.equal(constructorInjectionPoint, o.constructorInjectionPoint);
270     } else {
271       return false;
272     }
273   }
274 
275   @Override
hashCode()276   public int hashCode() {
277     return Objects.hashCode(getKey(), getScoping(), constructorInjectionPoint);
278   }
279 
280   private static class Factory<T> implements InternalFactory<T> {
281     private final boolean failIfNotLinked;
282     private final Key<?> key;
283     private ConstructorInjector<T> constructorInjector;
284     private ProvisionListenerStackCallback<T> provisionCallback;
285 
Factory(boolean failIfNotLinked, Key<?> key)286     Factory(boolean failIfNotLinked, Key<?> key) {
287       this.failIfNotLinked = failIfNotLinked;
288       this.key = key;
289     }
290 
291     @Override
292     @SuppressWarnings("unchecked")
get(InternalContext context, Dependency<?> dependency, boolean linked)293     public T get(InternalContext context, Dependency<?> dependency, boolean linked)
294         throws InternalProvisionException {
295       ConstructorInjector<T> localInjector = constructorInjector;
296       if (localInjector == null) {
297         throw new IllegalStateException("Constructor not ready");
298       }
299 
300       if (!linked && failIfNotLinked) {
301         throw InternalProvisionException.jitDisabled(key);
302       }
303 
304       // This may not actually be safe because it could return a super type of T (if that's all the
305       // client needs), but it should be OK in practice thanks to the wonders of erasure.
306       return (T) localInjector.construct(context, dependency, provisionCallback);
307     }
308   }
309 }
310