PageRenderTime 57ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 0ms

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

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