All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.inject.spi.Elements Maven / Gradle / Ivy

There is a newer version: 3.0.0-alpha-3
Show newest version
/*
 * Copyright (C) 2008 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.inject.spi;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.inject.internal.InternalFlags.getIncludeStackTraceOption;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.inject.AbstractModule;
import com.google.inject.Binder;
import com.google.inject.Binding;
import com.google.inject.Key;
import com.google.inject.MembersInjector;
import com.google.inject.Module;
import com.google.inject.PrivateBinder;
import com.google.inject.PrivateModule;
import com.google.inject.Provider;
import com.google.inject.Scope;
import com.google.inject.Stage;
import com.google.inject.TypeLiteral;
import com.google.inject.binder.AnnotatedBindingBuilder;
import com.google.inject.binder.AnnotatedConstantBindingBuilder;
import com.google.inject.binder.AnnotatedElementBuilder;
import com.google.inject.internal.AbstractBindingBuilder;
import com.google.inject.internal.BindingBuilder;
import com.google.inject.internal.ConstantBindingBuilderImpl;
import com.google.inject.internal.Errors;
import com.google.inject.internal.ExposureBuilder;
import com.google.inject.internal.GuiceInternal;
import com.google.inject.internal.InternalClassesToSkipSources;
import com.google.inject.internal.InternalFlags.IncludeStackTraceOption;
import com.google.inject.internal.MoreTypes;
import com.google.inject.internal.PrivateElementsImpl;
import com.google.inject.internal.ProviderMethod;
import com.google.inject.internal.ProviderMethodsModule;
import com.google.inject.internal.util.SourceProvider;
import com.google.inject.internal.util.StackTraceElements;
import com.google.inject.matcher.Matcher;
import com.google.inject.multibindings.MapBinder;
import com.google.inject.multibindings.Multibinder;
import com.google.inject.multibindings.OptionalBinder;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.aopalliance.intercept.MethodInterceptor;

/**
 * Exposes elements of a module so they can be inspected, validated or {@link
 * Element#applyTo(Binder) rewritten}.
 *
 * @author [email protected] (Jesse Wilson)
 * @since 2.0
 */
public final class Elements {

  private static final BindingTargetVisitor GET_INSTANCE_VISITOR =
      new DefaultBindingTargetVisitor() {
        @Override
        public Object visit(InstanceBinding binding) {
          return binding.getInstance();
        }

        @Override
        protected Object visitOther(Binding binding) {
          throw new IllegalArgumentException();
        }
      };

  /** Records the elements executed by {@code modules}. */
  public static List getElements(Module... modules) {
    return getElements(Stage.DEVELOPMENT, Arrays.asList(modules));
  }

  /** Records the elements executed by {@code modules}. */
  public static List getElements(Stage stage, Module... modules) {
    return getElements(stage, Arrays.asList(modules));
  }

  /** Records the elements executed by {@code modules}. */
  public static List getElements(Iterable modules) {
    return getElements(Stage.DEVELOPMENT, modules);
  }

  /** Records the elements executed by {@code modules}. */
  public static List getElements(Stage stage, Iterable modules) {
    RecordingBinder binder = new RecordingBinder(stage);
    for (Module module : modules) {
      binder.install(module);
    }
    binder.scanForAnnotatedMethods();
    for (RecordingBinder child : binder.privateBindersForScanning) {
      child.scanForAnnotatedMethods();
    }
    binder.permitMapConstruction.finish();
    // Free the memory consumed by the stack trace elements cache
    StackTraceElements.clearCache();
    return Collections.unmodifiableList(binder.elements);
  }

