org.elasticsearch.common.inject.internal.Errors Maven / Gradle / Ivy
Show all versions of elasticsearch Show documentation
/*
* Copyright (C) 2006 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.internal;
import org.apache.lucene.util.CollectionUtil;
import org.elasticsearch.common.inject.ConfigurationException;
import org.elasticsearch.common.inject.CreationException;
import org.elasticsearch.common.inject.Key;
import org.elasticsearch.common.inject.MembersInjector;
import org.elasticsearch.common.inject.Provider;
import org.elasticsearch.common.inject.ProvisionException;
import org.elasticsearch.common.inject.Scope;
import org.elasticsearch.common.inject.TypeLiteral;
import org.elasticsearch.common.inject.spi.Dependency;
import org.elasticsearch.common.inject.spi.InjectionListener;
import org.elasticsearch.common.inject.spi.InjectionPoint;
import org.elasticsearch.common.inject.spi.Message;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Formatter;
import java.util.List;
import java.util.Locale;
import static java.util.Collections.emptySet;
import static java.util.Collections.unmodifiableList;
/**
* A collection of error messages. If this type is passed as a method parameter, the method is
* considered to have executed successfully only if new errors were not added to this collection.
*
* Errors can be chained to provide additional context. To add context, call {@link #withSource}
* to create a new Errors instance that contains additional context. All messages added to the
* returned instance will contain full context.
*
* To avoid messages with redundant context, {@link #withSource} should be added sparingly. A
* good rule of thumb is to assume a method's caller has already specified enough context to
* identify that method. When calling a method that's defined in a different context, call that
* method with an errors object that includes its context.
*
* @author [email protected] (Jesse Wilson)
*/
@SuppressWarnings("rawtypes")
public final class Errors {
/**
* The root errors object. Used to access the list of error messages.
*/
private final Errors root;
/**
* The parent errors object. Used to obtain the chain of source objects.
*/
private final Errors parent;
/**
* The leaf source for errors added here.
*/
private final Object source;
/**
* null unless (root == this) and error messages exist. Never an empty list.
*/
private List errors; // lazy, use getErrorsForAdd()
public Errors() {
this.root = this;
this.parent = null;
this.source = SourceProvider.UNKNOWN_SOURCE;
}
public Errors(Object source) {
this.root = this;
this.parent = null;
this.source = source;
}
private Errors(Errors parent, Object source) {
this.root = parent.root;
this.parent = parent;
this.source = source;
}
/**
* Returns an instance that uses {@code source} as a reference point for newly added errors.
*/
public Errors withSource(Object source) {
return source == SourceProvider.UNKNOWN_SOURCE ? this : new Errors(this, source);
}
/**
* We use a fairly generic error message here. The motivation is to share the
* same message for both bind time errors:
* Guice.createInjector(new AbstractModule() {
* public void configure() {
* bind(Runnable.class);
* }
* }
* ...and at provide-time errors:
* Guice.createInjector().getInstance(Runnable.class);
* Otherwise we need to know who's calling when resolving a just-in-time
* binding, which makes things unnecessarily complex.
*/
public Errors missingImplementation(Key key) {
return addMessage("No implementation for %s was bound.", key);
}
public Errors converterReturnedNull(String stringValue, Object source, TypeLiteral type, MatcherAndConverter matchingConverter) {
return addMessage(
"Received null converting '%s' (bound at %s) to %s%n" + " using %s.",
stringValue,
convert(source),
type,
matchingConverter
);
}
public Errors conversionTypeError(
String stringValue,
Object source,
TypeLiteral type,
MatcherAndConverter matchingConverter,
Object converted
) {
return addMessage(
"Type mismatch converting '%s' (bound at %s) to %s%n" + " using %s.%n" + " Converter returned %s.",
stringValue,
convert(source),
type,
matchingConverter,
converted
);
}
public Errors conversionError(
String stringValue,
Object source,
TypeLiteral type,
MatcherAndConverter matchingConverter,
RuntimeException cause
) {
return errorInUserCode(
cause,
"Error converting '%s' (bound at %s) to %s%n" + " using %s.%n" + " Reason: %s",
stringValue,
convert(source),
type,
matchingConverter,
cause
);
}
public Errors ambiguousTypeConversion(
String stringValue,
Object source,
TypeLiteral type,
MatcherAndConverter a,
MatcherAndConverter b
) {
return addMessage(
"Multiple converters can convert '%s' (bound at %s) to %s:%n"
+ " %s and%n"
+ " %s.%n"
+ " Please adjust your type converter configuration to avoid overlapping matches.",
stringValue,
convert(source),
type,
a,
b
);
}
public Errors bindingToProvider() {
return addMessage("Binding to Provider is not allowed.");
}
public Errors subtypeNotProvided(Class> providerType, Class type) {
return addMessage("%s doesn't provide instances of %s.", providerType, type);
}
public Errors notASubtype(Class implementationType, Class type) {
return addMessage("%s doesn't extend %s.", implementationType, type);
}
public Errors recursiveImplementationType() {
return addMessage("@ImplementedBy points to the same class it annotates.");
}
public Errors recursiveProviderType() {
return addMessage("@ProvidedBy points to the same class it annotates.");
}
public Errors missingRuntimeRetention(Object source) {
return addMessage("Please annotate with @Retention(RUNTIME).%n" + " Bound at %s.", convert(source));
}
public Errors missingScopeAnnotation() {
return addMessage("Please annotate with @ScopeAnnotation.");
}
public Errors optionalConstructor(Constructor constructor) {
return addMessage("%s is annotated @Inject(optional=true), " + "but constructors cannot be optional.", constructor);
}
public Errors cannotBindToGuiceType(String simpleName) {
return addMessage("Binding to core guice framework type is not allowed: %s.", simpleName);
}
public Errors scopeNotFound(Class scopeAnnotation) {
return addMessage("No scope is bound to %s.", scopeAnnotation);
}
public Errors scopeAnnotationOnAbstractType(Class scopeAnnotation, Class type, Object source) {
return addMessage(
"%s is annotated with %s, but scope annotations are not supported " + "for abstract types.%n Bound at %s.",
type,
scopeAnnotation,
convert(source)
);
}
public Errors misplacedBindingAnnotation(Member member, Annotation bindingAnnotation) {
return addMessage(
"%s is annotated with %s, but binding annotations should be applied " + "to its parameters instead.",
member,
bindingAnnotation
);
}
private static final String CONSTRUCTOR_RULES = "Classes must have either one (and only one) constructor "
+ "annotated with @Inject or a zero-argument constructor that is not private.";
public Errors missingConstructor(Class implementation) {
return addMessage("Could not find a suitable constructor in %s. " + CONSTRUCTOR_RULES, implementation);
}
public Errors tooManyConstructors(Class implementation) {
return addMessage("%s has more than one constructor annotated with @Inject. " + CONSTRUCTOR_RULES, implementation);
}
public Errors duplicateScopes(Scope existing, Class annotationType, Scope scope) {
return addMessage("Scope %s is already bound to %s. Cannot bind %s.", existing, annotationType, scope);
}
public Errors voidProviderMethod() {
return addMessage("Provider methods must return a value. Do not return void.");
}
public Errors missingConstantValues() {
return addMessage("Missing constant value. Please call to(...).");
}
public Errors cannotInjectInnerClass(Class type) {
return addMessage(
"Injecting into inner classes is not supported. " + "Please use a 'static' class (top-level or nested) instead of %s.",
type
);
}
public Errors duplicateBindingAnnotations(Member member, Class a, Class b) {
return addMessage("%s has more than one annotation annotated with @BindingAnnotation: " + "%s and %s", member, a, b);
}
public Errors duplicateScopeAnnotations(Class a, Class b) {
return addMessage("More than one scope annotation was found: %s and %s.", a, b);
}
public Errors recursiveBinding() {
return addMessage("Binding points to itself.");
}
public Errors bindingAlreadySet(Key key, Object source) {
return addMessage("A binding to %s was already configured at %s.", key, convert(source));
}
public Errors childBindingAlreadySet(Key key) {
return addMessage("A binding to %s already exists on a child injector.", key);
}
public Errors errorInjectingMethod(Throwable cause) {
return errorInUserCode(cause, "Error injecting method, %s", cause);
}
public Errors errorInjectingConstructor(Throwable cause) {
return errorInUserCode(cause, "Error injecting constructor, %s", cause);
}
public Errors errorInProvider(RuntimeException runtimeException) {
return errorInUserCode(runtimeException, "Error in custom provider, %s", runtimeException);
}
public Errors errorInUserInjector(MembersInjector listener, TypeLiteral type, RuntimeException cause) {
return errorInUserCode(cause, "Error injecting %s using %s.%n" + " Reason: %s", type, listener, cause);
}
public Errors errorNotifyingInjectionListener(InjectionListener listener, TypeLiteral type, RuntimeException cause) {
return errorInUserCode(cause, "Error notifying InjectionListener %s of %s.%n" + " Reason: %s", listener, type, cause);
}
public static Collection getMessagesFromThrowable(Throwable throwable) {
if (throwable instanceof ProvisionException) {
return ((ProvisionException) throwable).getErrorMessages();
} else if (throwable instanceof ConfigurationException) {
return ((ConfigurationException) throwable).getErrorMessages();
} else if (throwable instanceof CreationException) {
return ((CreationException) throwable).getErrorMessages();
} else {
return emptySet();
}
}
public Errors errorInUserCode(Throwable cause, String messageFormat, Object... arguments) {
Collection messages = getMessagesFromThrowable(cause);
if (messages.isEmpty() == false) {
return merge(messages);
} else {
return addMessage(cause, messageFormat, arguments);
}
}
public Errors cannotInjectRawProvider() {
return addMessage("Cannot inject a Provider that has no type parameter");
}
public Errors cannotInjectRawMembersInjector() {
return addMessage("Cannot inject a MembersInjector that has no type parameter");
}
public Errors cannotInjectTypeLiteralOf(Type unsupportedType) {
return addMessage("Cannot inject a TypeLiteral of %s", unsupportedType);
}
public Errors cannotInjectRawTypeLiteral() {
return addMessage("Cannot inject a TypeLiteral that has no type parameter");
}
public Errors cannotSatisfyCircularDependency(Class expectedType) {
return addMessage("Tried proxying %s to support a circular dependency, but it is not an interface.", expectedType);
}
public void throwCreationExceptionIfErrorsExist() {
if (hasErrors() == false) {
return;
}
throw new CreationException(getMessages());
}
public void throwConfigurationExceptionIfErrorsExist() {
if (hasErrors() == false) {
return;
}
throw new ConfigurationException(getMessages());
}
public void throwProvisionExceptionIfErrorsExist() {
if (hasErrors() == false) {
return;
}
throw new ProvisionException(getMessages());
}
private Message merge(Message message) {
List