• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (C) 2008 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.spi;
18 
19 import static com.google.common.base.Preconditions.checkArgument;
20 import static com.google.inject.internal.InternalFlags.getIncludeStackTraceOption;
21 
22 import com.google.common.collect.ImmutableList;
23 import com.google.common.collect.Lists;
24 import com.google.common.collect.Maps;
25 import com.google.common.collect.Sets;
26 import com.google.inject.AbstractModule;
27 import com.google.inject.Binder;
28 import com.google.inject.Binding;
29 import com.google.inject.Key;
30 import com.google.inject.MembersInjector;
31 import com.google.inject.Module;
32 import com.google.inject.PrivateBinder;
33 import com.google.inject.PrivateModule;
34 import com.google.inject.Provider;
35 import com.google.inject.Scope;
36 import com.google.inject.Stage;
37 import com.google.inject.TypeLiteral;
38 import com.google.inject.binder.AnnotatedBindingBuilder;
39 import com.google.inject.binder.AnnotatedConstantBindingBuilder;
40 import com.google.inject.binder.AnnotatedElementBuilder;
41 import com.google.inject.internal.AbstractBindingBuilder;
42 import com.google.inject.internal.BindingBuilder;
43 import com.google.inject.internal.ConstantBindingBuilderImpl;
44 import com.google.inject.internal.Errors;
45 import com.google.inject.internal.ExposureBuilder;
46 import com.google.inject.internal.InternalFlags.IncludeStackTraceOption;
47 import com.google.inject.internal.MoreTypes;
48 import com.google.inject.internal.PrivateElementsImpl;
49 import com.google.inject.internal.ProviderMethodsModule;
50 import com.google.inject.internal.util.SourceProvider;
51 import com.google.inject.internal.util.StackTraceElements;
52 import com.google.inject.matcher.Matcher;
53 
54 import java.lang.annotation.Annotation;
55 import java.lang.reflect.Method;
56 import java.util.Arrays;
57 import java.util.Collection;
58 import java.util.Collections;
59 import java.util.List;
60 import java.util.Map;
61 import java.util.Set;
62 
63 /**
64  * Exposes elements of a module so they can be inspected, validated or {@link
65  * Element#applyTo(Binder) rewritten}.
66  *
67  * @author jessewilson@google.com (Jesse Wilson)
68  * @since 2.0
69  */
70 public final class Elements {
71 
72   private static final BindingTargetVisitor<Object, Object> GET_INSTANCE_VISITOR
73       = new DefaultBindingTargetVisitor<Object, Object>() {
74     @Override public Object visit(InstanceBinding<?> binding) {
75       return binding.getInstance();
76     }
77 
78     @Override protected Object visitOther(Binding<?> binding) {
79       throw new IllegalArgumentException();
80     }
81   };
82 
83   /**
84    * Records the elements executed by {@code modules}.
85    */
getElements(Module... modules)86   public static List<Element> getElements(Module... modules) {
87     return getElements(Stage.DEVELOPMENT, Arrays.asList(modules));
88   }
89 
90   /**
91    * Records the elements executed by {@code modules}.
92    */
getElements(Stage stage, Module... modules)93   public static List<Element> getElements(Stage stage, Module... modules) {
94     return getElements(stage, Arrays.asList(modules));
95   }
96 
97   /**
98    * Records the elements executed by {@code modules}.
99    */
getElements(Iterable<? extends Module> modules)100   public static List<Element> getElements(Iterable<? extends Module> modules) {
101     return getElements(Stage.DEVELOPMENT, modules);
102   }
103 
104   /**
105    * Records the elements executed by {@code modules}.
106    */
getElements(Stage stage, Iterable<? extends Module> modules)107   public static List<Element> getElements(Stage stage, Iterable<? extends Module> modules) {
108     RecordingBinder binder = new RecordingBinder(stage);
109     for (Module module : modules) {
110       binder.install(module);
111     }
112     binder.scanForAnnotatedMethods();
113     for (RecordingBinder child : binder.privateBinders) {
114       child.scanForAnnotatedMethods();
115     }
116     // Free the memory consumed by the stack trace elements cache
117     StackTraceElements.clearCache();
118     return Collections.unmodifiableList(binder.elements);
119   }
120 
121   private static class ElementsAsModule implements Module {
122     private final Iterable<? extends Element> elements;
123 
ElementsAsModule(Iterable<? extends Element> elements)124     ElementsAsModule(Iterable<? extends Element> elements) {
125       this.elements = elements;
126     }
127 
128     @Override
configure(Binder binder)129     public void configure(Binder binder) {
130       for (Element element : elements) {
131         element.applyTo(binder);
132       }
133     }
134   }
135 
136   /**
137    * Returns the module composed of {@code elements}.
138    */
getModule(final Iterable<? extends Element> elements)139   public static Module getModule(final Iterable<? extends Element> elements) {
140     return new ElementsAsModule(elements);
141   }
142 
143   @SuppressWarnings("unchecked")
getInstanceVisitor()144   static <T> BindingTargetVisitor<T, T> getInstanceVisitor() {
145     return (BindingTargetVisitor<T, T>) GET_INSTANCE_VISITOR;
146   }
147 
148   private static class ModuleInfo {
149     private final Binder binder;
150     private final ModuleSource moduleSource;
151     private final boolean skipScanning;
152 
ModuleInfo(Binder binder, ModuleSource moduleSource, boolean skipScanning)153     private ModuleInfo(Binder binder, ModuleSource moduleSource, boolean skipScanning) {
154       this.binder = binder;
155       this.moduleSource = moduleSource;
156       this.skipScanning = skipScanning;
157     }
158   }
159 
160   private static class RecordingBinder implements Binder, PrivateBinder {
161     private final Stage stage;
162     private final Map<Module, ModuleInfo> modules;
163     private final List<Element> elements;
164     private final Object source;
165     /** The current modules stack */
166     private ModuleSource moduleSource = null;
167     private final SourceProvider sourceProvider;
168     private final Set<ModuleAnnotatedMethodScanner> scanners;
169 
170     /** The binder where exposed bindings will be created */
171     private final RecordingBinder parent;
172     private final PrivateElementsImpl privateElements;
173 
174     /** All children private binders, so we can scan through them. */
175     private final List<RecordingBinder> privateBinders;
176 
RecordingBinder(Stage stage)177     private RecordingBinder(Stage stage) {
178       this.stage = stage;
179       this.modules = Maps.newLinkedHashMap();
180       this.scanners = Sets.newLinkedHashSet();
181       this.elements = Lists.newArrayList();
182       this.source = null;
183       this.sourceProvider = SourceProvider.DEFAULT_INSTANCE.plusSkippedClasses(
184           Elements.class, RecordingBinder.class, AbstractModule.class,
185           ConstantBindingBuilderImpl.class, AbstractBindingBuilder.class, BindingBuilder.class);
186       this.parent = null;
187       this.privateElements = null;
188       this.privateBinders = Lists.newArrayList();
189     }
190 
191     /** Creates a recording binder that's backed by {@code prototype}. */
RecordingBinder( RecordingBinder prototype, Object source, SourceProvider sourceProvider)192     private RecordingBinder(
193         RecordingBinder prototype, Object source, SourceProvider sourceProvider) {
194       checkArgument(source == null ^ sourceProvider == null);
195 
196       this.stage = prototype.stage;
197       this.modules = prototype.modules;
198       this.elements = prototype.elements;
199       this.scanners = prototype.scanners;
200       this.source = source;
201       this.moduleSource = prototype.moduleSource;
202       this.sourceProvider = sourceProvider;
203       this.parent = prototype.parent;
204       this.privateElements = prototype.privateElements;
205       this.privateBinders = prototype.privateBinders;
206     }
207 
208     /** Creates a private recording binder. */
RecordingBinder(RecordingBinder parent, PrivateElementsImpl privateElements)209     private RecordingBinder(RecordingBinder parent, PrivateElementsImpl privateElements) {
210       this.stage = parent.stage;
211       this.modules = Maps.newLinkedHashMap();
212       this.scanners = Sets.newLinkedHashSet(parent.scanners);
213       this.elements = privateElements.getElementsMutable();
214       this.source = parent.source;
215       this.moduleSource = parent.moduleSource;
216       this.sourceProvider = parent.sourceProvider;
217       this.parent = parent;
218       this.privateElements = privateElements;
219       this.privateBinders = parent.privateBinders;
220     }
221 
222     /*if[AOP]*/
223     @Override
bindInterceptor( Matcher<? super Class<?>> classMatcher, Matcher<? super Method> methodMatcher, org.aopalliance.intercept.MethodInterceptor... interceptors)224     public void bindInterceptor(
225         Matcher<? super Class<?>> classMatcher,
226         Matcher<? super Method> methodMatcher,
227         org.aopalliance.intercept.MethodInterceptor... interceptors) {
228       elements.add(new InterceptorBinding(
229           getElementSource(), classMatcher, methodMatcher, interceptors));
230     }
231     /*end[AOP]*/
232 
233     @Override
bindScope(Class<? extends Annotation> annotationType, Scope scope)234     public void bindScope(Class<? extends Annotation> annotationType, Scope scope) {
235       elements.add(new ScopeBinding(getElementSource(), annotationType, scope));
236     }
237 
238     @Override
239     @SuppressWarnings("unchecked") // it is safe to use the type literal for the raw type
requestInjection(Object instance)240     public void requestInjection(Object instance) {
241       requestInjection((TypeLiteral<Object>) TypeLiteral.get(instance.getClass()), instance);
242     }
243 
244     @Override
requestInjection(TypeLiteral<T> type, T instance)245     public <T> void requestInjection(TypeLiteral<T> type, T instance) {
246       elements.add(new InjectionRequest<T>(getElementSource(), MoreTypes.canonicalizeForKey(type),
247           instance));
248     }
249 
250     @Override
getMembersInjector(final TypeLiteral<T> typeLiteral)251     public <T> MembersInjector<T> getMembersInjector(final TypeLiteral<T> typeLiteral) {
252       final MembersInjectorLookup<T> element = new MembersInjectorLookup<T>(getElementSource(),
253           MoreTypes.canonicalizeForKey(typeLiteral));
254       elements.add(element);
255       return element.getMembersInjector();
256     }
257 
getMembersInjector(Class<T> type)258     public <T> MembersInjector<T> getMembersInjector(Class<T> type) {
259       return getMembersInjector(TypeLiteral.get(type));
260     }
261 
bindListener(Matcher<? super TypeLiteral<?>> typeMatcher, TypeListener listener)262     public void bindListener(Matcher<? super TypeLiteral<?>> typeMatcher, TypeListener listener) {
263       elements.add(new TypeListenerBinding(getElementSource(), listener, typeMatcher));
264     }
265 
bindListener(Matcher<? super Binding<?>> bindingMatcher, ProvisionListener... listeners)266     public void bindListener(Matcher<? super Binding<?>> bindingMatcher,
267         ProvisionListener... listeners) {
268       elements.add(new ProvisionListenerBinding(getElementSource(), bindingMatcher, listeners));
269     }
270 
requestStaticInjection(Class<?>.... types)271     public void requestStaticInjection(Class<?>... types) {
272       for (Class<?> type : types) {
273         elements.add(new StaticInjectionRequest(getElementSource(), type));
274       }
275     }
276 
277     /**
278      * Applies all scanners to the modules we've installed. We skip certain
279      * PrivateModules because store them in more than one Modules map and only
280      * want to process them through one of the maps.  (They're stored in both
281      * maps to prevent a module from being installed more than once.)
282      */
scanForAnnotatedMethods()283     void scanForAnnotatedMethods() {
284       for (ModuleAnnotatedMethodScanner scanner : scanners) {
285         // Note: we must iterate over a copy of the modules because calling install(..)
286         // will mutate modules, otherwise causing a ConcurrentModificationException.
287         for (Map.Entry<Module, ModuleInfo> entry : Maps.newLinkedHashMap(modules).entrySet()) {
288           Module module = entry.getKey();
289           ModuleInfo info = entry.getValue();
290           if (info.skipScanning) {
291             continue;
292           }
293           moduleSource = entry.getValue().moduleSource;
294           try {
295             info.binder.install(ProviderMethodsModule.forModule(module, scanner));
296           } catch(RuntimeException e) {
297             Collection<Message> messages = Errors.getMessagesFromThrowable(e);
298             if (!messages.isEmpty()) {
299               elements.addAll(messages);
300             } else {
301               addError(e);
302             }
303           }
304         }
305       }
306       moduleSource = null;
307     }
308 
install(Module module)309     public void install(Module module) {
310       if (!modules.containsKey(module)) {
311         RecordingBinder binder = this;
312         boolean unwrapModuleSource = false;
313         // Update the module source for the new module
314         if (module instanceof ProviderMethodsModule) {
315           // There are two reason's we'd want to get the module source in a ProviderMethodsModule.
316           // ModuleAnnotatedMethodScanner lets users scan their own modules for @Provides-like
317           // bindings.  If they install the module at a top-level, then moduleSource can be null.
318           // Also, if they pass something other than 'this' to it, we'd have the wrong source.
319           Object delegate = ((ProviderMethodsModule) module).getDelegateModule();
320           if (moduleSource == null
321               || !moduleSource.getModuleClassName().equals(delegate.getClass().getName())) {
322             moduleSource = getModuleSource(delegate);
323             unwrapModuleSource = true;
324           }
325         } else {
326           moduleSource = getModuleSource(module);
327           unwrapModuleSource = true;
328         }
329         boolean skipScanning = false;
330         if (module instanceof PrivateModule) {
331           binder = (RecordingBinder) binder.newPrivateBinder();
332           // Store the module in the private binder too so we scan for it.
333           binder.modules.put(module, new ModuleInfo(binder, moduleSource, false));
334           skipScanning = true; // don't scan this module in the parent's module set.
335         }
336         // Always store this in the parent binder (even if it was a private module)
337         // so that we know not to process it again, and so that scanners inherit down.
338         modules.put(module, new ModuleInfo(binder, moduleSource, skipScanning));
339         try {
340           module.configure(binder);
341         } catch (RuntimeException e) {
342           Collection<Message> messages = Errors.getMessagesFromThrowable(e);
343           if (!messages.isEmpty()) {
344             elements.addAll(messages);
345           } else {
346             addError(e);
347           }
348         }
349         binder.install(ProviderMethodsModule.forModule(module));
350         // We are done with this module, so undo module source change
351         if (unwrapModuleSource) {
352           moduleSource = moduleSource.getParent();
353         }
354       }
355     }
356 
currentStage()357     public Stage currentStage() {
358       return stage;
359     }
360 
addError(String message, Object... arguments)361     public void addError(String message, Object... arguments) {
362       elements.add(new Message(getElementSource(), Errors.format(message, arguments)));
363     }
364 
addError(Throwable t)365     public void addError(Throwable t) {
366       String message = "An exception was caught and reported. Message: " + t.getMessage();
367       elements.add(new Message(ImmutableList.of((Object) getElementSource()), message, t));
368     }
369 
addError(Message message)370     public void addError(Message message) {
371       elements.add(message);
372     }
373 
bind(Key<T> key)374     public <T> AnnotatedBindingBuilder<T> bind(Key<T> key) {
375       BindingBuilder<T> builder =
376           new BindingBuilder<T>(this, elements, getElementSource(), MoreTypes.canonicalizeKey(key));
377       return builder;
378     }
379 
bind(TypeLiteral<T> typeLiteral)380     public <T> AnnotatedBindingBuilder<T> bind(TypeLiteral<T> typeLiteral) {
381       return bind(Key.get(typeLiteral));
382     }
383 
bind(Class<T> type)384     public <T> AnnotatedBindingBuilder<T> bind(Class<T> type) {
385       return bind(Key.get(type));
386     }
387 
bindConstant()388     public AnnotatedConstantBindingBuilder bindConstant() {
389       return new ConstantBindingBuilderImpl<Void>(this, elements, getElementSource());
390     }
391 
getProvider(final Key<T> key)392     public <T> Provider<T> getProvider(final Key<T> key) {
393       return getProvider(Dependency.get(key));
394     }
395 
getProvider(final Dependency<T> dependency)396     public <T> Provider<T> getProvider(final Dependency<T> dependency) {
397       final ProviderLookup<T> element = new ProviderLookup<T>(getElementSource(), dependency);
398       elements.add(element);
399       return element.getProvider();
400     }
401 
getProvider(Class<T> type)402     public <T> Provider<T> getProvider(Class<T> type) {
403       return getProvider(Key.get(type));
404     }
405 
convertToTypes(Matcher<? super TypeLiteral<?>> typeMatcher, TypeConverter converter)406     public void convertToTypes(Matcher<? super TypeLiteral<?>> typeMatcher,
407         TypeConverter converter) {
408       elements.add(new TypeConverterBinding(getElementSource(), typeMatcher, converter));
409     }
410 
withSource(final Object source)411     public RecordingBinder withSource(final Object source) {
412       return source == this.source ? this : new RecordingBinder(this, source, null);
413     }
414 
skipSources(Class... classesToSkip)415     public RecordingBinder skipSources(Class... classesToSkip) {
416       // if a source is specified explicitly, we don't need to skip sources
417       if (source != null) {
418         return this;
419       }
420 
421       SourceProvider newSourceProvider = sourceProvider.plusSkippedClasses(classesToSkip);
422       return new RecordingBinder(this, null, newSourceProvider);
423     }
424 
425     @Override
newPrivateBinder()426     public PrivateBinder newPrivateBinder() {
427       PrivateElementsImpl privateElements = new PrivateElementsImpl(getElementSource());
428       RecordingBinder binder = new RecordingBinder(this, privateElements);
429       privateBinders.add(binder);
430       elements.add(privateElements);
431       return binder;
432     }
433 
434     @Override
disableCircularProxies()435     public void disableCircularProxies() {
436       elements.add(new DisableCircularProxiesOption(getElementSource()));
437     }
438 
439     @Override
requireExplicitBindings()440     public void requireExplicitBindings() {
441       elements.add(new RequireExplicitBindingsOption(getElementSource()));
442     }
443 
444     @Override
requireAtInjectOnConstructors()445     public void requireAtInjectOnConstructors() {
446       elements.add(new RequireAtInjectOnConstructorsOption(getElementSource()));
447     }
448 
449     @Override
requireExactBindingAnnotations()450     public void requireExactBindingAnnotations() {
451       elements.add(new RequireExactBindingAnnotationsOption(getElementSource()));
452     }
453 
454     @Override
scanModulesForAnnotatedMethods(ModuleAnnotatedMethodScanner scanner)455     public void scanModulesForAnnotatedMethods(ModuleAnnotatedMethodScanner scanner) {
456       scanners.add(scanner);
457       elements.add(new ModuleAnnotatedMethodScannerBinding(getElementSource(), scanner));
458     }
459 
expose(Key<?> key)460     public void expose(Key<?> key) {
461       exposeInternal(key);
462     }
463 
464     @Override
expose(Class<?> type)465     public AnnotatedElementBuilder expose(Class<?> type) {
466       return exposeInternal(Key.get(type));
467     }
468 
469     @Override
expose(TypeLiteral<?> type)470     public AnnotatedElementBuilder expose(TypeLiteral<?> type) {
471       return exposeInternal(Key.get(type));
472     }
473 
exposeInternal(Key<T> key)474     private <T> AnnotatedElementBuilder exposeInternal(Key<T> key) {
475       if (privateElements == null) {
476         addError("Cannot expose %s on a standard binder. "
477             + "Exposed bindings are only applicable to private binders.", key);
478         return new AnnotatedElementBuilder() {
479           @Override
480           public void annotatedWith(Class<? extends Annotation> annotationType) {}
481           @Override
482           public void annotatedWith(Annotation annotation) {}
483         };
484       }
485 
486       ExposureBuilder<T> builder =
487           new ExposureBuilder<T>(this, getElementSource(), MoreTypes.canonicalizeKey(key));
488       privateElements.addExposureBuilder(builder);
489       return builder;
490     }
491 
getModuleSource(Object module)492     private ModuleSource getModuleSource(Object module) {
493       StackTraceElement[] partialCallStack;
494       if (getIncludeStackTraceOption() == IncludeStackTraceOption.COMPLETE) {
495         partialCallStack = getPartialCallStack(new Throwable().getStackTrace());
496       } else {
497         partialCallStack = new StackTraceElement[0];
498       }
499       if (moduleSource == null) {
500         return new ModuleSource(module, partialCallStack);
501       }
502       return moduleSource.createChild(module, partialCallStack);
503     }
504 
getElementSource()505     private ElementSource getElementSource() {
506       // Full call stack
507       StackTraceElement[] callStack = null;
508       // The call stack starts from current top module configure and ends at this method caller
509       StackTraceElement[] partialCallStack = new StackTraceElement[0];
510       // The element original source
511       ElementSource originalSource = null;
512       // The element declaring source
513       Object declaringSource = source;
514       if (declaringSource instanceof ElementSource) {
515         originalSource = (ElementSource) declaringSource;
516         declaringSource = originalSource.getDeclaringSource();
517       }
518       IncludeStackTraceOption stackTraceOption = getIncludeStackTraceOption();
519       if (stackTraceOption == IncludeStackTraceOption.COMPLETE ||
520           (stackTraceOption == IncludeStackTraceOption.ONLY_FOR_DECLARING_SOURCE
521           && declaringSource == null)) {
522         callStack = new Throwable().getStackTrace();
523       }
524       if (stackTraceOption == IncludeStackTraceOption.COMPLETE) {
525         partialCallStack = getPartialCallStack(callStack);
526       }
527       if (declaringSource == null) {
528         // So 'source' and 'originalSource' are null otherwise declaringSource has some value
529         if (stackTraceOption == IncludeStackTraceOption.COMPLETE ||
530             stackTraceOption == IncludeStackTraceOption.ONLY_FOR_DECLARING_SOURCE) {
531           // With the above conditions and assignments 'callStack' is non-null
532           declaringSource = sourceProvider.get(callStack);
533         } else { // or if (stackTraceOption == IncludeStackTraceOptions.OFF)
534           // As neither 'declaring source' nor 'call stack' is available use 'module source'
535           declaringSource = sourceProvider.getFromClassNames(moduleSource.getModuleClassNames());
536         }
537       }
538       // Build the binding call stack
539       return new ElementSource(
540           originalSource, declaringSource, moduleSource, partialCallStack);
541     }
542 
543     /**
544      * Removes the {@link #moduleSource} call stack from the beginning of current call stack. It
545      * also removes the last two elements in order to make {@link #install(Module)} the last call
546      * in the call stack.
547      */
getPartialCallStack(StackTraceElement[] callStack)548     private StackTraceElement[] getPartialCallStack(StackTraceElement[] callStack) {
549       int toSkip = 0;
550       if (moduleSource != null) {
551         toSkip = moduleSource.getStackTraceSize();
552       }
553       // -1 for skipping 'getModuleSource' and 'getElementSource' calls
554       int chunkSize = callStack.length - toSkip - 1;
555 
556       StackTraceElement[] partialCallStack = new StackTraceElement[chunkSize];
557       System.arraycopy(callStack, 1, partialCallStack, 0, chunkSize);
558       return partialCallStack;
559     }
560 
toString()561     @Override public String toString() {
562       return "Binder";
563     }
564   }
565 }
566