• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.google.inject.internal;
2 
3 import com.google.common.collect.ImmutableSet;
4 import com.google.inject.Key;
5 import com.google.inject.Provider;
6 import com.google.inject.internal.ProvisionListenerStackCallback.ProvisionCallback;
7 import com.google.inject.spi.Dependency;
8 import com.google.inject.spi.HasDependencies;
9 import com.google.inject.spi.InjectionPoint;
10 import com.google.inject.spi.ProviderWithExtensionVisitor;
11 
12 /**
13  * A {@link ProviderInstanceBindingImpl} for implementing 'native' guice extensions.
14  *
15  * <p>Beyond the normal binding contract that is mostly handled by our baseclass, this also
16  * implements {@link DelayedInitialize} in order to initialize factory state.
17  */
18 final class InternalProviderInstanceBindingImpl<T> extends ProviderInstanceBindingImpl<T>
19     implements DelayedInitialize {
20   enum InitializationTiming {
21     /** This factory can be initialized eagerly. This should be the case for most things. */
22     EAGER,
23 
24     /**
25      * Initialization of this factory should be delayed until after all other static initialization
26      * completes. This will be useful for factories that need to call {@link
27      * InjectorImpl#getExistingBinding(Key)} to not create jit bindings, but also want to be able to
28      * conditionally consume jit bindings created by other other bindings.
29      */
30     DELAYED;
31   }
32 
33   private final Factory<T> originalFactory;
34 
InternalProviderInstanceBindingImpl( InjectorImpl injector, Key<T> key, Object source, Factory<T> originalFactory, InternalFactory<? extends T> scopedFactory, Scoping scoping)35   InternalProviderInstanceBindingImpl(
36       InjectorImpl injector,
37       Key<T> key,
38       Object source,
39       Factory<T> originalFactory,
40       InternalFactory<? extends T> scopedFactory,
41       Scoping scoping) {
42     super(
43         injector,
44         key,
45         source,
46         scopedFactory,
47         scoping,
48         originalFactory,
49         ImmutableSet.<InjectionPoint>of());
50     this.originalFactory = originalFactory;
51   }
52 
getInitializationTiming()53   InitializationTiming getInitializationTiming() {
54     return originalFactory.initializationTiming;
55   }
56 
57   @Override
initialize(final InjectorImpl injector, final Errors errors)58   public void initialize(final InjectorImpl injector, final Errors errors) throws ErrorsException {
59     originalFactory.source = getSource();
60     originalFactory.provisionCallback = injector.provisionListenerStore.get(this);
61     // For these kinds of providers, the 'user supplied provider' is really 'guice supplied'
62     // So make our user supplied provider just delegate to the guice supplied one.
63     originalFactory.delegateProvider = getProvider();
64     originalFactory.initialize(injector, errors);
65   }
66 
67   /**
68    * A base factory implementation. Any Factories that delegate to other bindings should use the
69    * {@code CyclicFactory} subclass, but trivial factories can use this one.
70    */
71   abstract static class Factory<T> implements InternalFactory<T>, Provider<T>, HasDependencies {
72     private final InitializationTiming initializationTiming;
73     private Object source;
74     private Provider<T> delegateProvider;
75     ProvisionListenerStackCallback<T> provisionCallback;
76 
Factory(InitializationTiming initializationTiming)77     Factory(InitializationTiming initializationTiming) {
78       this.initializationTiming = initializationTiming;
79     }
80     /**
81      * The binding source.
82      *
83      * <p>May be useful for augmenting runtime error messages.
84      *
85      * <p>Note: this will return {#code null} until {@link #initialize(InjectorImpl, Errors)} has
86      * already been called.
87      */
getSource()88     final Object getSource() {
89       return source;
90     }
91 
92     /**
93      * A callback that allows for implementations to fetch dependencies on other bindings.
94      *
95      * <p>Will be called exactly once, prior to any call to {@link #doProvision}.
96      */
initialize(InjectorImpl injector, Errors errors)97     abstract void initialize(InjectorImpl injector, Errors errors) throws ErrorsException;
98 
99     @Override
get()100     public final T get() {
101       Provider<T> local = delegateProvider;
102       if (local == null) {
103         throw new IllegalStateException(
104             "This Provider cannot be used until the Injector has been created.");
105       }
106       return local.get();
107     }
108 
109     @Override
get(final InternalContext context, final Dependency<?> dependency, boolean linked)110     public T get(final InternalContext context, final Dependency<?> dependency, boolean linked)
111         throws InternalProvisionException {
112       if (provisionCallback == null) {
113         return doProvision(context, dependency);
114       } else {
115         return provisionCallback.provision(
116             context,
117             new ProvisionCallback<T>() {
118               @Override
119               public T call() throws InternalProvisionException {
120                 return doProvision(context, dependency);
121               }
122             });
123       }
124     }
125     /**
126      * Creates an object to be injected.
127      *
128      * @throws com.google.inject.internal.InternalProvisionException if a value cannot be provided
129      * @return instance to be injected
130      */
doProvision(InternalContext context, Dependency<?> dependency)131     protected abstract T doProvision(InternalContext context, Dependency<?> dependency)
132         throws InternalProvisionException;
133   }
134 
135   /**
136    * An base factory implementation that can be extended to provide a specialized implementation of
137    * a {@link ProviderWithExtensionVisitor} and also implements {@link InternalFactory}
138    */
139   abstract static class CyclicFactory<T> extends Factory<T> {
140 
141     CyclicFactory(InitializationTiming initializationTiming) {
142       super(initializationTiming);
143     }
144 
145     @Override
146     public final T get(
147         final InternalContext context, final Dependency<?> dependency, boolean linked)
148         throws InternalProvisionException {
149       final ConstructionContext<T> constructionContext = context.getConstructionContext(this);
150       // We have a circular reference between bindings. Return a proxy.
151       if (constructionContext.isConstructing()) {
152         Class<?> expectedType = dependency.getKey().getTypeLiteral().getRawType();
153         @SuppressWarnings("unchecked")
154         T proxyType =
155             (T) constructionContext.createProxy(context.getInjectorOptions(), expectedType);
156         return proxyType;
157       }
158       // Optimization: Don't go through the callback stack if no one's listening.
159       constructionContext.startConstruction();
160       try {
161         if (provisionCallback == null) {
162           return provision(dependency, context, constructionContext);
163         } else {
164           return provisionCallback.provision(
165               context,
166               new ProvisionCallback<T>() {
167                 @Override
168                 public T call() throws InternalProvisionException {
169                   return provision(dependency, context, constructionContext);
170                 }
171               });
172         }
173       } finally {
174         constructionContext.removeCurrentReference();
175         constructionContext.finishConstruction();
176       }
177     }
178 
179     private T provision(
180         Dependency<?> dependency,
181         InternalContext context,
182         ConstructionContext<T> constructionContext)
183         throws InternalProvisionException {
184       try {
185         T t = doProvision(context, dependency);
186         constructionContext.setProxyDelegates(t);
187         return t;
188       } catch (InternalProvisionException ipe) {
189         throw ipe.addSource(getSource());
190       } catch (Throwable t) {
191         throw InternalProvisionException.errorInProvider(t).addSource(getSource());
192       }
193     }
194   }
195 }
196