PageRenderTime 887ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

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

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