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