alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Java example source code file (TypeListenerTest.java)

This example Java source code file (TypeListenerTest.java) is included in the alvinalexander.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Learn more about this Java project at its project page.

Java - Java tags/keywords

abstractmodule, atomicinteger, atomicreference, error, illegalstateexception, injectionlistener, injector, membersinjector, override, reason, string, typeencounter, typelistener, typeliteral, util

The TypeListenerTest.java Java example source code

/**
 * 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() {})
      .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 type, TypeEncounter encounter) {
      throw new ClassCastException("whoops, failure #" + (++failures));
    }

    @Override public String toString() {
      return "clumsy";
    }
  };

  final InjectionListener<Object> failingInjectionListener = new InjectionListener() {
    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() {
    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 type, TypeEncounter 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() {
      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 type, TypeEncounter 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 type, TypeEncounter 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 type, TypeEncounter 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() {
      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 type, TypeEncounter 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 bProviderReference = new AtomicReference>();
    final AtomicReference<MembersInjector aMembersInjectorReference
        = new AtomicReference<MembersInjector();

    final InjectionListener<Object> lookupsTester = new InjectionListener() {
      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 type, TypeEncounter 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 type, TypeEncounter 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() {
      public void injectMembers(D instance) {
        instance.userInjected++;
        assertEquals(instance.guiceInjected, instance.userInjected);
      }
    };

    final InjectionListener<D> injectionListener = new InjectionListener() {
      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 type, TypeEncounter 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 type, TypeEncounter 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 type, TypeEncounter encounter) {
            notificationCount.incrementAndGet();
          }
        });

        bind(C.class).toInstance(new C());
      }
    });

    assertEquals(1, notificationCount.get());
  }

  public void testEncounterCannotBeUsedAfterHearReturns() {
    final AtomicReference<TypeEncounter encounterReference = new AtomicReference>();

    Guice.createInjector(new AbstractModule() {
      @Override protected void configure() {
        bindListener(any(), new TypeListener() {
          public <I> void hear(TypeLiteral type, TypeEncounter 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 type, TypeEncounter 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 type, TypeEncounter 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);
    }
  }
}

Other Java examples (source code examples)

Here is a short list of links related to this Java TypeListenerTest.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.