PageRenderTime 100ms CodeModel.GetById 37ms app.highlight 55ms RepoModel.GetById 0ms app.codeStats 1ms

/modules/elasticsearch/src/main/java/org/elasticsearch/common/inject/internal/Errors.java

https://github.com/iw/elasticsearch
Java | 649 lines | 468 code | 112 blank | 69 comment | 75 complexity | 7c5cb0c72c97bc3f8d08c10ed6817ea4 MD5 | raw file
  1/**
  2 * Copyright (C) 2006 Google Inc.
  3 *
  4 * Licensed under the Apache License, Version 2.0 (the "License");
  5 * you may not use this file except in compliance with the License.
  6 * You may obtain a copy of the License at
  7 *
  8 * http://www.apache.org/licenses/LICENSE-2.0
  9 *
 10 * Unless required by applicable law or agreed to in writing, software
 11 * distributed under the License is distributed on an "AS IS" BASIS,
 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13 * See the License for the specific language governing permissions and
 14 * limitations under the License.
 15 */
 16
 17package org.elasticsearch.common.inject.internal;
 18
 19import org.elasticsearch.common.collect.ImmutableList;
 20import org.elasticsearch.common.collect.ImmutableSet;
 21import org.elasticsearch.common.collect.Lists;
 22import org.elasticsearch.common.inject.ConfigurationException;
 23import org.elasticsearch.common.inject.CreationException;
 24import org.elasticsearch.common.inject.Key;
 25import org.elasticsearch.common.inject.MembersInjector;
 26import org.elasticsearch.common.inject.Provider;
 27import org.elasticsearch.common.inject.ProvisionException;
 28import org.elasticsearch.common.inject.Scope;
 29import org.elasticsearch.common.inject.TypeLiteral;
 30import org.elasticsearch.common.inject.spi.Dependency;
 31import org.elasticsearch.common.inject.spi.InjectionListener;
 32import org.elasticsearch.common.inject.spi.InjectionPoint;
 33import org.elasticsearch.common.inject.spi.Message;
 34import org.elasticsearch.common.inject.spi.TypeListenerBinding;
 35
 36import java.io.PrintWriter;
 37import java.io.Serializable;
 38import java.io.StringWriter;
 39import java.lang.annotation.Annotation;
 40import java.lang.reflect.Constructor;
 41import java.lang.reflect.Field;
 42import java.lang.reflect.Member;
 43import java.lang.reflect.Type;
 44import java.util.Collection;
 45import java.util.Collections;
 46import java.util.Comparator;
 47import java.util.Formatter;
 48import java.util.List;
 49
 50/**
 51 * A collection of error messages. If this type is passed as a method parameter, the method is
 52 * considered to have executed succesfully only if new errors were not added to this collection.
 53 *
 54 * <p>Errors can be chained to provide additional context. To add context, call {@link #withSource}
 55 * to create a new Errors instance that contains additional context. All messages added to the
 56 * returned instance will contain full context.
 57 *
 58 * <p>To avoid messages with redundant context, {@link #withSource} should be added sparingly. A
 59 * good rule of thumb is to assume a ethod's caller has already specified enough context to
 60 * identify that method. When calling a method that's defined in a different context, call that
 61 * method with an errors object that includes its context.
 62 *
 63 * @author jessewilson@google.com (Jesse Wilson)
 64 */
 65public final class Errors implements Serializable {
 66
 67    /**
 68     * The root errors object. Used to access the list of error messages.
 69     */
 70    private final Errors root;
 71
 72    /**
 73     * The parent errors object. Used to obtain the chain of source objects.
 74     */
 75    private final Errors parent;
 76
 77    /**
 78     * The leaf source for errors added here.
 79     */
 80    private final Object source;
 81
 82    /**
 83     * null unless (root == this) and error messages exist. Never an empty list.
 84     */
 85    private List<Message> errors; // lazy, use getErrorsForAdd()
 86
 87    public Errors() {
 88        this.root = this;
 89        this.parent = null;
 90        this.source = SourceProvider.UNKNOWN_SOURCE;
 91    }
 92
 93    public Errors(Object source) {
 94        this.root = this;
 95        this.parent = null;
 96        this.source = source;
 97    }
 98
 99    private Errors(Errors parent, Object source) {
100        this.root = parent.root;
101        this.parent = parent;
102        this.source = source;
103    }
104
105    /**
106     * Returns an instance that uses {@code source} as a reference point for newly added errors.
107     */
108    public Errors withSource(Object source) {
109        return source == SourceProvider.UNKNOWN_SOURCE
110                ? this
111                : new Errors(this, source);
112    }
113
114    /**
115     * We use a fairly generic error message here. The motivation is to share the
116     * same message for both bind time errors:
117     * <pre><code>Guice.createInjector(new AbstractModule() {
118     *   public void configure() {
119     *     bind(Runnable.class);
120     *   }
121     * }</code></pre>
122     * ...and at provide-time errors:
123     * <pre><code>Guice.createInjector().getInstance(Runnable.class);</code></pre>
124     * Otherwise we need to know who's calling when resolving a just-in-time
125     * binding, which makes things unnecessarily complex.
126     */
127    public Errors missingImplementation(Key key) {
128        return addMessage("No implementation for %s was bound.", key);
129    }
130
131    public Errors converterReturnedNull(String stringValue, Object source,
132                                        TypeLiteral<?> type, MatcherAndConverter matchingConverter) {
133        return addMessage("Received null converting '%s' (bound at %s) to %s%n"
134                + " using %s.",
135                stringValue, convert(source), type, matchingConverter);
136    }
137
138    public Errors conversionTypeError(String stringValue, Object source, TypeLiteral<?> type,
139                                      MatcherAndConverter matchingConverter, Object converted) {
140        return addMessage("Type mismatch converting '%s' (bound at %s) to %s%n"
141                + " using %s.%n"
142                + " Converter returned %s.",
143                stringValue, convert(source), type, matchingConverter, converted);
144    }
145
146    public Errors conversionError(String stringValue, Object source,
147                                  TypeLiteral<?> type, MatcherAndConverter matchingConverter, RuntimeException cause) {
148        return errorInUserCode(cause, "Error converting '%s' (bound at %s) to %s%n"
149                + " using %s.%n"
150                + " Reason: %s",
151                stringValue, convert(source), type, matchingConverter, cause);
152    }
153
154    public Errors ambiguousTypeConversion(String stringValue, Object source, TypeLiteral<?> type,
155                                          MatcherAndConverter a, MatcherAndConverter b) {
156        return addMessage("Multiple converters can convert '%s' (bound at %s) to %s:%n"
157                + " %s and%n"
158                + " %s.%n"
159                + " Please adjust your type converter configuration to avoid overlapping matches.",
160                stringValue, convert(source), type, a, b);
161    }
162
163    public Errors bindingToProvider() {
164        return addMessage("Binding to Provider is not allowed.");
165    }
166
167    public Errors subtypeNotProvided(Class<? extends Provider<?>> providerType,
168                                     Class<?> type) {
169        return addMessage("%s doesn't provide instances of %s.", providerType, type);
170    }
171
172    public Errors notASubtype(Class<?> implementationType, Class<?> type) {
173        return addMessage("%s doesn't extend %s.", implementationType, type);
174    }
175
176    public Errors recursiveImplementationType() {
177        return addMessage("@ImplementedBy points to the same class it annotates.");
178    }
179
180    public Errors recursiveProviderType() {
181        return addMessage("@ProvidedBy points to the same class it annotates.");
182    }
183
184    public Errors missingRuntimeRetention(Object source) {
185        return addMessage("Please annotate with @Retention(RUNTIME).%n"
186                + " Bound at %s.", convert(source));
187    }
188
189    public Errors missingScopeAnnotation() {
190        return addMessage("Please annotate with @ScopeAnnotation.");
191    }
192
193    public Errors optionalConstructor(Constructor constructor) {
194        return addMessage("%s is annotated @Inject(optional=true), "
195                + "but constructors cannot be optional.", constructor);
196    }
197
198    public Errors cannotBindToGuiceType(String simpleName) {
199        return addMessage("Binding to core guice framework type is not allowed: %s.", simpleName);
200    }
201
202    public Errors scopeNotFound(Class<? extends Annotation> scopeAnnotation) {
203        return addMessage("No scope is bound to %s.", scopeAnnotation);
204    }
205
206    public Errors scopeAnnotationOnAbstractType(
207            Class<? extends Annotation> scopeAnnotation, Class<?> type, Object source) {
208        return addMessage("%s is annotated with %s, but scope annotations are not supported "
209                + "for abstract types.%n Bound at %s.", type, scopeAnnotation, convert(source));
210    }
211
212    public Errors misplacedBindingAnnotation(Member member, Annotation bindingAnnotation) {
213        return addMessage("%s is annotated with %s, but binding annotations should be applied "
214                + "to its parameters instead.", member, bindingAnnotation);
215    }
216
217    private static final String CONSTRUCTOR_RULES =
218            "Classes must have either one (and only one) constructor "
219                    + "annotated with @Inject or a zero-argument constructor that is not private.";
220
221    public Errors missingConstructor(Class<?> implementation) {
222        return addMessage("Could not find a suitable constructor in %s. " + CONSTRUCTOR_RULES,
223                implementation);
224    }
225
226    public Errors tooManyConstructors(Class<?> implementation) {
227        return addMessage("%s has more than one constructor annotated with @Inject. "
228                + CONSTRUCTOR_RULES, implementation);
229    }
230
231    public Errors duplicateScopes(Scope existing,
232                                  Class<? extends Annotation> annotationType, Scope scope) {
233        return addMessage("Scope %s is already bound to %s. Cannot bind %s.", existing,
234                annotationType, scope);
235    }
236
237    public Errors voidProviderMethod() {
238        return addMessage("Provider methods must return a value. Do not return void.");
239    }
240
241    public Errors missingConstantValues() {
242        return addMessage("Missing constant value. Please call to(...).");
243    }
244
245    public Errors cannotInjectInnerClass(Class<?> type) {
246        return addMessage("Injecting into inner classes is not supported.  "
247                + "Please use a 'static' class (top-level or nested) instead of %s.", type);
248    }
249
250    public Errors duplicateBindingAnnotations(Member member,
251                                              Class<? extends Annotation> a, Class<? extends Annotation> b) {
252        return addMessage("%s has more than one annotation annotated with @BindingAnnotation: "
253                + "%s and %s", member, a, b);
254    }
255
256    public Errors duplicateScopeAnnotations(
257            Class<? extends Annotation> a, Class<? extends Annotation> b) {
258        return addMessage("More than one scope annotation was found: %s and %s.", a, b);
259    }
260
261    public Errors recursiveBinding() {
262        return addMessage("Binding points to itself.");
263    }
264
265    public Errors bindingAlreadySet(Key<?> key, Object source) {
266        return addMessage("A binding to %s was already configured at %s.", key, convert(source));
267    }
268
269    public Errors childBindingAlreadySet(Key<?> key) {
270        return addMessage("A binding to %s already exists on a child injector.", key);
271    }
272
273    public Errors errorInjectingMethod(Throwable cause) {
274        return errorInUserCode(cause, "Error injecting method, %s", cause);
275    }
276
277    public Errors errorNotifyingTypeListener(TypeListenerBinding listener,
278                                             TypeLiteral<?> type, Throwable cause) {
279        return errorInUserCode(cause,
280                "Error notifying TypeListener %s (bound at %s) of %s.%n"
281                        + " Reason: %s",
282                listener.getListener(), convert(listener.getSource()), type, cause);
283    }
284
285    public Errors errorInjectingConstructor(Throwable cause) {
286        return errorInUserCode(cause, "Error injecting constructor, %s", cause);
287    }
288
289    public Errors errorInProvider(RuntimeException runtimeException) {
290        return errorInUserCode(runtimeException, "Error in custom provider, %s", runtimeException);
291    }
292
293    public Errors errorInUserInjector(
294            MembersInjector<?> listener, TypeLiteral<?> type, RuntimeException cause) {
295        return errorInUserCode(cause, "Error injecting %s using %s.%n"
296                + " Reason: %s", type, listener, cause);
297    }
298
299    public Errors errorNotifyingInjectionListener(
300            InjectionListener<?> listener, TypeLiteral<?> type, RuntimeException cause) {
301        return errorInUserCode(cause, "Error notifying InjectionListener %s of %s.%n"
302                + " Reason: %s", listener, type, cause);
303    }
304
305    public void exposedButNotBound(Key<?> key) {
306        addMessage("Could not expose() %s, it must be explicitly bound.", key);
307    }
308
309    public static Collection<Message> getMessagesFromThrowable(Throwable throwable) {
310        if (throwable instanceof ProvisionException) {
311            return ((ProvisionException) throwable).getErrorMessages();
312        } else if (throwable instanceof ConfigurationException) {
313            return ((ConfigurationException) throwable).getErrorMessages();
314        } else if (throwable instanceof CreationException) {
315            return ((CreationException) throwable).getErrorMessages();
316        } else {
317            return ImmutableSet.of();
318        }
319    }
320
321    public Errors errorInUserCode(Throwable cause, String messageFormat, Object... arguments) {
322        Collection<Message> messages = getMessagesFromThrowable(cause);
323
324        if (!messages.isEmpty()) {
325            return merge(messages);
326        } else {
327            return addMessage(cause, messageFormat, arguments);
328        }
329    }
330
331    public Errors cannotInjectRawProvider() {
332        return addMessage("Cannot inject a Provider that has no type parameter");
333    }
334
335    public Errors cannotInjectRawMembersInjector() {
336        return addMessage("Cannot inject a MembersInjector that has no type parameter");
337    }
338
339    public Errors cannotInjectTypeLiteralOf(Type unsupportedType) {
340        return addMessage("Cannot inject a TypeLiteral of %s", unsupportedType);
341    }
342
343    public Errors cannotInjectRawTypeLiteral() {
344        return addMessage("Cannot inject a TypeLiteral that has no type parameter");
345    }
346
347    public Errors cannotSatisfyCircularDependency(Class<?> expectedType) {
348        return addMessage(
349                "Tried proxying %s to support a circular dependency, but it is not an interface.",
350                expectedType);
351    }
352
353    public void throwCreationExceptionIfErrorsExist() {
354        if (!hasErrors()) {
355            return;
356        }
357
358        throw new CreationException(getMessages());
359    }
360
361    public void throwConfigurationExceptionIfErrorsExist() {
362        if (!hasErrors()) {
363            return;
364        }
365
366        throw new ConfigurationException(getMessages());
367    }
368
369    public void throwProvisionExceptionIfErrorsExist() {
370        if (!hasErrors()) {
371            return;
372        }
373
374        throw new ProvisionException(getMessages());
375    }
376
377    private Message merge(Message message) {
378        List<Object> sources = Lists.newArrayList();
379        sources.addAll(getSources());
380        sources.addAll(message.getSources());
381        return new Message(sources, message.getMessage(), message.getCause());
382    }
383
384    public Errors merge(Collection<Message> messages) {
385        for (Message message : messages) {
386            addMessage(merge(message));
387        }
388        return this;
389    }
390
391    public Errors merge(Errors moreErrors) {
392        if (moreErrors.root == root || moreErrors.root.errors == null) {
393            return this;
394        }
395
396        merge(moreErrors.root.errors);
397        return this;
398    }
399
400    public List<Object> getSources() {
401        List<Object> sources = Lists.newArrayList();
402        for (Errors e = this; e != null; e = e.parent) {
403            if (e.source != SourceProvider.UNKNOWN_SOURCE) {
404                sources.add(0, e.source);
405            }
406        }
407        return sources;
408    }
409
410    public void throwIfNewErrors(int expectedSize) throws ErrorsException {
411        if (size() == expectedSize) {
412            return;
413        }
414
415        throw toException();
416    }
417
418    public ErrorsException toException() {
419        return new ErrorsException(this);
420    }
421
422    public boolean hasErrors() {
423        return root.errors != null;
424    }
425
426    public Errors addMessage(String messageFormat, Object... arguments) {
427        return addMessage(null, messageFormat, arguments);
428    }
429
430    private Errors addMessage(Throwable cause, String messageFormat, Object... arguments) {
431        String message = format(messageFormat, arguments);
432        addMessage(new Message(getSources(), message, cause));
433        return this;
434    }
435
436    public Errors addMessage(Message message) {
437        if (root.errors == null) {
438            root.errors = Lists.newArrayList();
439        }
440        root.errors.add(message);
441        return this;
442    }
443
444    public static String format(String messageFormat, Object... arguments) {
445        for (int i = 0; i < arguments.length; i++) {
446            arguments[i] = Errors.convert(arguments[i]);
447        }
448        return String.format(messageFormat, arguments);
449    }
450
451    public List<Message> getMessages() {
452        if (root.errors == null) {
453            return ImmutableList.of();
454        }
455
456        List<Message> result = Lists.newArrayList(root.errors);
457        Collections.sort(result, new Comparator<Message>() {
458            public int compare(Message a, Message b) {
459                return a.getSource().compareTo(b.getSource());
460            }
461        });
462
463        return result;
464    }
465
466    /**
467     * Returns the formatted message for an exception with the specified messages.
468     */
469    public static String format(String heading, Collection<Message> errorMessages) {
470        Formatter fmt = new Formatter().format(heading).format(":%n%n");
471        int index = 1;
472        boolean displayCauses = getOnlyCause(errorMessages) == null;
473
474        for (Message errorMessage : errorMessages) {
475            fmt.format("%s) %s%n", index++, errorMessage.getMessage());
476
477            List<Object> dependencies = errorMessage.getSources();
478            for (int i = dependencies.size() - 1; i >= 0; i--) {
479                Object source = dependencies.get(i);
480                formatSource(fmt, source);
481            }
482
483            Throwable cause = errorMessage.getCause();
484            if (displayCauses && cause != null) {
485                StringWriter writer = new StringWriter();
486                cause.printStackTrace(new PrintWriter(writer));
487                fmt.format("Caused by: %s", writer.getBuffer());
488            }
489
490            fmt.format("%n");
491        }
492
493        if (errorMessages.size() == 1) {
494            fmt.format("1 error");
495        } else {
496            fmt.format("%s errors", errorMessages.size());
497        }
498
499        return fmt.toString();
500    }
501
502    /**
503     * Returns {@code value} if it is non-null allowed to be null. Otherwise a message is added and
504     * an {@code ErrorsException} is thrown.
505     */
506    public <T> T checkForNull(T value, Object source, Dependency<?> dependency)
507            throws ErrorsException {
508        if (value != null || dependency.isNullable()) {
509            return value;
510        }
511
512        int parameterIndex = dependency.getParameterIndex();
513        String parameterName = (parameterIndex != -1)
514                ? "parameter " + parameterIndex + " of "
515                : "";
516        addMessage("null returned by binding at %s%n but %s%s is not @Nullable",
517                source, parameterName, dependency.getInjectionPoint().getMember());
518
519        throw toException();
520    }
521
522    /**
523     * Returns the cause throwable if there is exactly one cause in {@code messages}. If there are
524     * zero or multiple messages with causes, null is returned.
525     */
526    public static Throwable getOnlyCause(Collection<Message> messages) {
527        Throwable onlyCause = null;
528        for (Message message : messages) {
529            Throwable messageCause = message.getCause();
530            if (messageCause == null) {
531                continue;
532            }
533
534            if (onlyCause != null) {
535                return null;
536            }
537
538            onlyCause = messageCause;
539        }
540
541        return onlyCause;
542    }
543
544    public int size() {
545        return root.errors == null ? 0 : root.errors.size();
546    }
547
548    private static abstract class Converter<T> {
549
550        final Class<T> type;
551
552        Converter(Class<T> type) {
553            this.type = type;
554        }
555
556        boolean appliesTo(Object o) {
557            return type.isAssignableFrom(o.getClass());
558        }
559
560        String convert(Object o) {
561            return toString(type.cast(o));
562        }
563
564        abstract String toString(T t);
565    }
566
567    private static final Collection<Converter<?>> converters = ImmutableList.of(
568            new Converter<Class>(Class.class) {
569                public String toString(Class c) {
570                    return c.getName();
571                }
572            },
573            new Converter<Member>(Member.class) {
574                public String toString(Member member) {
575                    return MoreTypes.toString(member);
576                }
577            },
578            new Converter<Key>(Key.class) {
579                public String toString(Key key) {
580                    if (key.getAnnotationType() != null) {
581                        return key.getTypeLiteral() + " annotated with "
582                                + (key.getAnnotation() != null ? key.getAnnotation() : key.getAnnotationType());
583                    } else {
584                        return key.getTypeLiteral().toString();
585                    }
586                }
587            }
588    );
589
590    public static Object convert(Object o) {
591        for (Converter<?> converter : converters) {
592            if (converter.appliesTo(o)) {
593                return converter.convert(o);
594            }
595        }
596        return o;
597    }
598
599    public static void formatSource(Formatter formatter, Object source) {
600        if (source instanceof Dependency) {
601            Dependency<?> dependency = (Dependency<?>) source;
602            InjectionPoint injectionPoint = dependency.getInjectionPoint();
603            if (injectionPoint != null) {
604                formatInjectionPoint(formatter, dependency, injectionPoint);
605            } else {
606                formatSource(formatter, dependency.getKey());
607            }
608
609        } else if (source instanceof InjectionPoint) {
610            formatInjectionPoint(formatter, null, (InjectionPoint) source);
611
612        } else if (source instanceof Class) {
613            formatter.format("  at %s%n", StackTraceElements.forType((Class<?>) source));
614
615        } else if (source instanceof Member) {
616            formatter.format("  at %s%n", StackTraceElements.forMember((Member) source));
617
618        } else if (source instanceof TypeLiteral) {
619            formatter.format("  while locating %s%n", source);
620
621        } else if (source instanceof Key) {
622            Key<?> key = (Key<?>) source;
623            formatter.format("  while locating %s%n", convert(key));
624
625        } else {
626            formatter.format("  at %s%n", source);
627        }
628    }
629
630    public static void formatInjectionPoint(Formatter formatter, Dependency<?> dependency,
631                                            InjectionPoint injectionPoint) {
632        Member member = injectionPoint.getMember();
633        Class<? extends Member> memberType = MoreTypes.memberType(member);
634
635        if (memberType == Field.class) {
636            dependency = injectionPoint.getDependencies().get(0);
637            formatter.format("  while locating %s%n", convert(dependency.getKey()));
638            formatter.format("    for field at %s%n", StackTraceElements.forMember(member));
639
640        } else if (dependency != null) {
641            formatter.format("  while locating %s%n", convert(dependency.getKey()));
642            formatter.format("    for parameter %s at %s%n",
643                    dependency.getParameterIndex(), StackTraceElements.forMember(member));
644
645        } else {
646            formatSource(formatter, injectionPoint.getMember());
647        }
648    }
649}