package com.netflix.governator.lifecycle; import static org.junit.Assert.assertNotNull; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import javax.annotation.Nullable; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import javax.inject.Inject; import javax.inject.Named; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Mockito; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.inject.AbstractModule; import com.google.inject.Injector; import com.google.inject.Key; import com.google.inject.Provides; import com.google.inject.Singleton; import com.google.inject.name.Names; import com.google.inject.util.Providers; import com.netflix.governator.LifecycleManager; import com.netflix.governator.guice.LifecycleInjector; import com.netflix.governator.guice.LifecycleInjectorBuilder; import com.netflix.governator.guice.LifecycleInjectorMode; import com.netflix.governator.spi.LifecycleListener; import com.tngtech.java.junit.dataprovider.DataProvider; import com.tngtech.java.junit.dataprovider.DataProviderRunner; import com.tngtech.java.junit.dataprovider.UseDataProvider; @RunWith(DataProviderRunner.class) public class LifeCycleFeaturesOnLegacyBuilderTest { static class LifecycleSubject { private Logger logger = LoggerFactory.getLogger(LifecycleSubject.class); private String name; private volatile boolean postConstructed = false; private volatile boolean preDestroyed = false; private static AtomicInteger instanceCounter = new AtomicInteger(0); public LifecycleSubject(String name) { this.name = name; instanceCounter.incrementAndGet(); logger.info("created instance " + this); } @PostConstruct public void init() { logger.info("@PostConstruct called " + this); this.postConstructed = true; } @PreDestroy public void destroy() { logger.info("@PreDestroy called " + this); this.preDestroyed = true; } public boolean isPostConstructed() { return postConstructed; } public boolean isPreDestroyed() { return preDestroyed; } public String getName() { return name; } public static int getInstanceCount() { return instanceCounter.get(); } public String toString() { return "LifecycleSubject@" + System.identityHashCode(this) + '[' + name + ']'; } } static class UsesNullable { private AtomicBoolean preDestroyed = new AtomicBoolean(false); private LifecycleSubject subject; @Inject public void UsesNullable(@Nullable @Named("missing") LifecycleSubject subject) { this.subject = subject; } @PreDestroy public void destroy() { this.preDestroyed.set(true); } } static interface LifecycleInterface { @PostConstruct public default void init() { System.out.println("init() called"); } @PreDestroy public default void destroy() { System.out.println("destroy() called"); } } @Mock private TestListener listener; private Injector injector; private LocalScope localScope; private static class TestListener implements LifecycleListener { @PostConstruct public void init() { } @PreDestroy public void shutdown() { } public void onStopped(Throwable error) { } public void onStarted() { } } public com.netflix.governator.lifecycle.LifecycleManager init(LifecycleInjectorBuilder builder) throws Exception { LifecycleSubject.instanceCounter.set(0); localScope = new LocalScope(); listener = Mockito.mock(TestListener.class); LifecycleInjector lifecycleInjector = builder .withAdditionalModules(new AbstractModule() { @Override protected void configure() { bind(TestListener.class).toInstance(listener); bindScope(LocalScoped.class, localScope); bind(UsesNullable.class); bind(Key.get(LifecycleSubject.class, Names.named("missing"))).toProvider(Providers.of((LifecycleSubject)null)); } @Provides @LocalScoped @Named("thing1") public LifecycleSubject thing1() { return new LifecycleSubject("thing1"); } @Provides @Singleton @Named("thing2") public LifecycleSubject thing2() { return new LifecycleSubject("thing2"); } }) .requiringExplicitBindings() .build(); injector = lifecycleInjector.createInjector(); com.netflix.governator.lifecycle.LifecycleManager lifecycleManager = lifecycleInjector.getLifecycleManager(); lifecycleManager.start(); return lifecycleManager; } @UseDataProvider("builders") @Test public void testPostActionsAndLifecycleListenersInvoked(LifecycleInjectorBuilder builder) throws Exception { try (com.netflix.governator.lifecycle.LifecycleManager lm = init(builder)) { assertNotNull(injector); assertNotNull(injector.getInstance(LifecycleManager.class)); Mockito.verify(listener, Mockito.times(1)).onStarted(); Mockito.verify(listener, Mockito.times(1)).init(); Mockito.verify(listener, Mockito.times(0)).onStopped(Mockito.any(Throwable.class)); Mockito.verify(listener, Mockito.times(0)).shutdown(); } Mockito.verify(listener, Mockito.times(1)).onStopped(Mockito.any(Throwable.class)); Mockito.verify(listener, Mockito.times(1)).onStopped(Mockito.any(Throwable.class)); } @UseDataProvider("builders") @Test public void testScopeManagement(LifecycleInjectorBuilder builder) throws Exception { LifecycleSubject thing2 = null; try (com.netflix.governator.lifecycle.LifecycleManager lm = init(builder)) { thing2 = injector.getInstance(Key.get(LifecycleSubject.class, Names.named("thing2"))); localScope.enter(); injector.getInstance(Key.get(LifecycleSubject.class, Names.named("thing1"))); LifecycleSubject thing1 = injector.getInstance(Key.get(LifecycleSubject.class, Names.named("thing1"))); Assert.assertTrue(thing1.isPostConstructed()); Assert.assertFalse(thing1.isPreDestroyed()); Assert.assertTrue(thing2.isPostConstructed()); Assert.assertFalse(thing2.isPreDestroyed()); Assert.assertEquals(2, LifecycleSubject.getInstanceCount()); // thing1 and // thing2 Assert.assertFalse(thing2.isPreDestroyed()); localScope.exit(); System.gc(); Thread.sleep(500); Assert.assertTrue(thing1.isPreDestroyed()); } System.gc(); Thread.sleep(500); Assert.assertTrue(thing2.isPreDestroyed()); } @UseDataProvider("builders") @Test public void testSingletonScopeManagement(LifecycleInjectorBuilder builder) throws Exception { LifecycleSubject thing2 = null; try (com.netflix.governator.lifecycle.LifecycleManager lm = init(builder)) { thing2 = injector.getInstance(Key.get(LifecycleSubject.class, Names.named("thing2"))); Assert.assertTrue(thing2.isPostConstructed()); Assert.assertFalse(thing2.isPreDestroyed()); injector.getInstance(Key.get(LifecycleSubject.class, Names.named("thing2"))); injector.getInstance(Key.get(LifecycleSubject.class, Names.named("thing2"))); injector.getInstance(Key.get(LifecycleSubject.class, Names.named("thing2"))); Assert.assertEquals(1, LifecycleSubject.getInstanceCount()); } System.gc(); Thread.sleep(500); Assert.assertTrue(thing2.isPreDestroyed()); } @UseDataProvider("builders") @Test public void testNullableInjection(LifecycleInjectorBuilder builder) throws Exception { AtomicBoolean nullableConsumerDestroyed = null; try (com.netflix.governator.lifecycle.LifecycleManager lm = init(builder)) { UsesNullable nullableConsumer = injector.getInstance(UsesNullable.class); nullableConsumerDestroyed = nullableConsumer.preDestroyed; Assert.assertNull(nullableConsumer.subject); } System.gc(); Thread.sleep(500); Assert.assertTrue(nullableConsumerDestroyed.get()); } @Test public void testLifecycleMethods() throws Exception { LifecycleInterface instance = new LifecycleInterface() {}; Class<?> defaultMethodsClass = instance.getClass(); LifecycleMethods methods = new LifecycleMethods(defaultMethodsClass); methods.methodInvoke(defaultMethodsClass.getMethod("init", (Class[])null), instance); methods.methodInvoke(defaultMethodsClass.getMethod("destroy", (Class[])null), instance); } @DataProvider public static Object[][] builders() { return new Object[][] { new Object[] { "simulatedChildInjector", LifecycleInjector.builder().withMode(LifecycleInjectorMode.SIMULATED_CHILD_INJECTORS) }, new Object[] { "childInjector", LifecycleInjector.builder() }, new Object[] { "simulatedChildInjectorExplicitBindings", LifecycleInjector.builder().withMode(LifecycleInjectorMode.SIMULATED_CHILD_INJECTORS).requiringExplicitBindings() }, new Object[] { "childInjectorExplicitBindings", LifecycleInjector.builder().requiringExplicitBindings() } }; } }