/** * Returns the cause throwable if there is exactly one cause in * {@code messages}. If there are zero or multiple messages with causes, * null is returned. */ public static Throwable getOnlyCause(Collection<Message> messages) { Throwable onlyCause = null; for (Message message : messages) { Throwable messageCause = message.getCause(); if (messageCause == null) { continue; } if (onlyCause != null) { return null; } onlyCause = messageCause; } return onlyCause; }
@Test public void testUserReportedError() { final Message message = new Message(getClass(), "Whoops!"); try { Guice.createInjector(new AbstractModule() { @Override protected void configure() { addError(message); } }); fail(); } catch (SaltaException expected) { if (!expected.getMessage().contains("Whoops")) throw expected; } }
public void testGenericErrorMessageMakesSense() { try { Guice.createInjector( new AbstractModule() { @Override protected void configure() { install(new FactoryModuleBuilder().build(Key.get(Foo.Factory.class))); } }); fail(); } catch (CreationException ce) { // Assert not only that it's the correct message, but also that it's the *only* message. Collection<Message> messages = ce.getErrorMessages(); assertEquals( Foo.Factory.class.getName() + " cannot be used as a key; It is not fully specified.", Iterables.getOnlyElement(messages).getMessage()); } }
/** * Ensure that when we cleanup failed JIT bindings, we don't break. * The test here requires a sequence of JIT bindings: * A-> B * B -> C, A * C -> A, D * D not JITable * The problem was that C cleaned up A's binding and then handed control back to B, * which tried to continue processing A.. but A was removed from the jitBindings Map, * so it attempts to create a new JIT binding for A, but we haven't yet finished * constructing the first JIT binding for A, so we get a recursive * computation exception from ComputingConcurrentHashMap. * * We also throw in a valid JIT binding, E, to guarantee that if * something fails in this flow, it can be recreated later if it's * not from a failed sequence. */ public void testRecursiveJitBindingsCleanupCorrectly() throws Exception { Injector injector = Guice.createInjector(); try { injector.getInstance(A.class); fail("Expected failure"); } catch(ConfigurationException expected) { Message msg = Iterables.getOnlyElement(expected.getErrorMessages()); Asserts.assertContains(msg.getMessage(), "Could not find a suitable constructor in " + D.class.getName()); } // Assert that we've removed all the bindings. assertNull(injector.getExistingBinding(Key.get(A.class))); assertNull(injector.getExistingBinding(Key.get(B.class))); assertNull(injector.getExistingBinding(Key.get(C.class))); assertNull(injector.getExistingBinding(Key.get(D.class))); // Confirm that we didn't prevent 'E' from working. assertNotNull(injector.getBinding(Key.get(E.class))); }
/** Prepends the list of sources to the given {@link Message} */ static Message mergeSources(List<Object> sources, Message message) { List<Object> messageSources = message.getSources(); // It is possible that the end of getSources() and the beginning of message.getSources() are // equivalent, in this case we should drop the repeated source when joining the lists. The // most likely scenario where this would happen is when a scoped binding throws an exception, // due to the fact that InternalFactoryToProviderAdapter applies the binding source when // merging errors. if (!sources.isEmpty() && !messageSources.isEmpty() && Objects.equal(messageSources.get(0), sources.get(sources.size() - 1))) { messageSources = messageSources.subList(1, messageSources.size()); } return new Message( ImmutableList.builder().addAll(sources).addAll(messageSources).build(), message.getMessage(), message.getCause()); }
/** * Returns the cause throwable if there is exactly one cause in {@code messages}. If there are * zero or multiple messages with causes, null is returned. */ public static Throwable getOnlyCause(Collection<Message> messages) { Throwable onlyCause = null; for (Message message : messages) { Throwable messageCause = message.getCause(); if (messageCause == null) { continue; } if (onlyCause != null && !ThrowableEquivalence.INSTANCE.equivalent(onlyCause, messageCause)) { return null; } onlyCause = messageCause; } return onlyCause; }
public void testDuplicateCausesCollapsed() { final RuntimeException sharedException = new RuntimeException("fail"); try { Guice.createInjector( new AbstractModule() { @Override protected void configure() { addError(sharedException); addError(sharedException); } }); fail(); } catch (CreationException ce) { assertEquals(sharedException, ce.getCause()); assertEquals(2, ce.getErrorMessages().size()); for (Message message : ce.getErrorMessages()) { assertEquals(sharedException, message.getCause()); } } }
@SuppressWarnings("unchecked") private void assertFailure(Injector injector, Class clazz) { try { injector.getBinding(clazz); fail("Shouldn't have been able to get binding of: " + clazz); } catch (ConfigurationException expected) { Message msg = Iterables.getOnlyElement(expected.getErrorMessages()); Asserts.assertContains( msg.getMessage(), "No implementation for " + InvalidInterface.class.getName() + " was bound."); List<Object> sources = msg.getSources(); // Assert that the first item in the sources if the key for the class we're looking up, // ensuring that each lookup is "new". assertEquals(Key.get(clazz).toString(), sources.get(0).toString()); // Assert that the last item in each lookup contains the InvalidInterface class Asserts.assertContains( sources.get(sources.size() - 1).toString(), Key.get(InvalidInterface.class).toString()); } }
/** * Ensure that when we cleanup failed JIT bindings, we don't break. The test here requires a * sequence of JIT bindings: * * <ol> * <li> A-> B * <li> B -> C, A * <li> C -> A, D * <li> D not JITable * </ol> * * <p>The problem was that C cleaned up A's binding and then handed control back to B, which tried * to continue processing A.. but A was removed from the jitBindings Map, so it attempts to create * a new JIT binding for A, but we haven't yet finished constructing the first JIT binding for A, * so we get a recursive computation exception from ComputingConcurrentHashMap. * * <p>We also throw in a valid JIT binding, E, to guarantee that if something fails in this flow, * it can be recreated later if it's not from a failed sequence. */ public void testRecursiveJitBindingsCleanupCorrectly() throws Exception { Injector injector = Guice.createInjector(); try { injector.getInstance(A.class); fail("Expected failure"); } catch (ConfigurationException expected) { Message msg = Iterables.getOnlyElement(expected.getErrorMessages()); Asserts.assertContains( msg.getMessage(), "Could not find a suitable constructor in " + D.class.getName()); } // Assert that we've removed all the bindings. assertNull(injector.getExistingBinding(Key.get(A.class))); assertNull(injector.getExistingBinding(Key.get(B.class))); assertNull(injector.getExistingBinding(Key.get(C.class))); assertNull(injector.getExistingBinding(Key.get(D.class))); // Confirm that we didn't prevent 'E' from working. assertNotNull(injector.getBinding(Key.get(E.class))); }
public void testUserReportedErrorsAreAlsoLogged() { try { Guice.createInjector( new AbstractModule() { @Override protected void configure() { addError(new Message("Whoops!", new IllegalArgumentException())); } }); fail(); } catch (CreationException expected) { } LogRecord logRecord = Iterables.getOnlyElement(this.logRecords); assertContains( logRecord.getMessage(), "An exception was caught and reported. Message: java.lang.IllegalArgumentException"); }
/** * At injector-creation time, we initialize the invocation handler. At this time we make sure * all factory methods will be able to build the target types. */ @Inject @Toolable void initialize(Injector injector) { if (this.injector != null) { throw new ConfigurationException(ImmutableList.of(new Message(FactoryProvider2.class, "Factories.create() factories may only be used in one Injector!"))); } this.injector = injector; for (Map.Entry<Method, AssistData> entry : assistDataByMethod.entrySet()) { Method method = entry.getKey(); AssistData data = entry.getValue(); Object[] args; if(!data.optimized) { args = new Object[method.getParameterTypes().length]; Arrays.fill(args, "dummy object for validating Factories"); } else { args = null; // won't be used -- instead will bind to data.providers. } getBindingFromNewInjector(method, args, data); // throws if the binding isn't properly configured } }
public void testGenericErrorMessageMakesSense() { try { Guice.createInjector(new AbstractModule() { @Override protected void configure() { install(new FactoryModuleBuilder().build(Key.get(Foo.Factory.class))); } }); fail(); } catch(CreationException ce ) { // Assert not only that it's the correct message, but also that it's the *only* message. Collection<Message> messages = ce.getErrorMessages(); assertEquals( Foo.Factory.class.getName() + " cannot be used as a key; It is not fully specified.", Iterables.getOnlyElement(messages).getMessage()); } }
public void testAddErrors() { try { Guice.createInjector(new AbstractModule() { @Override protected void configure() { requestInjection(new Object()); bindListener(Matchers.any(), new TypeListener() { public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) { encounter.addError("There was an error on %s", type); encounter.addError(new IllegalArgumentException("whoops!")); encounter.addError(new Message("And another problem")); encounter.addError(new IllegalStateException()); } }); } }); fail(); } catch (CreationException expected) { assertContains(expected.getMessage(), "1) There was an error on java.lang.Object", "2) An exception was caught and reported. Message: whoops!", "3) And another problem", "4) An exception was caught and reported. Message: null", "4 errors"); } }
public void testUserReportedErrorsAreAlsoLogged() { try { Guice.createInjector(new AbstractModule() { @Override protected void configure() { addError(new Message("Whoops!", new IllegalArgumentException())); } }); fail(); } catch (CreationException expected) { } LogRecord logRecord = Iterables.getOnlyElement(this.logRecords); assertContains(logRecord.getMessage(), "An exception was caught and reported. Message: java.lang.IllegalArgumentException"); }
public static List<Message> getMessage( String response ) { ObjectMapper mapper = new ObjectMapper(); List<Message> messages = null; try { messages = mapper.readValue( response, new TypeReference<List<Message>>() { } ); } catch ( IOException e ) { e.printStackTrace(); } return messages; }
/** * Gets an instance from Guice for the provided class. Any missing flags or bindings will be * printed in the error message if there was a problem retrieving the class instance. * * @param cls The class type to retrieve from the Guice injector. * @param <T> The type of the class that is being returned. * @return The class instance retrieved from Guice. */ public <T> T get(Class<T> cls) { try { return injector.getInstance(cls); } catch (ProvisionException e) { System.err.println("Could not start application:"); for (Message msg : e.getErrorMessages()) { System.err.println(" " + msg.toString()); } System.exit(1); } throw new IllegalStateException("Did not get an instance, and did not get an exception?"); }
@Override protected final void configure() { Properties properties = new Properties(); try (FileInputStream stream = new FileInputStream(fileName)) { properties.load(stream); Names.bindProperties(binder(), properties); } catch (Exception ex) { addError(new Message("Could not load properties file: " + fileName, ex)); } }
public static Collection<Message> getMessagesFromThrowable( Throwable throwable) { if (throwable instanceof ProvisionException) { return ((ProvisionException) throwable).getErrorMessages(); } else if (throwable instanceof ConfigurationException) { return ((ConfigurationException) throwable).getErrorMessages(); } else if (throwable instanceof CreationException) { return ((CreationException) throwable).getErrorMessages(); } else { return ImmutableSet.of(); } }
public Errors errorInUserCode(Throwable cause, String messageFormat, Object... arguments) { Collection<Message> messages = getMessagesFromThrowable(cause); if (!messages.isEmpty()) { return merge(messages); } else { return addMessage(cause, messageFormat, arguments); } }
public Errors addMessage(Message message) { if (root.errors == null) { root.errors = Lists.newArrayList(); } root.errors.add(message); return this; }
public List<Message> getMessages() { if (root.errors == null) { return ImmutableList.of(); } return new ArrayList<>(root.errors); }
/** * Returns the formatted message for an exception with the specified * messages. */ public static String format(String heading, Collection<Message> errorMessages) { Formatter fmt = new Formatter().format(heading).format(":%n%n"); int index = 1; boolean displayCauses = getOnlyCause(errorMessages) == null; for (Message errorMessage : errorMessages) { fmt.format("%s) %s%n", index++, errorMessage.getMessage()); Throwable cause = errorMessage.getCause(); if (displayCauses && cause != null) { StringWriter writer = new StringWriter(); cause.printStackTrace(new PrintWriter(writer)); fmt.format("Caused by: %s", writer.getBuffer()); } fmt.format("%n"); } if (errorMessages.size() == 1) { fmt.format("1 error"); } else { fmt.format("%s errors", errorMessages.size()); } return fmt.toString(); }
void configure(PrivateBinder binder) { binder = binder.withSource(source); if (errors.hasErrors()) { for (Message message : errors.getMessages()) { binder.addError(message); } } else { bindProvidersInScope(binder); } }
public BindingCollector addBinding(Key<?> key, TypeLiteral<?> target) { if (bindings.containsKey(key)) { throw new ConfigurationException(Collections.singleton( new Message("Only one implementation can be specified for " + key))); } bindings.put(key, target); return this; }
public GuiceBindingVisitor(Key<T> targetKey, List<Message> messages, GinjectorBindings bindingsCollection, BindingFactory bindingFactory) { this.targetKey = targetKey; this.messages = messages; this.bindingsCollection = bindingsCollection; this.bindingFactory = bindingFactory; }
public Void visit(InstanceBinding<? extends T> instanceBinding) { T instance = instanceBinding.getInstance(); if (BindConstantBinding.isConstantKey(targetKey)) { Context context = Context.forElement(instanceBinding); bindingsCollection.addBinding(targetKey, bindingFactory.getBindConstantBinding(targetKey, instance, context)); } else { messages.add(new Message(instanceBinding.getSource(), PrettyPrinter.format("Instance binding not supported; key=%s, inst=%s", targetKey, instance))); } return null; }
public Void visitScopeAnnotation(Class<? extends Annotation> scopeAnnotation) { if (scopeAnnotation == Singleton.class || scopeAnnotation == javax.inject.Singleton.class) { bindingsCollection.putScope(targetKey, GinScope.SINGLETON); } else { messages.add(new Message(PrettyPrinter.format("Unsupported scope annotation: key=%s scope=%s", targetKey, scopeAnnotation))); } return null; }
public void visitElementsAndReportErrors(List<Element> elements) { visitElements(elements); // Capture any binding errors, any of which we treat as fatal. if (!messages.isEmpty()) { for (Message message : messages) { // tostring has both source and message so use that errorManager.logError(message.toString(), message.getCause()); } } }
@Test public void guiceConfigurationException_dontShowConfigurationException() { // Arrange LinkedList<Message> messages = new LinkedList<Message>(); messages.add(new Message( "No implementation for java.lang.Integer annotated with @com.google.inject.name.Named(value=random_forest_number_of_trees) was bound.")); ConfigurationException exception = new ConfigurationException(messages); // Act messagePrintingExceptionHandler.handle(exception, path); // Assert assertTrue(errContent.toString().contains( "No implementation for java.lang.Integer annotated with @com.google.inject.name.Named:") == false); }
@Test public void guiceCreationException_dontShowCreationException() { // Arrange LinkedList<Message> messages = new LinkedList<Message>(); messages.add(new Message( "No implementation for java.lang.String annotated with @com.google.inject.name.Named(value=measurement_classifier_path) was bound.")); CreationException exception = new CreationException(messages); // Act messagePrintingExceptionHandler.handle(exception, path); // Assert assertTrue(errContent.toString().contains( "No implementation for java.lang.String annotated with @com.google.inject.name.Named") == false); }
@Test public void guiceCreationException_showCreationExceptionCauses() { // Arrange LinkedList<Message> messages = new LinkedList<Message>(); messages.add(new Message( "No implementation for java.lang.String annotated with @com.google.inject.name.Named(value=measurement_classifier_path) was bound.")); CreationException exception = new CreationException(messages); // Act messagePrintingExceptionHandler.handle(exception, path); // Assert assertTrue(errContent.toString().contains("measurement_classifier_path")); }
private void validate(Injector injector) { // Note that here we don't report all errors at once, correctness relies on Guice injecting even providers // that get de-duplicated. This way, all errors are attached to the right source. List<Message> messages = new ArrayList<>(); // Get the errors into a multimap by priority Set<ListBinderErrors<T>> errorSet = injector.getInstance(errorSetKey); ListMultimap<Priority, ListBinderErrors<T>> errorMap = ArrayListMultimap.create(); for (ListBinderErrors<T> errors : errorSet) { errorMap.put(errors.priority, errors); } // Check for duplicate priorities List<ListBinderErrors<T>> ourPriorityErrors = errorMap.get(priority); ListBinderErrors<T> ourErrors = ourPriorityErrors.get(0); if (ourPriorityErrors.size() > 1) { messages.add(ourErrors.duplicateBindersError); } // Check for default and non-default priorities if (errorMap.containsKey(Priority.getDefault()) && errorMap.keySet().size() > 1) { messages.add(ourErrors.conflictingDefaultExplicitError); } if (!messages.isEmpty()) { throw new CreationException(messages); } }
@Test public void testMessage() { thrown.expect(CreationException.class); thrown.expectMessage("the message"); thrown.expectMessage("at the source"); thrown.expectMessage(not(containsString("CreationException"))); Guice.createInjector(new AbstractModule() { @Override protected void configure() { DelayedError.create(binder(), new Message("the source", "the message")); } }); }
public BindingCollector addBinding(Key<?> key, TypeLiteral<?> target) { if (bindings.containsKey(key)) { throw new ConfigurationException( ImmutableSet.of(new Message("Only one implementation can be specified for " + key))); } bindings.put(key, target); return this; }
/** * Returns true if the ConfigurationException is due to an error of TypeLiteral not being fully * specified. */ private boolean isTypeNotSpecified(TypeLiteral<?> typeLiteral, ConfigurationException ce) { Collection<Message> messages = ce.getErrorMessages(); if (messages.size() == 1) { Message msg = Iterables.getOnlyElement(new Errors().keyNotFullySpecified(typeLiteral).getMessages()); return msg.getMessage().equals(Iterables.getOnlyElement(messages).getMessage()); } else { return false; } }
/** * At injector-creation time, we initialize the invocation handler. At this time we make sure all * factory methods will be able to build the target types. */ @Inject @Toolable void initialize(Injector injector) { if (this.injector != null) { throw new ConfigurationException( ImmutableList.of( new Message( FactoryProvider2.class, "Factories.create() factories may only be used in one Injector!"))); } this.injector = injector; for (Map.Entry<Method, AssistData> entry : assistDataByMethod.entrySet()) { Method method = entry.getKey(); AssistData data = entry.getValue(); Object[] args; if (!data.optimized) { args = new Object[method.getParameterTypes().length]; Arrays.fill(args, "dummy object for validating Factories"); } else { args = null; // won't be used -- instead will bind to data.providers. } getBindingFromNewInjector( method, args, data); // throws if the binding isn't properly configured } }
@SuppressWarnings("unchecked") // safe because it's a constructor of the typeLiteral static <T> Constructor<? extends T> findThrowingConstructor( TypeLiteral<? extends T> typeLiteral, Binder binder) { Class<?> rawType = typeLiteral.getRawType(); Errors errors = new Errors(rawType); Constructor<?> cxtor = null; for (Constructor<?> constructor : rawType.getDeclaredConstructors()) { if (constructor.isAnnotationPresent(ThrowingInject.class)) { if (cxtor != null) { errors.addMessage( "%s has more than one constructor annotated with @ThrowingInject. " + CONSTRUCTOR_RULES, rawType); } cxtor = constructor; Annotation misplacedBindingAnnotation = Annotations.findBindingAnnotation( errors, cxtor, ((AnnotatedElement) cxtor).getAnnotations()); if (misplacedBindingAnnotation != null) { errors.misplacedBindingAnnotation(cxtor, misplacedBindingAnnotation); } } } if (cxtor == null) { errors.addMessage( "Could not find a suitable constructor in %s. " + CONSTRUCTOR_RULES, rawType); } for (Message msg : errors.getMessages()) { binder.addError(msg); } return (Constructor<? extends T>) cxtor; }
public void testCxtorWithManyExceptions() { try { Guice.createInjector( new AbstractModule() { @Override protected void configure() { ThrowingProviderBinder.create(binder()) .bind(RemoteProvider.class, Foo.class) .providing(ManyExceptionFoo.class); } }); fail(); } catch (CreationException ce) { // The only two that should fail are Interrupted & TooManyListeners.. the rest are OK. List<Message> errors = ImmutableList.copyOf(ce.getErrorMessages()); assertEquals( InterruptedException.class.getName() + " is not compatible with the exceptions ([" + RemoteException.class + ", " + BindException.class + "]) declared in the CheckedProvider interface (" + RemoteProvider.class.getName() + ")", errors.get(0).getMessage()); assertEquals( TooManyListenersException.class.getName() + " is not compatible with the exceptions ([" + RemoteException.class + ", " + BindException.class + "]) declared in the CheckedProvider interface (" + RemoteProvider.class.getName() + ")", errors.get(1).getMessage()); assertEquals(2, errors.size()); } }
public void testProvisionExceptionOnDependenciesOfCxtor() throws Exception { Injector injector = Guice.createInjector( new AbstractModule() { @Override protected void configure() { ThrowingProviderBinder.create(binder()) .bind(RemoteProvider.class, Foo.class) .providing(ProvisionExceptionFoo.class); bindScope( BadScope.class, new Scope() { @Override public <T> Provider<T> scope(final Key<T> key, Provider<T> unscoped) { return new Provider<T>() { @Override public T get() { throw new OutOfScopeException("failure: " + key.toString()); } }; } }); } }); try { injector.getInstance(Key.get(remoteProviderOfFoo)).get(); fail(); } catch (ProvisionException pe) { Message message = Iterables.getOnlyElement(pe.getErrorMessages()); assertEquals( "Error in custom provider, com.google.inject.OutOfScopeException: failure: " + Key.get(Unscoped1.class), message.getMessage()); } }
public void testProviderMethodWithManyExceptions() { try { Guice.createInjector( new AbstractModule() { @Override protected void configure() { install(ThrowingProviderBinder.forModule(this)); } @SuppressWarnings("unused") @CheckedProvides(RemoteProvider.class) String foo() throws InterruptedException, RuntimeException, RemoteException, AccessException, TooManyListenersException { return null; } }); fail(); } catch (CreationException ce) { // The only two that should fail are Interrupted & TooManyListeners.. the rest are OK. List<Message> errors = ImmutableList.copyOf(ce.getErrorMessages()); assertEquals( InterruptedException.class.getName() + " is not compatible with the exceptions ([" + RemoteException.class + "]) declared in the CheckedProvider interface (" + RemoteProvider.class.getName() + ")", errors.get(0).getMessage()); assertEquals( TooManyListenersException.class.getName() + " is not compatible with the exceptions ([" + RemoteException.class + "]) declared in the CheckedProvider interface (" + RemoteProvider.class.getName() + ")", errors.get(1).getMessage()); assertEquals(2, errors.size()); } }