Internal: Remove child injectors from guice

This change removes the ability for guice to have child injectors (and
the entire concept of parent injectors) from our fork of guice. The
methodology for removing was simple: I removed createChildInjector, and
continued to remove methods and members that were unused until my head
was spinning. The motivation for this change is to limit what our fork
of guice gives us access to, so we don't regress and start adding back
more complicated uses.
This commit is contained in:
Ryan Ernst 2016-07-08 14:57:44 -07:00
parent dea00a0b16
commit 7e59181e58
16 changed files with 15 additions and 1027 deletions

View File

@ -38,12 +38,6 @@ import java.util.Map;
* An injector can also {@link #injectMembers(Object) inject the dependencies} of * An injector can also {@link #injectMembers(Object) inject the dependencies} of
* already-constructed instances. This can be used to interoperate with objects created by other * already-constructed instances. This can be used to interoperate with objects created by other
* frameworks or services. * frameworks or services.
* <p>
* Injectors can be {@link #createChildInjector(Iterable) hierarchical}. Child injectors inherit
* the configuration of their parent injectors, but the converse does not hold.
* <p>
* The injector's {@link #getBindings() internal bindings} are available for introspection. This
* enables tools and extensions to operate on an injector reflectively.
* *
* @author crazybob@google.com (Bob Lee) * @author crazybob@google.com (Bob Lee)
* @author jessewilson@google.com (Jesse Wilson) * @author jessewilson@google.com (Jesse Wilson)
@ -87,41 +81,6 @@ public interface Injector {
*/ */
<T> MembersInjector<T> getMembersInjector(Class<T> type); <T> MembersInjector<T> getMembersInjector(Class<T> type);
/**
* Returns all explicit bindings.
* <p>
* The returned map does not include bindings inherited from a {@link #getParent() parent
* injector}, should one exist. The returned map is guaranteed to iterate (for example, with
* its {@link java.util.Map#entrySet()} iterator) in the order of insertion. In other words,
* the order in which bindings appear in user Modules.
* <p>
* This method is part of the Guice SPI and is intended for use by tools and extensions.
*/
Map<Key<?>, Binding<?>> getBindings();
/**
* Returns the binding for the given injection key. This will be an explicit bindings if the key
* was bound explicitly by a module, or an implicit binding otherwise. The implicit binding will
* be created if necessary.
* <p>
* This method is part of the Guice SPI and is intended for use by tools and extensions.
*
* @throws ConfigurationException if this injector cannot find or create the binding.
*/
<T> Binding<T> getBinding(Key<T> key);
/**
* Returns the binding for the given type. This will be an explicit bindings if the injection key
* was bound explicitly by a module, or an implicit binding otherwise. The implicit binding will
* be created if necessary.
* <p>
* This method is part of the Guice SPI and is intended for use by tools and extensions.
*
* @throws ConfigurationException if this injector cannot find or create the binding.
* @since 2.0
*/
<T> Binding<T> getBinding(Class<T> type);
/** /**
* Returns all explicit bindings for {@code type}. * Returns all explicit bindings for {@code type}.
* <p> * <p>
@ -166,45 +125,4 @@ public interface Injector {
* @throws ProvisionException if there was a runtime failure while providing an instance. * @throws ProvisionException if there was a runtime failure while providing an instance.
*/ */
<T> T getInstance(Class<T> type); <T> T getInstance(Class<T> type);
/**
* Returns this injector's parent, or {@code null} if this is a top-level injector.
*
* @since 2.0
*/
Injector getParent();
/**
* Returns a new injector that inherits all state from this injector. All bindings, scopes,
* interceptors and type converters are inherited -- they are visible to the child injector.
* Elements of the child injector are not visible to its parent.
* <p>
* Just-in-time bindings created for child injectors will be created in an ancestor injector
* whenever possible. This allows for scoped instances to be shared between injectors. Use
* explicit bindings to prevent bindings from being shared with the parent injector.
* <p>
* No key may be bound by both an injector and one of its ancestors. This includes just-in-time
* bindings. The lone exception is the key for {@code Injector.class}, which is bound by each
* injector to itself.
*
* @since 2.0
*/
Injector createChildInjector(Iterable<? extends Module> modules);
/**
* Returns a new injector that inherits all state from this injector. All bindings, scopes,
* interceptors and type converters are inherited -- they are visible to the child injector.
* Elements of the child injector are not visible to its parent.
* <p>
* Just-in-time bindings created for child injectors will be created in an ancestor injector
* whenever possible. This allows for scoped instances to be shared between injectors. Use
* explicit bindings to prevent bindings from being shared with the parent injector.
* <p>
* No key may be bound by both an injector and one of its ancestors. This includes just-in-time
* bindings. The lone exception is the key for {@code Injector.class}, which is bound by each
* injector to itself.
*
* @since 2.0
*/
Injector createChildInjector(Module... modules);
} }

View File

@ -74,15 +74,6 @@ class InjectorBuilder {
return this; return this;
} }
/**
* Sets the parent of the injector to-be-constructed. As a side effect, this sets this injector's
* stage to the stage of {@code parent}.
*/
InjectorBuilder parentInjector(InjectorImpl parent) {
shellBuilder.parent(parent);
return stage(parent.getInstance(Stage.class));
}
InjectorBuilder addModules(Iterable<? extends Module> modules) { InjectorBuilder addModules(Iterable<? extends Module> modules) {
shellBuilder.addModules(modules); shellBuilder.addModules(modules);
return this; return this;
@ -102,11 +93,6 @@ class InjectorBuilder {
initializeStatically(); initializeStatically();
} }
// If we're in the tool stage, stop here. Don't eagerly inject or load anything.
if (stage == Stage.TOOL) {
return new ToolStageInjector(primaryInjector());
}
injectDynamically(); injectDynamically();
return primaryInjector(); return primaryInjector();
@ -217,92 +203,4 @@ class InjectorBuilder {
} }
} }
} }
/**
* {@link Injector} exposed to users in {@link Stage#TOOL}.
*/
static class ToolStageInjector implements Injector {
private final Injector delegateInjector;
ToolStageInjector(Injector delegateInjector) {
this.delegateInjector = delegateInjector;
}
@Override
public void injectMembers(Object o) {
throw new UnsupportedOperationException(
"Injector.injectMembers(Object) is not supported in Stage.TOOL");
}
@Override
public Map<Key<?>, Binding<?>> getBindings() {
return this.delegateInjector.getBindings();
}
@Override
public <T> Binding<T> getBinding(Key<T> key) {
return this.delegateInjector.getBinding(key);
}
@Override
public <T> Binding<T> getBinding(Class<T> type) {
return this.delegateInjector.getBinding(type);
}
@Override
public <T> List<Binding<T>> findBindingsByType(TypeLiteral<T> type) {
return this.delegateInjector.findBindingsByType(type);
}
@Override
public Injector getParent() {
return delegateInjector.getParent();
}
@Override
public Injector createChildInjector(Iterable<? extends Module> modules) {
return delegateInjector.createChildInjector(modules);
}
@Override
public Injector createChildInjector(Module... modules) {
return delegateInjector.createChildInjector(modules);
}
@Override
public <T> Provider<T> getProvider(Key<T> key) {
throw new UnsupportedOperationException(
"Injector.getProvider(Key<T>) is not supported in Stage.TOOL");
}
@Override
public <T> Provider<T> getProvider(Class<T> type) {
throw new UnsupportedOperationException(
"Injector.getProvider(Class<T>) is not supported in Stage.TOOL");
}
@Override
public <T> MembersInjector<T> getMembersInjector(TypeLiteral<T> typeLiteral) {
throw new UnsupportedOperationException(
"Injector.getMembersInjector(TypeLiteral<T>) is not supported in Stage.TOOL");
}
@Override
public <T> MembersInjector<T> getMembersInjector(Class<T> type) {
throw new UnsupportedOperationException(
"Injector.getMembersInjector(Class<T>) is not supported in Stage.TOOL");
}
@Override
public <T> T getInstance(Key<T> key) {
throw new UnsupportedOperationException(
"Injector.getInstance(Key<T>) is not supported in Stage.TOOL");
}
@Override
public <T> T getInstance(Class<T> type) {
throw new UnsupportedOperationException(
"Injector.getInstance(Class<T>) is not supported in Stage.TOOL");
}
}
} }

