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