PageRenderTime 1469ms CodeModel.GetById 61ms RepoModel.GetById 16ms app.codeStats 0ms

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

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