Merge pull request #19346 from rjernst/deguice_guice

Remove child injectors from guice
This commit is contained in:
Ryan Ernst 2016-07-09 12:33:06 -07:00 committed by GitHub
commit 03bd152a01
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
* already-constructed instances. This can be used to interoperate with objects created by other
* 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 jessewilson@google.com (Jesse Wilson)
@ -87,41 +81,6 @@ public interface Injector {
*/
<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}.
* <p>
@ -166,45 +125,4 @@ public interface Injector {
* @throws ProvisionException if there was a runtime failure while providing an instance.
*/
<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;
}
/**
* 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) {
shellBuilder.addModules(modules);
return this;
@ -102,11 +93,6 @@ class InjectorBuilder {
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();
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 {
final State state;
final InjectorImpl parent;
boolean readOnly;
BindingsMultimap bindingsMultimap = new BindingsMultimap();
final Initializer initializer;
@ -76,16 +75,10 @@ class InjectorImpl implements Injector, Lookups {
Lookups lookups = new DeferredLookups(this);
InjectorImpl(@Nullable InjectorImpl parent, State state, Initializer initializer) {
this.parent = parent;
InjectorImpl(State state, Initializer initializer) {
this.state = state;
this.initializer = initializer;
if (parent != null) {
localContext = parent.localContext;
} else {
localContext = new ThreadLocal<>();
}
localContext = new ThreadLocal<>();
}
/**
@ -106,21 +99,6 @@ class InjectorImpl implements Injector, Lookups {
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
* 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);
}
@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.
*
@ -171,13 +126,11 @@ class InjectorImpl implements Injector, Lookups {
throws ErrorsException {
synchronized (state.lock()) {
// 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
BindingImpl<T> binding = (BindingImpl<T>) injector.jitBindings.get(key);
@SuppressWarnings("unchecked") // we only store bindings that match their key
BindingImpl<T> binding = (BindingImpl<T>) jitBindings.get(key);
if (binding != null) {
return binding;
}
if (binding != null) {
return binding;
}
return createJustInTimeBindingRecursive(key, errors);
@ -600,14 +553,6 @@ class InjectorImpl implements Injector, Lookups {
*/
private <T> BindingImpl<T> createJustInTimeBindingRecursive(Key<T> key, Errors errors)
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)) {
throw errors.childBindingAlreadySet(key).toException();
}
@ -686,12 +631,6 @@ class InjectorImpl implements Injector, Lookups {
return getBindingOrThrow(key, errors).getInternalFactory();
}
// not test-covered
@Override
public Map<Key<?>, Binding<?>> getBindings() {
return state.getExplicitBindingsThisLevel();
}
private static class BindingsMultimap {
final Map<TypeLiteral<?>, List<Binding<?>>> multimap = new HashMap<>();

View File

@ -50,18 +50,12 @@ class InjectorShell {
private final List<Element> elements;
private final InjectorImpl injector;
private final PrivateElements privateElements;
private InjectorShell(Builder builder, List<Element> elements, InjectorImpl injector) {
this.privateElements = builder.privateElements;
private InjectorShell(List<Element> elements, InjectorImpl injector) {
this.elements = elements;
this.injector = injector;
}
PrivateElements getPrivateElements() {
return privateElements;
}
InjectorImpl getInjector() {
return injector;
}
@ -134,7 +128,7 @@ class InjectorShell {
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) {
privateElements.initInjector(injector);
}
@ -167,7 +161,7 @@ class InjectorShell {
stopwatch.resetAndLog("Binding creation");
List<InjectorShell> injectorShells = new ArrayList<>();
injectorShells.add(new InjectorShell(this, elements, injector));
injectorShells.add(new InjectorShell(elements, injector));
// recursively build child shells
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.List;
/**
*
*/
public class ModulesBuilder implements Iterable<Module> {
private final List<Module> modules = new ArrayList<>();
@ -44,19 +41,10 @@ public class ModulesBuilder implements Iterable<Module> {
public Injector createInjector() {
Injector injector = Guice.createInjector(modules);
Injectors.cleanCaches(injector);
((InjectorImpl) injector).clearCache();
// 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) injector).readOnlyAllSingletons();
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>
* <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
* module will be unique to its environment. But a binding for the same type in a different private
* module will yield a different instance.
@ -143,13 +139,6 @@ public abstract class PrivateModule implements Module {
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)
*/
@ -171,13 +160,6 @@ public abstract class PrivateModule implements Module {
return binder.bind(clazz);
}
/**
* @see Binder#bindConstant()
*/
protected final AnnotatedConstantBindingBuilder bindConstant() {
return binder.bindConstant();
}
/**
* @see Binder#install(Module)
*/
@ -206,34 +188,6 @@ public abstract class PrivateModule implements Module {
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)
*/
@ -248,21 +202,6 @@ public abstract class PrivateModule implements Module {
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)
*/
@ -276,12 +215,4 @@ public abstract class PrivateModule implements Module {
protected <T> MembersInjector<T> getMembersInjector(TypeLiteral<T> 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
* to factory methods. The new child injector implementation lives in FactoryProvider2.
* to factory methods.
*/
private Injector injector;
@ -142,23 +142,6 @@ public class FactoryProvider<F> implements Provider<F>, HasDependencies {
private final TypeLiteral<F> factoryType;
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,
Map<Method, AssistedConstructor<?>> factoryMethodToConstructor) {
this.factoryType = factoryType;
@ -166,22 +149,6 @@ public class FactoryProvider<F> implements Provider<F>, HasDependencies {
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() {
for (Map.Entry<Method, AssistedConstructor<?>> entry : factoryMethodToConstructor.entrySet()) {
for (Class<?> constructorException : entry.getValue().getDeclaredExceptions()) {
@ -204,82 +171,6 @@ public class FactoryProvider<F> implements Provider<F>, HasDependencies {
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
public Set<Dependency<?>> getDependencies() {
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());
}
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() {
return isProvider
? getBindingForType(getProvidedType(type))

View File

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

View File

@ -80,13 +80,6 @@ public final class Elements {
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}.
*/
@ -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 final Stage stage;
private final Set<Module> modules;

View File

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

View File

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