View Javadoc

1   /*
2    * Grapht, an open source dependency injector.
3    * Copyright 2014-2015 various contributors (see CONTRIBUTORS.txt)
4    * Copyright 2010-2014 Regents of the University of Minnesota
5    *
6    * This program is free software; you can redistribute it and/or modify
7    * it under the terms of the GNU Lesser General Public License as
8    * published by the Free Software Foundation; either version 2.1 of the
9    * License, or (at your option) any later version.
10   *
11   * This program is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13   * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14   * details.
15   *
16   * You should have received a copy of the GNU General Public License along with
17   * this program; if not, write to the Free Software Foundation, Inc., 51
18   * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19   */
20  package org.grouplens.grapht.solver;
21  
22  import org.grouplens.grapht.*;
23  import org.grouplens.grapht.reflect.*;
24  import org.grouplens.grapht.util.InstanceProvider;
25  import org.grouplens.grapht.util.Types;
26  
27  import javax.inject.Provider;
28  import java.io.*;
29  import java.lang.reflect.ParameterizedType;
30  import java.lang.reflect.Type;
31  import java.util.Collections;
32  import java.util.List;
33  import java.util.Map;
34  
35  /**
36   * <p>
37   * BindingFunction that enables provider-injection. This function supports
38   * just-in-time binding for injection points for Providers, that creates
39   * Providers wrapping whatever provided-type is necessary for that injection
40   * point.
41   * <p>
42   * As an example, <code>Provider&lt;Foo&gt;</code> would have a Provider of Foo
43   * injected, and the Foo instances returned by that Provider's get() method
44   * would be configured as if the injection point was for <code>Foo</code>.
45   * 
46   * @author <a href="http://grouplens.org">GroupLens Research</a>
47   */
48  public class ProviderBindingFunction implements BindingFunction {
49      public ProviderBindingFunction() {
50      }
51      
52      @Override
53      public BindingResult bind(InjectionContext context, DesireChain desires) throws ResolutionException {
54          Desire desire = desires.getCurrentDesire();
55          if (Provider.class.equals(desire.getDesiredType())) {
56              // Look at the parameterized type of the injection point to
57              // find what type of object should be provided
58              Type providerType = desire.getInjectionPoint().getType();
59              if (providerType instanceof ParameterizedType) {
60                  // Can only inject a Provider if it's a parameterized type,
61                  // otherwise we have no type information about the provided type
62                  Type[] typeArgs = ((ParameterizedType) providerType).getActualTypeArguments();
63                  if (typeArgs.length == 1 && (typeArgs[0] instanceof Class || 
64                                               typeArgs[0] instanceof ParameterizedType)) {
65                      Class<?> providedType = Types.erase(typeArgs[0]);
66                      
67                      // Create a desire for the provided type, cloning the attributes
68                      // and nullability from the original desire
69                      Desire providedDesire = Desires.create(desire.getInjectionPoint().getQualifier(),
70                                                             providedType, desire.getInjectionPoint().isNullable());
71                      // Satisfied JIT desire for this injection point
72                      Desire jitDesire = desire.restrict(new ProviderInjectionSatisfaction(providedDesire));
73                      // Make sure to defer this binding since the single dependency
74                      // on the provided type might very well create a cycle that deferred
75                      // injection must break.
76                      return BindingResult.newBuilder()
77                                          .setDesire(jitDesire)
78                                          .setCachePolicy(CachePolicy.NO_PREFERENCE)
79                                          .addFlag(BindingFlag.DEFERRED)
80                                          .addFlag(BindingFlag.TERMINAL)
81                                          .build();
82                  }
83              }
84          }
85          
86          // Not a Provider desire, or the type didn't have 
87          // enough information to determine what we should provide
88          return null;
89      }
90  
91      /**
92       * Satisfaction implementation that provides a Provider, and has a single
93       * dependency on the provided type.
94       */
95      private static class ProviderInjectionSatisfaction implements Satisfaction, Serializable {
96          private static final long serialVersionUID = 1L;
97  
98          private final Desire providedDesire;
99          
100         public ProviderInjectionSatisfaction(Desire providedDesire) {
101             this.providedDesire = providedDesire;
102         }
103         
104         @Override
105         public CachePolicy getDefaultCachePolicy() {
106             return CachePolicy.NO_PREFERENCE;
107         }
108         
109         @Override
110         public List<Desire> getDependencies() {
111             return Collections.singletonList(providedDesire);
112         }
113 
114         @Override
115         public Type getType() {
116             return Types.parameterizedType(Provider.class, providedDesire.getDesiredType());
117         }
118 
119         @Override
120         public Class<?> getErasedType() {
121             return Provider.class;
122         }
123 
124         @Override
125         public boolean hasInstance() {
126             return false;
127         }
128 
129         @SuppressWarnings("unchecked")
130         @Override
131         public <T> T visit(SatisfactionVisitor<T> visitor) {
132             return (T) visitor.visitProviderClass((Class) InstanceProvider.class);
133         }
134 
135         @Override
136         public Instantiator makeInstantiator(Map<Desire,Instantiator> dependencies,
137                                              LifecycleManager lm) {
138             Instantiator instantiator = dependencies.get(providedDesire);
139             
140             // Inject an instance of a provider wrapping this instantiator.
141             return Instantiators.ofInstance(Instantiators.toProvider(instantiator));
142         }
143 
144         @Override
145         public boolean equals(Object o) {
146             if (!(o instanceof ProviderInjectionSatisfaction)) {
147                 return false;
148             }
149             
150             return ((ProviderInjectionSatisfaction) o).providedDesire.equals(providedDesire);
151         }
152         
153         @Override
154         public int hashCode() {
155             return providedDesire.hashCode();
156         }
157         
158         @Override
159         public String toString() {
160             return "Provider<" + providedDesire + ">";
161         }
162     }
163 }