  // TODO(user): Consider moving the RecordingBinder to com.google.inject.internal and removing these
  // internal 'friend' methods.
  /**
   * Internal version of Binder.withSource for establishing a trusted ElementSource chain for
   * source-restricting bindings that are re-written using {@link Element#applyTo}.
   *
   * 

Using Binder.withSource is not trustworthy because it's a public API that external users can * use to spoof the original ElementSource of a binding by calling withSource(bogusElementSource). * * @since 5.0 */ public static Binder withTrustedSource( GuiceInternal guiceInternal, Binder binder, Object source) { checkNotNull(guiceInternal); if (binder instanceof RecordingBinder) { return ((RecordingBinder) binder).withTrustedSource(source); } // Preserve existing (untrusted) behavior for non-standard Binder implementations. return binder.withSource(source); } private static class ElementsAsModule implements Module { private final Iterable elements; ElementsAsModule(Iterable elements) { this.elements = elements; } @Override public void configure(Binder binder) { for (Element element : elements) { element.applyTo(binder); } } } /** Returns the module composed of {@code elements}. */ public static Module getModule(final Iterable elements) { return new ElementsAsModule(elements); } @SuppressWarnings("unchecked") static BindingTargetVisitor getInstanceVisitor() { return (BindingTargetVisitor) GET_INSTANCE_VISITOR; } private static class ModuleInfo { private final ModuleSource moduleSource; private final boolean skipScanning; private ModuleInfo(ModuleSource moduleSource, boolean skipScanning) { this.moduleSource = moduleSource; this.skipScanning = skipScanning; } } private static class RecordingBinder implements Binder, PrivateBinder { private static final ImmutableSet> CLASSES_TO_SKIP = ImmutableSet.>builder() .add( Elements.class, RecordingBinder.class, AbstractModule.class, ConstantBindingBuilderImpl.class, AbstractBindingBuilder.class, BindingBuilder.class, MapBinder.class, Multibinder.class, OptionalBinder.class) .addAll(InternalClassesToSkipSources.classesToSkipSources()) .build(); private static final SourceProvider DEFAULT_SOURCE_PROVIDER = SourceProvider.DEFAULT_INSTANCE.plusSkippedClasses( CLASSES_TO_SKIP.toArray(new Class[0])); private final Stage stage; private final Map modules; private final List elements; private final Object source; private final SourceProvider sourceProvider; private final Set scanners; /** The binder where exposed bindings will be created */ private final RecordingBinder parent; private final PrivateElementsImpl privateElements; /** All children private binders, so we can scan through them. */ private final List privateBindersForScanning; private final BindingSourceRestriction.PermitMapConstruction permitMapConstruction; /** The current modules stack */ private ModuleSource moduleSource = null; /** * The current scanner. * *

Note that scanners cannot nest, ie. a scanner cannot install a module that requires * scanning - except the built-in @Provides* methods. The built-in scanner isn't tracked by this * variable, only custom scanners are. */ private ModuleAnnotatedMethodScanner scannerSource = null; private ModuleAnnotatedMethodScanner currentScanner = null; private boolean trustedSource = false; private RecordingBinder(Stage stage) { this.stage = stage; this.modules = Maps.newLinkedHashMap(); this.scanners = Sets.newLinkedHashSet(); this.elements = Lists.newArrayList(); this.source = null; this.sourceProvider = DEFAULT_SOURCE_PROVIDER; this.parent = null; this.privateElements = null; this.privateBindersForScanning = Lists.newArrayList(); this.permitMapConstruction = new BindingSourceRestriction.PermitMapConstruction(); } /** Creates a recording binder that's backed by {@code prototype}. */ private RecordingBinder( RecordingBinder prototype, Object source, SourceProvider sourceProvider, boolean trustedSource) { checkArgument(source == null ^ sourceProvider == null); this.stage = prototype.stage; this.modules = prototype.modules; this.elements = prototype.elements; this.scanners = prototype.scanners; this.currentScanner = prototype.currentScanner; this.source = source; this.trustedSource = trustedSource; this.moduleSource = prototype.moduleSource; this.sourceProvider = sourceProvider; this.parent = prototype.parent; this.privateElements = prototype.privateElements; this.privateBindersForScanning = prototype.privateBindersForScanning; this.permitMapConstruction = prototype.permitMapConstruction; this.scannerSource = prototype.scannerSource; } /** Creates a private recording binder. */ private RecordingBinder(RecordingBinder parent, PrivateElementsImpl privateElements) { this.stage = parent.stage; this.modules = Maps.newLinkedHashMap(); this.scanners = Sets.newLinkedHashSet(); this.currentScanner = parent.currentScanner; this.elements = privateElements.getElementsMutable(); this.source = parent.source; this.moduleSource = parent.moduleSource; this.sourceProvider = parent.sourceProvider; this.parent = parent; this.privateElements = privateElements; this.privateBindersForScanning = parent.privateBindersForScanning; this.permitMapConstruction = parent.permitMapConstruction; this.scannerSource = parent.scannerSource; } @Override public void bindInterceptor( Matcher> classMatcher, Matcher methodMatcher, MethodInterceptor... interceptors) { elements.add( new InterceptorBinding(getElementSource(), classMatcher, methodMatcher, interceptors)); } @Override public void bindScope(Class annotationType, Scope scope) { elements.add(new ScopeBinding(getElementSource(), annotationType, scope)); } @Override @SuppressWarnings("unchecked") // it is safe to use the type literal for the raw type public void requestInjection(Object instance) { checkNotNull(instance, "instance"); requestInjection((TypeLiteral) TypeLiteral.get(instance.getClass()), instance); } @Override public void requestInjection(TypeLiteral type, T instance) { checkNotNull(instance, "instance"); elements.add( new InjectionRequest( getElementSource(), MoreTypes.canonicalizeForKey(type), instance)); } @Override public MembersInjector getMembersInjector(final TypeLiteral typeLiteral) { final MembersInjectorLookup element = new MembersInjectorLookup( getElementSource(), MoreTypes.canonicalizeForKey(typeLiteral)); elements.add(element); return element.getMembersInjector(); } @Override public MembersInjector getMembersInjector(Class type) { return getMembersInjector(TypeLiteral.get(type)); } @Override public void bindListener(Matcher> typeMatcher, TypeListener listener) { elements.add(new TypeListenerBinding(getElementSource(), listener, typeMatcher)); } @Override public void bindListener( Matcher> bindingMatcher, ProvisionListener... listeners) { elements.add(new ProvisionListenerBinding(getElementSource(), bindingMatcher, listeners)); } @Override public void requestStaticInjection(Class... types) { for (Class type : types) { elements.add(new StaticInjectionRequest(getElementSource(), type)); } } /** * Applies all scanners to the modules we've installed. We skip certain PrivateModules because * store them in more than one Modules map and only want to process them through one of the * maps. (They're stored in both maps to prevent a module from being installed more than once.) */ void scanForAnnotatedMethods() { Iterable scanners = getAllScanners(); // Note: we must iterate over a copy of the modules because calling install(..) // will mutate modules, otherwise causing a ConcurrentModificationException. for (Map.Entry entry : Maps.newLinkedHashMap(modules).entrySet()) { Module module = entry.getKey(); ModuleInfo info = entry.getValue(); if (info.skipScanning) { continue; } for (ModuleAnnotatedMethodScanner scanner : scanners) { currentScanner = scanner; moduleSource = entry.getValue().moduleSource; permitMapConstruction.restoreCurrentModulePermits(moduleSource); try { install(ProviderMethodsModule.forModule(module, scanner)); } catch (RuntimeException e) { Collection messages = Errors.getMessagesFromThrowable(e); if (!messages.isEmpty()) { elements.addAll(messages); } else { addError(e); } } } } moduleSource = null; } @Override public void install(Module module) { // Ignore duplicate installations of the same module instance. if (modules.containsKey(module)) { return; } // Whether the module installed is a ProviderMethodModule for a custom scanner. boolean customScanner = false; Class newModuleClass = null; RecordingBinder binder = this; // Update the module source for the new module if (module instanceof ProviderMethodsModule) { ProviderMethodsModule providerMethodsModule = (ProviderMethodsModule) module; if (!providerMethodsModule.isScanningBuiltInProvidesMethods()) { scannerSource = providerMethodsModule.getScanner(); customScanner = true; } // There are two reason's we'd want to get the module source in a ProviderMethodsModule. // ModuleAnnotatedMethodScanner lets users scan their own modules for @Provides-like // bindings. If they install the module at a top-level, then moduleSource can be null. // Also, if they pass something other than 'this' to it, we'd have the wrong source. Class delegateClass = providerMethodsModule.getDelegateModuleClass(); if (moduleSource == null || !moduleSource.getModuleClassName().equals(delegateClass.getName())) { newModuleClass = delegateClass; } } else { if (moduleScanning()) { forbidNestedScannerMethods(module); } newModuleClass = module.getClass(); } if (newModuleClass != null) { moduleSource = getModuleSource(newModuleClass); permitMapConstruction.pushModule(newModuleClass, moduleSource); } boolean skipScanning = false; if (module instanceof PrivateModule) { binder = (RecordingBinder) binder.newPrivateBinder(); // Store the module in the private binder too so we scan for it. binder.modules.put(module, new ModuleInfo(moduleSource, false)); skipScanning = true; // don't scan this module in the parent's module set. } // Always store this in the parent binder (even if it was a private module) // so that we know not to process it again, and so that scanners inherit down. modules.put(module, new ModuleInfo(moduleSource, skipScanning)); try { module.configure(binder); } catch (RuntimeException e) { Collection messages = Errors.getMessagesFromThrowable(e); if (!messages.isEmpty()) { elements.addAll(messages); } else { addError(e); } } binder.install(ProviderMethodsModule.forModule(module)); // We are done with this module, so undo module source change if (newModuleClass != null) { moduleSource = moduleSource.getParent(); permitMapConstruction.popModule(); } // Only wipe the scannerSource once custom scanner installation is finished. This way all // bindings created by the custom scanner will have it as their scanner source, including // bindings created by the built-in scanner scanning @Provides* methods in modules installed // by the custom scanner. if (customScanner) { scannerSource = null; } } private void forbidNestedScannerMethods(Module module) { for (ModuleAnnotatedMethodScanner scanner : getAllScanners()) { ProviderMethodsModule providerMethodsModule = (ProviderMethodsModule) ProviderMethodsModule.forModule(module, scanner); for (ProviderMethod method : providerMethodsModule.getProviderMethods(this)) { addError( "Scanner %s is installing a module with %s method. Installing modules with custom " + "provides methods from a ModuleAnnotatedMethodScanner is not supported.", currentScanner, method.getAnnotation().annotationType().getCanonicalName()); } } } /** * Get all scanners registered in this binder and its ancestors. * *

Should only be called during module scanning, because at that point registering new * scanners is forbidden. */ private Iterable getAllScanners() { if (privateElements == null) { return scanners; } // Private binders have their own set of scanners and they inherit from their parent. return Iterables.concat(scanners, parent.getAllScanners()); } @Override public Stage currentStage() { return stage; } @Override public void addError(String message, Object... arguments) { elements.add(new Message(getElementSource(), Errors.format(message, arguments))); } @Override public void addError(Throwable t) { String message = "An exception was caught and reported. Message: " + t.getMessage(); elements.add(new Message(ImmutableList.of((Object) getElementSource()), message, t)); } @Override public void addError(Message message) { elements.add(message); } @Override public AnnotatedBindingBuilder bind(Key key) { BindingBuilder builder = new BindingBuilder(this, elements, getElementSource(), MoreTypes.canonicalizeKey(key)); return builder; } @Override public AnnotatedBindingBuilder bind(TypeLiteral typeLiteral) { return bind(Key.get(typeLiteral)); } @Override public AnnotatedBindingBuilder bind(Class type) { return bind(Key.get(type)); } @Override public AnnotatedConstantBindingBuilder bindConstant() { return new ConstantBindingBuilderImpl(this, elements, getElementSource()); } @Override public Provider getProvider(final Key key) { return getProvider(Dependency.get(key)); } @Override public Provider getProvider(final Dependency dependency) { final ProviderLookup element = new ProviderLookup<>(getElementSource(), dependency); elements.add(element); return element.getProvider(); } @Override public Provider getProvider(Class type) { return getProvider(Key.get(type)); } @Override public void convertToTypes( Matcher> typeMatcher, TypeConverter converter) { elements.add(new TypeConverterBinding(getElementSource(), typeMatcher, converter)); } @Override public RecordingBinder withSource(final Object source) { return source == this.source ? this : new RecordingBinder( this, source, /* sourceProvider= */ null, /* trustedSource= */ false); } public RecordingBinder withTrustedSource(final Object source) { return source == this.source ? this : new RecordingBinder( this, source, /* sourceProvider= */ null, /* trustedSource= */ true); } @Override public RecordingBinder skipSources(Class... classesToSkip) { // if a source is specified explicitly, we don't need to skip sources if (source != null) { return this; } SourceProvider newSourceProvider = sourceProvider.plusSkippedClasses(classesToSkip); return new RecordingBinder( this, /* source= */ null, newSourceProvider, /* trustedSource= */ false); } @Override public PrivateBinder newPrivateBinder() { PrivateElementsImpl privateElements = new PrivateElementsImpl(getElementSource()); RecordingBinder binder = new RecordingBinder(this, privateElements); elements.add(privateElements); // Don't want to scan private modules installed by scanners. if (!moduleScanning()) { privateBindersForScanning.add(binder); } return binder; } @Override public void disableCircularProxies() { elements.add(new DisableCircularProxiesOption(getElementSource())); } @Override public void requireExplicitBindings() { elements.add(new RequireExplicitBindingsOption(getElementSource())); } @Override public void requireAtInjectOnConstructors() { elements.add(new RequireAtInjectOnConstructorsOption(getElementSource())); } @Override public void requireExactBindingAnnotations() { elements.add(new RequireExactBindingAnnotationsOption(getElementSource())); } @Override public void scanModulesForAnnotatedMethods(ModuleAnnotatedMethodScanner scanner) { if (moduleScanning()) { addError( "Attempting to register ModuleAnnotatedMethodScanner %s from scanner %s. Scanners are" + " not allowed to register other scanners.", currentScanner, scanner); return; } scanners.add(scanner); elements.add(new ModuleAnnotatedMethodScannerBinding(getElementSource(), scanner)); } @Override public void expose(Key key) { exposeInternal(key); } @Override public AnnotatedElementBuilder expose(Class type) { return exposeInternal(Key.get(type)); } @Override public AnnotatedElementBuilder expose(TypeLiteral type) { return exposeInternal(Key.get(type)); } private AnnotatedElementBuilder exposeInternal(Key key) { if (privateElements == null) { addError( "Cannot expose %s on a standard binder. " + "Exposed bindings are only applicable to private binders.", key); return new AnnotatedElementBuilder() { @Override public void annotatedWith(Class annotationType) {} @Override public void annotatedWith(Annotation annotation) {} }; } ExposureBuilder builder = new ExposureBuilder(this, getElementSource(), MoreTypes.canonicalizeKey(key)); privateElements.addExposureBuilder(builder); return builder; } private ModuleSource getModuleSource(Class module) { if (moduleSource == null) { return new ModuleSource(module, permitMapConstruction.getPermitMap()); } return moduleSource.createChild(module); } private ElementSource getElementSource() { // The element original source ElementSource originalSource = null; // The element declaring source Object declaringSource = source; if (declaringSource instanceof ElementSource) { originalSource = (ElementSource) declaringSource; declaringSource = originalSource.getDeclaringSource(); } if (declaringSource == null) { IncludeStackTraceOption stackTraceOption = getIncludeStackTraceOption(); if (stackTraceOption == IncludeStackTraceOption.ONLY_FOR_DECLARING_SOURCE) { StackTraceElement callingSource = sourceProvider.getCaller(); // If we've traversed past all reasonable sources and into our internal code, then we // don't know the source. if (callingSource .getClassName() .equals("com.google.inject.internal.InjectorShell$Builder") && callingSource.getMethodName().equals("build")) { declaringSource = SourceProvider.UNKNOWN_SOURCE; } else { declaringSource = callingSource; } } else { // As neither 'declaring source' nor 'call stack' is available use 'module source' declaringSource = sourceProvider.getFromClassNames(moduleSource.getModuleClassNames()); } } // Build the binding call stack return new ElementSource( originalSource, trustedSource, declaringSource, moduleSource, scannerSource); } /** Returns if the binder is in the module scanning phase. */ private boolean moduleScanning() { return currentScanner != null; } @Override public String toString() { return "Binder"; } } }