View File

@ -64,7 +64,6 @@ import static org.elasticsearch.common.inject.internal.Annotations.findScopeAnno
*/ */
class InjectorImpl implements Injector, Lookups { class InjectorImpl implements Injector, Lookups {
final State state; final State state;
final InjectorImpl parent;
boolean readOnly; boolean readOnly;
BindingsMultimap bindingsMultimap = new BindingsMultimap(); BindingsMultimap bindingsMultimap = new BindingsMultimap();
final Initializer initializer; final Initializer initializer;
@ -76,16 +75,10 @@ class InjectorImpl implements Injector, Lookups {
Lookups lookups = new DeferredLookups(this); Lookups lookups = new DeferredLookups(this);
InjectorImpl(@Nullable InjectorImpl parent, State state, Initializer initializer) { InjectorImpl(State state, Initializer initializer) {
this.parent = parent;
this.state = state; this.state = state;
this.initializer = initializer; this.initializer = initializer;
localContext = new ThreadLocal<>();
if (parent != null) {
localContext = parent.localContext;
} else {
localContext = new ThreadLocal<>();
}
} }
/** /**
@ -106,21 +99,6 @@ class InjectorImpl implements Injector, Lookups {
return bindingsMultimap.getAll(type); return bindingsMultimap.getAll(type);
} }
/**
* Returns the binding for {@code key}
*/
@Override
public <T> BindingImpl<T> getBinding(Key<T> key) {
Errors errors = new Errors(key);
try {
BindingImpl<T> result = getBindingOrThrow(key, errors);
errors.throwConfigurationExceptionIfErrorsExist();
return result;
} catch (ErrorsException e) {
throw new ConfigurationException(errors.merge(e.getErrors()).getMessages());
}
}
/** /**
* Gets a binding implementation. First, it check to see if the parent has a binding. If the * Gets a binding implementation. First, it check to see if the parent has a binding. If the
* parent has a binding and the binding is scoped, it will use that binding. Otherwise, this * parent has a binding and the binding is scoped, it will use that binding. Otherwise, this
@ -139,29 +117,6 @@ class InjectorImpl implements Injector, Lookups {
return getJustInTimeBinding(key, errors); return getJustInTimeBinding(key, errors);
} }
@Override
public <T> Binding<T> getBinding(Class<T> type) {
return getBinding(Key.get(type));
}
@Override
public Injector getParent() {
return parent;
}
@Override
public Injector createChildInjector(Iterable<? extends Module> modules) {
return new InjectorBuilder()
.parentInjector(this)
.addModules(modules)
.build();
}
@Override
public Injector createChildInjector(Module... modules) {
return createChildInjector(Arrays.asList(modules));
}
/** /**
* Returns a just-in-time binding for {@code key}, creating it if necessary. * Returns a just-in-time binding for {@code key}, creating it if necessary.
* *
@ -171,13 +126,11 @@ class InjectorImpl implements Injector, Lookups {
throws ErrorsException { throws ErrorsException {
synchronized (state.lock()) { synchronized (state.lock()) {
// first try to find a JIT binding that we've already created // first try to find a JIT binding that we've already created
for (InjectorImpl injector = this; injector != null; injector = injector.parent) { @SuppressWarnings("unchecked") // we only store bindings that match their key
@SuppressWarnings("unchecked") // we only store bindings that match their key BindingImpl<T> binding = (BindingImpl<T>) jitBindings.get(key);
BindingImpl<T> binding = (BindingImpl<T>) injector.jitBindings.get(key);
if (binding != null) { if (binding != null) {
return binding; return binding;
}
} }
return createJustInTimeBindingRecursive(key, errors); return createJustInTimeBindingRecursive(key, errors);
@ -600,14 +553,6 @@ class InjectorImpl implements Injector, Lookups {
*/ */
private <T> BindingImpl<T> createJustInTimeBindingRecursive(Key<T> key, Errors errors) private <T> BindingImpl<T> createJustInTimeBindingRecursive(Key<T> key, Errors errors)
throws ErrorsException { throws ErrorsException {
// ask the parent to create the JIT binding
if (parent != null && !parent.readOnly /* ES: don't check on parent if its read only, its already created all the bindings it can*/) {
try {
return parent.createJustInTimeBindingRecursive(key, new Errors());
} catch (ErrorsException ignored) {
}
}
if (state.isBlacklisted(key)) { if (state.isBlacklisted(key)) {
throw errors.childBindingAlreadySet(key).toException(); throw errors.childBindingAlreadySet(key).toException();
} }
@ -686,12 +631,6 @@ class InjectorImpl implements Injector, Lookups {
return getBindingOrThrow(key, errors).getInternalFactory(); return getBindingOrThrow(key, errors).getInternalFactory();
} }
// not test-covered
@Override
public Map<Key<?>, Binding<?>> getBindings() {
return state.getExplicitBindingsThisLevel();
}
private static class BindingsMultimap { private static class BindingsMultimap {
final Map<TypeLiteral<?>, List<Binding<?>>> multimap = new HashMap<>(); final Map<TypeLiteral<?>, List<Binding<?>>> multimap = new HashMap<>();

View File

@ -50,18 +50,12 @@ class InjectorShell {
private final List<Element> elements; private final List<Element> elements;
private final InjectorImpl injector; private final InjectorImpl injector;
private final PrivateElements privateElements;
private InjectorShell(Builder builder, List<Element> elements, InjectorImpl injector) { private InjectorShell(List<Element> elements, InjectorImpl injector) {
this.privateElements = builder.privateElements;
this.elements = elements; this.elements = elements;
this.injector = injector; this.injector = injector;
} }
PrivateElements getPrivateElements() {
return privateElements;
}
InjectorImpl getInjector() { InjectorImpl getInjector() {
return injector; return injector;
} }
@ -134,7 +128,7 @@ class InjectorShell {
throw new IllegalStateException("no state. Did you remember to lock() ?"); throw new IllegalStateException("no state. Did you remember to lock() ?");
} }
InjectorImpl injector = new InjectorImpl(parent, state, initializer); InjectorImpl injector = new InjectorImpl(state, initializer);
if (privateElements != null) { if (privateElements != null) {
privateElements.initInjector(injector); privateElements.initInjector(injector);
} }
@ -167,7 +161,7 @@ class InjectorShell {
stopwatch.resetAndLog("Binding creation"); stopwatch.resetAndLog("Binding creation");
List<InjectorShell> injectorShells = new ArrayList<>(); List<InjectorShell> injectorShells = new ArrayList<>();
injectorShells.add(new InjectorShell(this, elements, injector)); injectorShells.add(new InjectorShell(elements, injector));
// recursively build child shells // recursively build child shells
PrivateElementProcessor processor = new PrivateElementProcessor(errors, stage); PrivateElementProcessor processor = new PrivateElementProcessor(errors, stage);

View File

@ -1,239 +0,0 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you 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 org.elasticsearch.common.inject;
import org.elasticsearch.common.inject.matcher.Matcher;
import org.elasticsearch.common.inject.name.Names;
import org.elasticsearch.common.inject.spi.Message;
import java.lang.reflect.Type;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
/**
*
*/
public class Injectors {
public static Throwable getFirstErrorFailure(CreationException e) {
if (e.getErrorMessages().isEmpty()) {
return e;
}
// return the first message that has root cause, probably an actual error
for (Message message : e.getErrorMessages()) {
if (message.getCause() != null) {
return message.getCause();
}
}
return e;
}
/**
* Returns an instance of the given type with the {@link org.elasticsearch.common.inject.name.Named}
* annotation value.
* <p>
* This method allows you to switch this code
* <code>injector.getInstance(Key.get(type, Names.named(name)));</code>
* <p>
* to the more concise
* <code>Injectors.getInstance(injector, type, name);</code>
*/
public static <T> T getInstance(Injector injector, java.lang.Class<T> type, String name) {
return injector.getInstance(Key.get(type, Names.named(name)));
}
/**
* Returns a collection of all instances of the given base type
*
* @param baseClass the base type of objects required
* @param <T> the base type
* @return a set of objects returned from this injector
*/
public static <T> Set<T> getInstancesOf(Injector injector, Class<T> baseClass) {
Set<T> answer = new HashSet<>();
Set<Entry<Key<?>, Binding<?>>> entries = injector.getBindings().entrySet();
for (Entry<Key<?>, Binding<?>> entry : entries) {
Key<?> key = entry.getKey();
Class<?> keyType = getKeyType(key);
if (keyType != null && baseClass.isAssignableFrom(keyType)) {
Binding<?> binding = entry.getValue();
Object value = binding.getProvider().get();
if (value != null) {
T castValue = baseClass.cast(value);
answer.add(castValue);
}
}
}
return answer;
}
/**
* Returns a collection of all instances matching the given matcher
*
* @param matcher matches the types to return instances
* @return a set of objects returned from this injector
*/
public static <T> Set<T> getInstancesOf(Injector injector, Matcher<Class> matcher) {
Set<T> answer = new HashSet<>();
Set<Entry<Key<?>, Binding<?>>> entries = injector.getBindings().entrySet();
for (Entry<Key<?>, Binding<?>> entry : entries) {
Key<?> key = entry.getKey();
Class<?> keyType = getKeyType(key);
if (keyType != null && matcher.matches(keyType)) {
Binding<?> binding = entry.getValue();
Object value = binding.getProvider().get();
answer.add((T) value);
}
}
return answer;
}
/**
* Returns a collection of all of the providers matching the given matcher
*
* @param matcher matches the types to return instances
* @return a set of objects returned from this injector
*/
public static <T> Set<Provider<T>> getProvidersOf(Injector injector, Matcher<Class> matcher) {
Set<Provider<T>> answer = new HashSet<>();
Set<Entry<Key<?>, Binding<?>>> entries = injector.getBindings().entrySet();
for (Entry<Key<?>, Binding<?>> entry : entries) {
Key<?> key = entry.getKey();
Class<?> keyType = getKeyType(key);
if (keyType != null && matcher.matches(keyType)) {
Binding<?> binding = entry.getValue();
answer.add((Provider<T>) binding.getProvider());
}
}
return answer;
}
/**
* Returns a collection of all providers of the given base type
*
* @param baseClass the base type of objects required
* @param <T> the base type
* @return a set of objects returned from this injector
*/
public static <T> Set<Provider<T>> getProvidersOf(Injector injector, Class<T> baseClass) {
Set<Provider<T>> answer = new HashSet<>();
Set<Entry<Key<?>, Binding<?>>> entries = injector.getBindings().entrySet();
for (Entry<Key<?>, Binding<?>> entry : entries) {
Key<?> key = entry.getKey();
Class<?> keyType = getKeyType(key);
if (keyType != null && baseClass.isAssignableFrom(keyType)) {
Binding<?> binding = entry.getValue();
answer.add((Provider<T>) binding.getProvider());
}
}
return answer;
}
/**
* Returns true if a binding exists for the given matcher
*/
public static boolean hasBinding(Injector injector, Matcher<Class> matcher) {
return !getBindingsOf(injector, matcher).isEmpty();
}
/**
* Returns true if a binding exists for the given base class
*/
public static boolean hasBinding(Injector injector, Class<?> baseClass) {
return !getBindingsOf(injector, baseClass).isEmpty();
}
/**
* Returns true if a binding exists for the given key
*/
public static boolean hasBinding(Injector injector, Key<?> key) {
Binding<?> binding = getBinding(injector, key);
return binding != null;
}
/**
* Returns the binding for the given key or null if there is no such binding
*/
public static Binding<?> getBinding(Injector injector, Key<?> key) {
Map<Key<?>, Binding<?>> bindings = injector.getBindings();
Binding<?> binding = bindings.get(key);
return binding;
}
/**
* Returns a collection of all of the bindings matching the given matcher
*
* @param matcher matches the types to return instances
* @return a set of objects returned from this injector
*/
public static Set<Binding<?>> getBindingsOf(Injector injector, Matcher<Class> matcher) {
Set<Binding<?>> answer = new HashSet<>();
Set<Entry<Key<?>, Binding<?>>> entries = injector.getBindings().entrySet();
for (Entry<Key<?>, Binding<?>> entry : entries) {
Key<?> key = entry.getKey();
Class<?> keyType = getKeyType(key);
if (keyType != null && matcher.matches(keyType)) {
answer.add(entry.getValue());
}
}
return answer;
}
/**
* Returns a collection of all bindings of the given base type
*
* @param baseClass the base type of objects required
* @return a set of objects returned from this injector
*/
public static Set<Binding<?>> getBindingsOf(Injector injector, Class<?> baseClass) {
Set<Binding<?>> answer = new HashSet<>();
Set<Entry<Key<?>, Binding<?>>> entries = injector.getBindings().entrySet();
for (Entry<Key<?>, Binding<?>> entry : entries) {
Key<?> key = entry.getKey();
Class<?> keyType = getKeyType(key);
if (keyType != null && baseClass.isAssignableFrom(keyType)) {
answer.add(entry.getValue());
}
}
return answer;
}
/**
* Returns the key type of the given key
*/
public static <T> Class<?> getKeyType(Key<?> key) {
Class<?> keyType = null;
TypeLiteral<?> typeLiteral = key.getTypeLiteral();
Type type = typeLiteral.getType();
if (type instanceof Class) {
keyType = (Class<?>) type;
}
return keyType;
}
public static void cleanCaches(Injector injector) {
((InjectorImpl) injector).clearCache();
if (injector.getParent() != null) {
cleanCaches(injector.getParent());
}
}
}

View File

@ -23,9 +23,6 @@ import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
/**
*
*/
public class ModulesBuilder implements Iterable<Module> { public class ModulesBuilder implements Iterable<Module> {
private final List<Module> modules = new ArrayList<>(); private final List<Module> modules = new ArrayList<>();
@ -44,19 +41,10 @@ public class ModulesBuilder implements Iterable<Module> {
public Injector createInjector() { public Injector createInjector() {
Injector injector = Guice.createInjector(modules); Injector injector = Guice.createInjector(modules);
Injectors.cleanCaches(injector); ((InjectorImpl) injector).clearCache();
// in ES, we always create all instances as if they are eager singletons // in ES, we always create all instances as if they are eager singletons
// this allows for considerable memory savings (no need to store construction info) as well as cycles // this allows for considerable memory savings (no need to store construction info) as well as cycles
((InjectorImpl) injector).readOnlyAllSingletons(); ((InjectorImpl) injector).readOnlyAllSingletons();
return injector; return injector;
} }
public Injector createChildInjector(Injector injector) {
Injector childInjector = injector.createChildInjector(modules);
Injectors.cleanCaches(childInjector);
// in ES, we always create all instances as if they are eager singletons
// this allows for considerable memory savings (no need to store construction info) as well as cycles
((InjectorImpl) childInjector).readOnlyAllSingletons();
return childInjector;
}
} }

View File

@ -61,10 +61,6 @@ import java.lang.annotation.Annotation;
* } * }
* </pre> * </pre>
* <p> * <p>
* Private modules are implemented using {@link Injector#createChildInjector(Module[]) parent
* injectors}. When it can satisfy their dependencies, just-in-time bindings will be created in the
* root environment. Such bindings are shared among all environments in the tree.
* <p>
* The scope of a binding is constrained to its environment. A singleton bound in a private * The scope of a binding is constrained to its environment. A singleton bound in a private
* module will be unique to its environment. But a binding for the same type in a different private * module will be unique to its environment. But a binding for the same type in a different private
* module will yield a different instance. * module will yield a different instance.
@ -143,13 +139,6 @@ public abstract class PrivateModule implements Module {
return binder; return binder;
} }
/**
* @see Binder#bindScope(Class, Scope)
*/
protected final void bindScope(Class<? extends Annotation> scopeAnnotation, Scope scope) {
binder.bindScope(scopeAnnotation, scope);
}
/** /**
* @see Binder#bind(Key) * @see Binder#bind(Key)
*/ */
@ -171,13 +160,6 @@ public abstract class PrivateModule implements Module {
return binder.bind(clazz); return binder.bind(clazz);
} }
/**
* @see Binder#bindConstant()
*/
protected final AnnotatedConstantBindingBuilder bindConstant() {
return binder.bindConstant();
}
/** /**
* @see Binder#install(Module) * @see Binder#install(Module)
*/ */
@ -206,34 +188,6 @@ public abstract class PrivateModule implements Module {
binder.addError(message); binder.addError(message);
} }
/**
* @see Binder#requestInjection(Object)
*/
protected final void requestInjection(Object instance) {
binder.requestInjection(instance);
}
/**
* @see Binder#requestStaticInjection(Class[])
*/
protected final void requestStaticInjection(Class<?>... types) {
binder.requestStaticInjection(types);
}
/**
* Instructs Guice to require a binding to the given key.
*/
protected final void requireBinding(Key<?> key) {
binder.getProvider(key);
}
/**
* Instructs Guice to require a binding to the given type.
*/
protected final void requireBinding(Class<?> type) {
binder.getProvider(type);
}
/** /**
* @see Binder#getProvider(Key) * @see Binder#getProvider(Key)
*/ */
@ -248,21 +202,6 @@ public abstract class PrivateModule implements Module {
return binder.getProvider(type); return binder.getProvider(type);
} }
/**
* @see Binder#convertToTypes(org.elasticsearch.common.inject.matcher.Matcher, org.elasticsearch.common.inject.spi.TypeConverter)
*/
protected final void convertToTypes(Matcher<? super TypeLiteral<?>> typeMatcher,
TypeConverter converter) {
binder.convertToTypes(typeMatcher, converter);
}
/**
* @see Binder#currentStage()
*/
protected final Stage currentStage() {
return binder.currentStage();
}
/** /**
* @see Binder#getMembersInjector(Class) * @see Binder#getMembersInjector(Class)
*/ */
@ -276,12 +215,4 @@ public abstract class PrivateModule implements Module {
protected <T> MembersInjector<T> getMembersInjector(TypeLiteral<T> type) { protected <T> MembersInjector<T> getMembersInjector(TypeLiteral<T> type) {
return binder.getMembersInjector(type); return binder.getMembersInjector(type);
} }
/**
* @see Binder#bindListener(org.elasticsearch.common.inject.matcher.Matcher, org.elasticsearch.common.inject.spi.TypeListener)
*/
protected void bindListener(Matcher<? super TypeLiteral<?>> typeMatcher,
TypeListener listener) {
binder.bindListener(typeMatcher, listener);
}
} }

View File

@ -134,7 +134,7 @@ public class FactoryProvider<F> implements Provider<F>, HasDependencies {
/* /*
* This class implements the old @AssistedInject implementation that manually matches constructors * This class implements the old @AssistedInject implementation that manually matches constructors
* to factory methods. The new child injector implementation lives in FactoryProvider2. * to factory methods.
*/ */
private Injector injector; private Injector injector;
@ -142,23 +142,6 @@ public class FactoryProvider<F> implements Provider<F>, HasDependencies {
private final TypeLiteral<F> factoryType; private final TypeLiteral<F> factoryType;
private final Map<Method, AssistedConstructor<?>> factoryMethodToConstructor; private final Map<Method, AssistedConstructor<?>> factoryMethodToConstructor;
public static <F> Provider<F> newFactory(
Class<F> factoryType, Class<?> implementationType) {
return newFactory(TypeLiteral.get(factoryType), TypeLiteral.get(implementationType));
}
public static <F> Provider<F> newFactory(
TypeLiteral<F> factoryType, TypeLiteral<?> implementationType) {
Map<Method, AssistedConstructor<?>> factoryMethodToConstructor
= createMethodMapping(factoryType, implementationType);
if (!factoryMethodToConstructor.isEmpty()) {
return new FactoryProvider<>(factoryType, factoryMethodToConstructor);
} else {
return new FactoryProvider2<>(factoryType, Key.get(implementationType));
}
}
private FactoryProvider(TypeLiteral<F> factoryType, private FactoryProvider(TypeLiteral<F> factoryType,
Map<Method, AssistedConstructor<?>> factoryMethodToConstructor) { Map<Method, AssistedConstructor<?>> factoryMethodToConstructor) {
this.factoryType = factoryType; this.factoryType = factoryType;
@ -166,22 +149,6 @@ public class FactoryProvider<F> implements Provider<F>, HasDependencies {
checkDeclaredExceptionsMatch(); checkDeclaredExceptionsMatch();
} }
@Inject
void setInjectorAndCheckUnboundParametersAreInjectable(Injector injector) {
this.injector = injector;
for (AssistedConstructor<?> c : factoryMethodToConstructor.values()) {
for (Parameter p : c.getAllParameters()) {
if (!p.isProvidedByFactory() && !paramCanBeInjected(p, injector)) {
// this is lame - we're not using the proper mechanism to add an
// error to the injector. Throughout this class we throw exceptions
// to add errors, which isn't really the best way in Guice
throw newConfigurationException("Parameter of type '%s' is not injectable or annotated "
+ "with @Assisted for Constructor '%s'", p, c);
}
}
}
}
private void checkDeclaredExceptionsMatch() { private void checkDeclaredExceptionsMatch() {
for (Map.Entry<Method, AssistedConstructor<?>> entry : factoryMethodToConstructor.entrySet()) { for (Map.Entry<Method, AssistedConstructor<?>> entry : factoryMethodToConstructor.entrySet()) {
for (Class<?> constructorException : entry.getValue().getDeclaredExceptions()) { for (Class<?> constructorException : entry.getValue().getDeclaredExceptions()) {
@ -204,82 +171,6 @@ public class FactoryProvider<F> implements Provider<F>, HasDependencies {
return false; return false;
} }
private boolean paramCanBeInjected(Parameter parameter, Injector injector) {
return parameter.isBound(injector);
}
private static Map<Method, AssistedConstructor<?>> createMethodMapping(
TypeLiteral<?> factoryType, TypeLiteral<?> implementationType) {
List<AssistedConstructor<?>> constructors = new ArrayList<>();
for (Constructor<?> constructor : implementationType.getRawType().getConstructors()) {
if (constructor.getAnnotation(AssistedInject.class) != null) {
@SuppressWarnings("unchecked") // the constructor type and implementation type agree
AssistedConstructor assistedConstructor = new AssistedConstructor(
constructor, implementationType.getParameterTypes(constructor));
constructors.add(assistedConstructor);
}
}
if (constructors.isEmpty()) {
return emptyMap();
}
Method[] factoryMethods = factoryType.getRawType().getMethods();
if (constructors.size() != factoryMethods.length) {
throw newConfigurationException("Constructor mismatch: %s has %s @AssistedInject "
+ "constructors, factory %s has %s creation methods", implementationType,
constructors.size(), factoryType, factoryMethods.length);
}
Map<ParameterListKey, AssistedConstructor> paramsToConstructor = new HashMap<>();
for (AssistedConstructor c : constructors) {
if (paramsToConstructor.containsKey(c.getAssistedParameters())) {
throw new RuntimeException("Duplicate constructor, " + c);
}
paramsToConstructor.put(c.getAssistedParameters(), c);
}
Map<Method, AssistedConstructor<?>> result = new HashMap<>();
for (Method method : factoryMethods) {
if (!method.getReturnType().isAssignableFrom(implementationType.getRawType())) {
throw newConfigurationException("Return type of method %s is not assignable from %s",
method, implementationType);
}
List<Type> parameterTypes = new ArrayList<>();
for (TypeLiteral<?> parameterType : factoryType.getParameterTypes(method)) {
parameterTypes.add(parameterType.getType());
}
ParameterListKey methodParams = new ParameterListKey(parameterTypes);
if (!paramsToConstructor.containsKey(methodParams)) {
throw newConfigurationException("%s has no @AssistInject constructor that takes the "
+ "@Assisted parameters %s in that order. @AssistInject constructors are %s",
implementationType, methodParams, paramsToConstructor.values());
}
method.getParameterAnnotations();
for (Annotation[] parameterAnnotations : method.getParameterAnnotations()) {
for (Annotation parameterAnnotation : parameterAnnotations) {
if (parameterAnnotation.annotationType() == Assisted.class) {
throw newConfigurationException("Factory method %s has an @Assisted parameter, which "
+ "is incompatible with the deprecated @AssistedInject annotation. Please replace "
+ "@AssistedInject with @Inject on the %s constructor.",
method, implementationType);
}
}
}
AssistedConstructor matchingConstructor = paramsToConstructor.remove(methodParams);
result.put(method, matchingConstructor);
}
return result;
}
@Override @Override
public Set<Dependency<?>> getDependencies() { public Set<Dependency<?>> getDependencies() {
Set<Dependency<?>> dependencies = new HashSet<>(); Set<Dependency<?>> dependencies = new HashSet<>();

View File

@ -1,283 +0,0 @@
/*
* 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 org.elasticsearch.common.inject.assistedinject;
import org.elasticsearch.common.inject.AbstractModule;
import org.elasticsearch.common.inject.Binder;
import org.elasticsearch.common.inject.Binding;
import org.elasticsearch.common.inject.ConfigurationException;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.inject.Injector;
import org.elasticsearch.common.inject.Key;
import org.elasticsearch.common.inject.Module;
import org.elasticsearch.common.inject.Provider;
import org.elasticsearch.common.inject.ProvisionException;
import org.elasticsearch.common.inject.TypeLiteral;
import org.elasticsearch.common.inject.internal.Errors;
import org.elasticsearch.common.inject.internal.ErrorsException;
import org.elasticsearch.common.inject.spi.Message;
import org.elasticsearch.common.inject.util.Providers;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static java.util.Collections.unmodifiableMap;
import static org.elasticsearch.common.inject.internal.Annotations.getKey;
/**
* The newer implementation of factory provider. This implementation uses a child injector to
* create values.
*
* @author jessewilson@google.com (Jesse Wilson)
* @author dtm@google.com (Daniel Martin)
*/
public final class FactoryProvider2<F> implements InvocationHandler, Provider<F> {
/**
* if a factory method parameter isn't annotated, it gets this annotation.
*/
static final Assisted DEFAULT_ANNOTATION = new Assisted() {
@Override
public String value() {
return "";
}
@Override
public Class<? extends Annotation> annotationType() {
return Assisted.class;
}
@Override
public boolean equals(Object o) {
return o instanceof Assisted
&& ((Assisted) o).value().equals("");
}
@Override
public int hashCode() {
return 127 * "value".hashCode() ^ "".hashCode();
}
@Override
public String toString() {
return "@" + Assisted.class.getName() + "(value=)";
}
};
/**
* the produced type, or null if all methods return concrete types
*/
private final Key<?> producedType;
private final Map<Method, Key<?>> returnTypesByMethod;
private final Map<Method, List<Key<?>>> paramTypes;
/**
* the hosting injector, or null if we haven't been initialized yet
*/
private Injector injector;
/**
* the factory interface, implemented and provided
*/
private final F factory;
/**
* @param factoryType a Java interface that defines one or more create methods.
* @param producedType a concrete type that is assignable to the return types of all factory
* methods.
*/
FactoryProvider2(TypeLiteral<F> factoryType, Key<?> producedType) {
this.producedType = producedType;
Errors errors = new Errors();
@SuppressWarnings("unchecked") // we imprecisely treat the class literal of T as a Class<T>
Class<F> factoryRawType = (Class) factoryType.getRawType();
try {
Map<Method, Key<?>> returnTypesBuilder = new HashMap<>();
Map<Method, List<Key<?>>> paramTypesBuilder = new HashMap<>();
// TODO: also grab methods from superinterfaces
for (Method method : factoryRawType.getMethods()) {
Key<?> returnType = getKey(
factoryType.getReturnType(method), method, method.getAnnotations(), errors);
returnTypesBuilder.put(method, returnType);
List<TypeLiteral<?>> params = factoryType.getParameterTypes(method);
Annotation[][] paramAnnotations = method.getParameterAnnotations();
int p = 0;
List<Key<?>> keys = new ArrayList<>();
for (TypeLiteral<?> param : params) {
Key<?> paramKey = getKey(param, method, paramAnnotations[p++], errors);
keys.add(assistKey(method, paramKey, errors));
}
paramTypesBuilder.put(method, Collections.unmodifiableList(keys));
}
returnTypesByMethod = unmodifiableMap(returnTypesBuilder);
paramTypes = unmodifiableMap(paramTypesBuilder);
} catch (ErrorsException e) {
throw new ConfigurationException(e.getErrors().getMessages());
}
factory = factoryRawType.cast(Proxy.newProxyInstance(factoryRawType.getClassLoader(),
new Class[]{factoryRawType}, this));
}
@Override
public F get() {
return factory;
}
/**
* Returns a key similar to {@code key}, but with an {@literal @}Assisted binding annotation.
* This fails if another binding annotation is clobbered in the process. If the key already has
* the {@literal @}Assisted annotation, it is returned as-is to preserve any String value.
*/
private <T> Key<T> assistKey(Method method, Key<T> key, Errors errors) throws ErrorsException {
if (key.getAnnotationType() == null) {
return Key.get(key.getTypeLiteral(), DEFAULT_ANNOTATION);
} else if (key.getAnnotationType() == Assisted.class) {
return key;
} else {
errors.withSource(method).addMessage(
"Only @Assisted is allowed for factory parameters, but found @%s",
key.getAnnotationType());
throw errors.toException();
}
}
/**
* At injector-creation time, we initialize the invocation handler. At this time we make sure
* all factory methods will be able to build the target types.
*/
@Inject
public void initialize(Injector injector) {
if (this.injector != null) {
throw new ConfigurationException(Collections.singletonList(new Message(FactoryProvider2.class,
"Factories.create() factories may only be used in one Injector!")));
}
this.injector = injector;
for (Method method : returnTypesByMethod.keySet()) {
Object[] args = new Object[method.getParameterTypes().length];
Arrays.fill(args, "dummy object for validating Factories");
getBindingFromNewInjector(method, args); // throws if the binding isn't properly configured
}
}
/**
* Creates a child injector that binds the args, and returns the binding for the method's result.
*/
public Binding<?> getBindingFromNewInjector(final Method method, final Object[] args) {
if (injector == null) {
throw new IllegalStateException("Factories.create() factories cannot be used until they're initialized by Guice.");
}
final Key<?> returnType = returnTypesByMethod.get(method);
Module assistedModule = new AbstractModule() {
@Override
@SuppressWarnings("unchecked") // raw keys are necessary for the args array and return value
protected void configure() {
Binder binder = binder().withSource(method);
int p = 0;
for (Key<?> paramKey : paramTypes.get(method)) {
// Wrap in a Provider to cover null, and to prevent Guice from injecting the parameter
binder.bind((Key) paramKey).toProvider(Providers.of(args[p++]));
}
if (producedType != null && !returnType.equals(producedType)) {
binder.bind(returnType).to((Key) producedType);
} else {
binder.bind(returnType);
}
}
};
Injector forCreate = injector.createChildInjector(assistedModule);
return forCreate.getBinding(returnType);
}
/**
* When a factory method is invoked, we create a child injector that binds all parameters, then
* use that to get an instance of the return type.
*/
@Override
public Object invoke(Object proxy, final Method method, final Object[] args) throws Throwable {
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
Provider<?> provider = getBindingFromNewInjector(method, args).getProvider();
try {
return provider.get();
} catch (ProvisionException e) {
// if this is an exception declared by the factory method, throw it as-is
if (e.getErrorMessages().size() == 1) {
Message onlyError = e.getErrorMessages().iterator().next();
Throwable cause = onlyError.getCause();
if (cause != null && canRethrow(method, cause)) {
throw cause;
}
}
throw e;
}
}
@Override
public String toString() {
return factory.getClass().getInterfaces()[0].getName()
+ " for " + producedType.getTypeLiteral();
}
@Override
public boolean equals(Object o) {
return o == this || o == factory;
}
@Override
public int hashCode() {
// This way both this and its factory hash to the same spot, making hashCode consistent.
return factory.hashCode();
}
/**
* Returns true if {@code thrown} can be thrown by {@code invoked} without wrapping.
*/
static boolean canRethrow(Method invoked, Throwable thrown) {
if (thrown instanceof Error || thrown instanceof RuntimeException) {
return true;
}
for (Class<?> declared : invoked.getExceptionTypes()) {
if (declared.isInstance(thrown)) {
return true;
}
}
return false;
}
}

View File

@ -87,34 +87,6 @@ class Parameter {
: injector.getInstance(getPrimaryBindingKey()); : injector.getInstance(getPrimaryBindingKey());
} }
public boolean isBound(Injector injector) {
return isBound(injector, getPrimaryBindingKey())
|| isBound(injector, fixAnnotations(getPrimaryBindingKey()));
}
private boolean isBound(Injector injector, Key<?> key) {
// This method is particularly lame - we really need an API that can test
// for any binding, implicit or explicit
try {
return injector.getBinding(key) != null;
} catch (ConfigurationException e) {
return false;
}
}
/**
* Replace annotation instances with annotation types, this is only
* appropriate for testing if a key is bound and not for injecting.
* <p>
* See Guice bug 125,
* http://code.google.com/p/google-guice/issues/detail?id=125
*/
public Key<?> fixAnnotations(Key<?> key) {
return key.getAnnotation() == null
? key
: Key.get(key.getTypeLiteral(), key.getAnnotation().annotationType());
}
Key<?> getPrimaryBindingKey() { Key<?> getPrimaryBindingKey() {
return isProvider return isProvider
? getBindingForType(getProvidedType(type)) ? getBindingForType(getProvidedType(type))

View File

@ -36,8 +36,7 @@ public interface ConvertedConstantBinding<T> extends Binding<T>, HasDependencies
T getValue(); T getValue();
/** /**
* Returns the key for the source binding. That binding can e retrieved from an injector using * Returns the key for the source binding.
* {@link org.elasticsearch.common.inject.Injector#getBinding(Key) Injector.getBinding(key)}.
*/ */
Key<String> getSourceKey(); Key<String> getSourceKey();

View File

@ -25,9 +25,6 @@ import org.elasticsearch.common.inject.Binder;
* Elements#getElements(org.elasticsearch.common.inject.Module[]) Elements.getElements()} to read the elements * Elements#getElements(org.elasticsearch.common.inject.Module[]) Elements.getElements()} to read the elements
* from a module, and {@link Elements#getModule(Iterable) Elements.getModule()} to rewrite them. * from a module, and {@link Elements#getModule(Iterable) Elements.getModule()} to rewrite them.
* This can be used for static analysis and generation of Guice modules. * This can be used for static analysis and generation of Guice modules.
* <p>
* The elements of an injector can be inspected and exercised. Use {@link
* org.elasticsearch.common.inject.Injector#getBindings Injector.getBindings()} to reflect on Guice injectors.
* *
* @author jessewilson@google.com (Jesse Wilson) * @author jessewilson@google.com (Jesse Wilson)
* @author crazybob@google.com (Bob Lee) * @author crazybob@google.com (Bob Lee)

View File

@ -80,13 +80,6 @@ public final class Elements {
return getElements(Stage.DEVELOPMENT, Arrays.asList(modules)); return getElements(Stage.DEVELOPMENT, Arrays.asList(modules));
} }
/**
* Records the elements executed by {@code modules}.
*/
public static List<Element> getElements(Stage stage, Module... modules) {
return getElements(stage, Arrays.asList(modules));
}
/** /**
* Records the elements executed by {@code modules}. * Records the elements executed by {@code modules}.
*/ */
@ -119,11 +112,6 @@ public final class Elements {
}; };
} }
@SuppressWarnings("unchecked")
static <T> BindingTargetVisitor<T, T> getInstanceVisitor() {
return (BindingTargetVisitor<T, T>) GET_INSTANCE_VISITOR;
}
private static class RecordingBinder implements Binder, PrivateBinder { private static class RecordingBinder implements Binder, PrivateBinder {
private final Stage stage; private final Stage stage;
private final Set<Module> modules; private final Set<Module> modules;

View File

@ -28,8 +28,7 @@ import org.elasticsearch.common.inject.Key;
public interface LinkedKeyBinding<T> extends Binding<T> { public interface LinkedKeyBinding<T> extends Binding<T> {
/** /**
* Returns the linked key used to resolve injections. That binding can be retrieved from an * Returns the linked key used to resolve injections.
* injector using {@link org.elasticsearch.common.inject.Injector#getBinding(Key) Injector.getBinding(key)}.
*/ */
Key<? extends T> getLinkedKey(); Key<? extends T> getLinkedKey();

View File

@ -30,9 +30,7 @@ import org.elasticsearch.common.inject.Provider;
public interface ProviderBinding<T extends Provider<?>> extends Binding<T> { public interface ProviderBinding<T extends Provider<?>> extends Binding<T> {
/** /**
* Returns the key whose binding is used to {@link Provider#get provide instances}. That binding * Returns the key whose binding is used to {@link Provider#get provide instances}.
* can be retrieved from an injector using {@link org.elasticsearch.common.inject.Injector#getBinding(Key)
* Injector.getBinding(providedKey)}
*/ */
Key<?> getProvidedKey(); Key<?> getProvidedKey();
} }

View File

@ -30,9 +30,7 @@ import org.elasticsearch.common.inject.Provider;
public interface ProviderKeyBinding<T> extends Binding<T> { public interface ProviderKeyBinding<T> extends Binding<T> {
/** /**
* Returns the key used to resolve the provider's binding. That binding can be retrieved from an * Returns the key used to resolve the provider's binding.
* injector using {@link org.elasticsearch.common.inject.Injector#getBinding(Key)
* Injector.getBinding(providerKey)}
*/ */
Key<? extends Provider<? extends T>> getProviderKey(); Key<? extends Provider<? extends T>> getProviderKey();