/core/test/com/google/inject/TypeListenerTest.java
Java | 710 lines | 559 code | 106 blank | 45 comment | 0 complexity | a9195d3e77e2df5049640be40cbf5b01 MD5 | raw file
- /**
- * Copyright (C) 2009 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package com.google.inject;
- import static com.google.inject.Asserts.asModuleChain;
- import static com.google.inject.Asserts.assertContains;
- import static com.google.inject.Asserts.getDeclaringSourcePart;
- import static com.google.inject.matcher.Matchers.any;
- import static com.google.inject.matcher.Matchers.only;
- import static com.google.inject.name.Names.named;
- import com.google.common.collect.ImmutableList;
- import com.google.common.collect.Lists;
- import com.google.inject.matcher.Matcher;
- import com.google.inject.matcher.Matchers;
- import com.google.inject.spi.InjectionListener;
- import com.google.inject.spi.Message;
- import com.google.inject.spi.TypeEncounter;
- import com.google.inject.spi.TypeListener;
- import junit.framework.TestCase;
- import java.util.List;
- import java.util.concurrent.atomic.AtomicInteger;
- import java.util.concurrent.atomic.AtomicReference;
- /**
- * @author jessewilson@google.com (Jesse Wilson)
- */
- public class TypeListenerTest extends TestCase {
- private final Matcher<Object> onlyAbcd = Matchers.only(new TypeLiteral<A>() {})
- .or(only(new TypeLiteral<B>() {}))
- .or(only(new TypeLiteral<C>() {}))
- .or(only(new TypeLiteral<D>() {}));
- final TypeListener failingTypeListener = new TypeListener() {
- int failures = 0;
- public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
- throw new ClassCastException("whoops, failure #" + (++failures));
- }
- @Override public String toString() {
- return "clumsy";
- }
- };
- final InjectionListener<Object> failingInjectionListener = new InjectionListener<Object>() {
- int failures = 0;
- public void afterInjection(Object injectee) {
- throw new ClassCastException("whoops, failure #" + (++failures));
- }
- @Override public String toString() {
- return "goofy";
- }
- };
- final MembersInjector<Object> failingMembersInjector = new MembersInjector<Object>() {
- int failures = 0;
- public void injectMembers(Object instance) {
- throw new ClassCastException("whoops, failure #" + (++failures));
- }
- @Override public String toString() {
- return "awkward";
- }
- };
- public void testTypeListenersAreFired() {
- final AtomicInteger firedCount = new AtomicInteger();
- final TypeListener typeListener = new TypeListener() {
- public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
- assertEquals(new TypeLiteral<A>() {}, type);
- firedCount.incrementAndGet();
- }
- };
- Guice.createInjector(new AbstractModule() {
- @Override protected void configure() {
- bindListener(onlyAbcd, typeListener);
- bind(A.class);
- }
- });
- assertEquals(1, firedCount.get());
- }
- public void testInstallingInjectionListener() {
- final List<Object> injectees = Lists.newArrayList();
- final InjectionListener<Object> injectionListener = new InjectionListener<Object>() {
- public void afterInjection(Object injectee) {
- injectees.add(injectee);
- }
- };
- Injector injector = Guice.createInjector(new AbstractModule() {
- @Override protected void configure() {
- bindListener(onlyAbcd, new TypeListener() {
- public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
- encounter.register(injectionListener);
- }
- });
- bind(A.class);
- }
- });
- assertEquals(ImmutableList.of(), injectees);
- Object a1 = injector.getInstance(A.class);
- assertEquals(ImmutableList.of(a1), injectees);
- Object a2 = injector.getInstance(A.class);
- assertEquals(ImmutableList.of(a1, a2), injectees);
- Object b1 = injector.getInstance(B.class);
- assertEquals(ImmutableList.of(a1, a2, b1), injectees);
- Provider<A> aProvider = injector.getProvider(A.class);
- assertEquals(ImmutableList.of(a1, a2, b1), injectees);
- A a3 = aProvider.get();
- A a4 = aProvider.get();
- assertEquals(ImmutableList.of(a1, a2, b1, a3, a4), injectees);
- }
-
- /*if[AOP]*/
- private static org.aopalliance.intercept.MethodInterceptor prefixInterceptor(
- final String prefix) {
- return new org.aopalliance.intercept.MethodInterceptor() {
- public Object invoke(org.aopalliance.intercept.MethodInvocation methodInvocation)
- throws Throwable {
- return prefix + methodInvocation.proceed();
- }
- };
- }
- public void testAddingInterceptors() throws NoSuchMethodException {
- final Matcher<Object> buzz = only(C.class.getMethod("buzz"));
- Injector injector = Guice.createInjector(new AbstractModule() {
- @Override protected void configure() {
- bindInterceptor(any(), buzz, prefixInterceptor("ka"));
- bindInterceptor(any(), any(), prefixInterceptor("fe"));
- bindListener(onlyAbcd, new TypeListener() {
- public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
- encounter.bindInterceptor(any(), prefixInterceptor("li"));
- encounter.bindInterceptor(buzz, prefixInterceptor("no"));
- }
- });
- }
- });
- // interceptors must be invoked in the order they're bound.
- C c = injector.getInstance(C.class);
- assertEquals("kafelinobuzz", c.buzz());
- assertEquals("felibeep", c.beep());
- }
- /*end[AOP]*/
- class OuterThrowsModule extends AbstractModule {
- @Override protected void configure() {
- install(new InnerThrowsModule());
- }
- }
- class InnerThrowsModule extends AbstractModule {
- @Override protected void configure() {
- bindListener(onlyAbcd, failingTypeListener);
- bind(B.class);
- bind(C.class);
- }
- }
- public void testTypeListenerThrows() {
- try {
- Guice.createInjector(new OuterThrowsModule());
- fail();
- } catch (CreationException expected) {
- assertContains(expected.getMessage(),
- "1) Error notifying TypeListener clumsy (bound at " + getClass().getName(),
- getDeclaringSourcePart(getClass()),
- asModuleChain(OuterThrowsModule.class, InnerThrowsModule.class),
- "of " + B.class.getName(),
- "Reason: java.lang.ClassCastException: whoops, failure #1",
- "2) Error notifying TypeListener clumsy (bound at " + getClass().getName(),
- getDeclaringSourcePart(getClass()),
- asModuleChain(OuterThrowsModule.class, InnerThrowsModule.class),
- "of " + C.class.getName(),
- "Reason: java.lang.ClassCastException: whoops, failure #2");
- }
-
- Injector injector = Guice.createInjector(new AbstractModule() {
- @Override protected void configure() {
- bindListener(onlyAbcd, failingTypeListener);
- }
- });
- try {
- injector.getProvider(B.class);
- fail();
- } catch (ConfigurationException expected) {
- assertContains(expected.getMessage(),
- "1) Error notifying TypeListener clumsy (bound at " + getClass().getName(),
- getDeclaringSourcePart(getClass()),
- "of " + B.class.getName(),
- "Reason: java.lang.ClassCastException: whoops, failure #3");
- }
- // getting it again should yield the same exception #3
- try {
- injector.getInstance(B.class);
- fail();
- } catch (ConfigurationException expected) {
- assertContains(expected.getMessage(),
- "1) Error notifying TypeListener clumsy (bound at " + getClass().getName(),
- getDeclaringSourcePart(getClass()),
- "of " + B.class.getName(),
- "Reason: java.lang.ClassCastException: whoops, failure #3");
- }
- // non-injected types do not participate
- assertSame(Stage.DEVELOPMENT, injector.getInstance(Stage.class));
- }
- public void testInjectionListenerThrows() {
- Injector injector = Guice.createInjector(new AbstractModule() {
- @Override protected void configure() {
- bindListener(onlyAbcd, new TypeListener() {
- public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
- encounter.register(failingInjectionListener);
- }
- });
- bind(B.class);
- }
- });
- try {
- injector.getInstance(A.class);
- fail();
- } catch (ProvisionException e) {
- assertContains(e.getMessage(),
- "1) Error notifying InjectionListener goofy of " + A.class.getName(),
- " Reason: java.lang.ClassCastException: whoops, failure #1");
- }
- // second time through should be a new cause (#2)
- try {
- injector.getInstance(A.class);
- fail();
- } catch (ProvisionException e) {
- assertContains(e.getMessage(),
- "1) Error notifying InjectionListener goofy of " + A.class.getName(),
- " Reason: java.lang.ClassCastException: whoops, failure #2");
- }
- // we should get errors for all types, but only on getInstance()
- Provider<B> bProvider = injector.getProvider(B.class);
- try {
- bProvider.get();
- fail();
- } catch (ProvisionException e) {
- assertContains(e.getMessage(),
- "1) Error notifying InjectionListener goofy of " + B.class.getName(),
- " Reason: java.lang.ClassCastException: whoops, failure #3");
- }
- // non-injected types do not participate
- assertSame(Stage.DEVELOPMENT, injector.getInstance(Stage.class));
- }
- public void testInjectMembersTypeListenerFails() {
- try {
- Guice.createInjector(new AbstractModule() {
- @Override protected void configure() {
- getMembersInjector(A.class);
- bindListener(onlyAbcd, failingTypeListener);
- }
- });
- fail();
- } catch (CreationException expected) {
- assertContains(expected.getMessage(),
- "1) Error notifying TypeListener clumsy (bound at ",
- TypeListenerTest.class.getName(), getDeclaringSourcePart(getClass()),
- "of " + A.class.getName(),
- " Reason: java.lang.ClassCastException: whoops, failure #1");
- }
- }
- public void testConstructedTypeListenerIsTheSameAsMembersInjectorListener() {
- final AtomicInteger typeEncounters = new AtomicInteger();
- final AtomicInteger injections = new AtomicInteger();
- final InjectionListener<A> listener = new InjectionListener<A>() {
- public void afterInjection(A injectee) {
- injections.incrementAndGet();
- assertNotNull(injectee.injector);
- }
- };
- Injector injector = Guice.createInjector(new AbstractModule() {
- @Override protected void configure() {
- bindListener(onlyAbcd, new TypeListener() {
- @SuppressWarnings("unchecked")
- public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
- typeEncounters.incrementAndGet();
- encounter.register((InjectionListener) listener);
- }
- });
- bind(A.class);
- getMembersInjector(A.class);
- }
- });
- // creating the injector shouldn't trigger injections
- assertEquals(0, injections.getAndSet(0));
- // constructing an A should trigger an injection
- injector.getInstance(A.class);
- assertEquals(1, injections.getAndSet(0));
- // injecting an A should trigger an injection
- injector.injectMembers(new A());
- assertEquals(1, injections.getAndSet(0));
- // getting a provider shouldn't
- Provider<A> aProvider = injector.getProvider(A.class);
- MembersInjector<A> aMembersInjector = injector.getMembersInjector(A.class);
- assertEquals(0, injections.getAndSet(0));
- // exercise the provider
- aProvider.get();
- aProvider.get();
- assertEquals(2, injections.getAndSet(0));
- // exercise the members injector
- aMembersInjector.injectMembers(new A());
- aMembersInjector.injectMembers(new A());
- assertEquals(2, injections.getAndSet(0));
- // we should only have encountered one type
- assertEquals(1, typeEncounters.getAndSet(0));
- }
- public void testLookupsAtInjectorCreateTime() {
- final AtomicReference<Provider<B>> bProviderReference = new AtomicReference<Provider<B>>();
- final AtomicReference<MembersInjector<A>> aMembersInjectorReference
- = new AtomicReference<MembersInjector<A>>();
- final InjectionListener<Object> lookupsTester = new InjectionListener<Object>() {
- public void afterInjection(Object injectee) {
- assertNotNull(bProviderReference.get().get());
- A a = new A();
- aMembersInjectorReference.get().injectMembers(a);
- assertNotNull(a.injector);
- }
- };
- Guice.createInjector(new AbstractModule() {
- @Override protected void configure() {
- bindListener(only(TypeLiteral.get(C.class)), new TypeListener() {
- public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
- Provider<B> bProvider = encounter.getProvider(B.class);
- try {
- bProvider.get();
- fail();
- } catch (IllegalStateException expected) {
- assertEquals("This Provider cannot be used until the Injector has been created.",
- expected.getMessage());
- }
- bProviderReference.set(bProvider);
- MembersInjector<A> aMembersInjector = encounter.getMembersInjector(A.class);
- try {
- aMembersInjector.injectMembers(new A());
- fail();
- } catch (IllegalStateException expected) {
- assertEquals(
- "This MembersInjector cannot be used until the Injector has been created.",
- expected.getMessage());
- }
- aMembersInjectorReference.set(aMembersInjector);
- encounter.register(lookupsTester);
- }
- });
- // this ensures the type listener fires, and also the afterInjection() listener
- bind(C.class).asEagerSingleton();
- }
- });
- lookupsTester.afterInjection(null);
- }
- public void testLookupsPostCreate() {
- Injector injector = Guice.createInjector(new AbstractModule() {
- @Override protected void configure() {
- bindListener(only(TypeLiteral.get(C.class)), new TypeListener() {
- public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
- assertNotNull(encounter.getProvider(B.class).get());
- A a = new A();
- encounter.getMembersInjector(A.class).injectMembers(a);
- assertNotNull(a.injector);
- }
- });
- }
- });
-
- injector.getInstance(C.class);
- }
- public void testMembersInjector() {
- final MembersInjector<D> membersInjector = new MembersInjector<D>() {
- public void injectMembers(D instance) {
- instance.userInjected++;
- assertEquals(instance.guiceInjected, instance.userInjected);
- }
- };
- final InjectionListener<D> injectionListener = new InjectionListener<D>() {
- public void afterInjection(D injectee) {
- assertTrue(injectee.userInjected > 0);
- injectee.listenersNotified++;
- assertEquals(injectee.guiceInjected, injectee.listenersNotified);
- }
- };
- Injector injector = Guice.createInjector(new AbstractModule() {
- @Override protected void configure() {
- bindListener(onlyAbcd, new TypeListener() {
- @SuppressWarnings("unchecked")
- public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
- encounter.register((MembersInjector) membersInjector);
- encounter.register((InjectionListener) injectionListener);
- }
- });
- D boundThreeTimes = new D();
- bind(D.class).annotatedWith(named("i")).toInstance(boundThreeTimes);
- bind(D.class).annotatedWith(named("ii")).toInstance(boundThreeTimes);
- bind(D.class).annotatedWith(named("iii")).toInstance(boundThreeTimes);
- }
- });
- D boundThreeTimes = injector.getInstance(Key.get(D.class, named("iii")));
- boundThreeTimes.assertAllCounts(1);
- D getInstance = injector.getInstance(D.class);
- getInstance.assertAllCounts(1);
- D memberInjection = new D();
- injector.injectMembers(memberInjection);
- memberInjection.assertAllCounts(1);
- injector.injectMembers(memberInjection);
- injector.injectMembers(memberInjection);
- memberInjection.assertAllCounts(3);
- injector.getMembersInjector(D.class).injectMembers(memberInjection);
- memberInjection.assertAllCounts(4);
- }
- public void testMembersInjectorThrows() {
- Injector injector = Guice.createInjector(new AbstractModule() {
- @Override protected void configure() {
- bindListener(onlyAbcd, new TypeListener() {
- public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
- encounter.register(failingMembersInjector);
- }
- });
- bind(B.class);
- }
- });
- try {
- injector.getInstance(A.class);
- fail();
- } catch (ProvisionException e) {
- assertContains(e.getMessage(),
- "1) Error injecting " + A.class.getName() + " using awkward.",
- "Reason: java.lang.ClassCastException: whoops, failure #1");
- }
- // second time through should be a new cause (#2)
- try {
- injector.getInstance(A.class);
- fail();
- } catch (ProvisionException e) {
- assertContains(e.getMessage(),
- "1) Error injecting " + A.class.getName() + " using awkward.",
- "Reason: java.lang.ClassCastException: whoops, failure #2");
- }
- // we should get errors for all types, but only on getInstance()
- Provider<B> bProvider = injector.getProvider(B.class);
- try {
- bProvider.get();
- fail();
- } catch (ProvisionException e) {
- assertContains(e.getMessage(),
- "1) Error injecting " + B.class.getName() + " using awkward.",
- "Reason: java.lang.ClassCastException: whoops, failure #3");
- }
- // non-injected types do not participate
- assertSame(Stage.DEVELOPMENT, injector.getInstance(Stage.class));
- }
- /**
- * We had a bug where we weren't notifying of types encountered for member injection when those
- * types had no members to be injected. Constructed types are always injected because they always
- * have at least one injection point: the class constructor.
- */
- public void testTypesWithNoInjectableMembersAreNotified() {
- final AtomicInteger notificationCount = new AtomicInteger();
- Guice.createInjector(new AbstractModule() {
- @Override protected void configure() {
- bindListener(onlyAbcd, new TypeListener() {
- public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
- notificationCount.incrementAndGet();
- }
- });
- bind(C.class).toInstance(new C());
- }
- });
- assertEquals(1, notificationCount.get());
- }
- public void testEncounterCannotBeUsedAfterHearReturns() {
- final AtomicReference<TypeEncounter<?>> encounterReference = new AtomicReference<TypeEncounter<?>>();
- Guice.createInjector(new AbstractModule() {
- @Override protected void configure() {
- bindListener(any(), new TypeListener() {
- public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
- encounterReference.set(encounter);
- }
- });
- bind(C.class);
- }
- });
- TypeEncounter<?> encounter = encounterReference.get();
- try {
- encounter.register(new InjectionListener<Object>() {
- public void afterInjection(Object injectee) {}
- });
- fail();
- } catch (IllegalStateException expected) {
- }
- /*if[AOP]*/
- try {
- encounter.bindInterceptor(any(), new org.aopalliance.intercept.MethodInterceptor() {
- public Object invoke(org.aopalliance.intercept.MethodInvocation methodInvocation)
- throws Throwable {
- return methodInvocation.proceed();
- }
- });
- fail();
- } catch (IllegalStateException expected) {
- }
- /*end[AOP]*/
- try {
- encounter.addError(new Exception());
- fail();
- } catch (IllegalStateException expected) {
- }
- try {
- encounter.getMembersInjector(A.class);
- fail();
- } catch (IllegalStateException expected) {
- }
- try {
- encounter.getProvider(B.class);
- fail();
- } catch (IllegalStateException expected) {
- }
- }
- 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");
- }
- }
- private static class CountingMembersInjector implements MembersInjector<D> {
- public void injectMembers(D instance) {
- ++instance.userInjected;
- }
- }
- private static class CountingInjectionListener implements InjectionListener<D> {
- public void afterInjection(D injectee) {
- ++injectee.listenersNotified;
- }
- }
- private static class DuplicatingTypeListener implements TypeListener {
- int count = 0;
- @SuppressWarnings({"rawtypes", "unchecked"})
- public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
- ++count;
- MembersInjector membersInjector = new CountingMembersInjector();
- encounter.register(membersInjector);
- encounter.register(membersInjector);
- InjectionListener injectionListener = new CountingInjectionListener();
- encounter.register(injectionListener);
- encounter.register(injectionListener);
- }
- }
- public void testDeDuplicateTypeListeners() {
- final DuplicatingTypeListener typeListener = new DuplicatingTypeListener();
- Injector injector = Guice.createInjector(new AbstractModule() {
- @Override
- protected void configure() {
- bindListener(any(), typeListener);
- bindListener(only(new TypeLiteral<D>() {}), typeListener);
- }
- });
- D d = injector.getInstance(D.class);
- d.assertAllCounts(1);
- assertEquals(1, typeListener.count);
- }
- // TODO: recursively accessing a lookup should fail
- static class A {
- @Inject Injector injector;
- @Inject Stage stage;
- }
- static class B {}
- public static class C {
- public String buzz() {
- return "buzz";
- }
- public String beep() {
- return "beep";
- }
- }
- static class D {
- int guiceInjected = 0;
- int userInjected = 0;
- int listenersNotified = 0;
- @Inject void guiceInjected() {
- guiceInjected++;
- }
- void assertAllCounts(int expected) {
- assertEquals(expected, guiceInjected);
- assertEquals(expected, userInjected);
- assertEquals(expected, listenersNotified);
- }
- }
- }