package com.netflix.governator; import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import javax.inject.Inject; import javax.inject.Provider; import org.junit.Assert; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestName; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.inject.AbstractModule; import com.google.inject.CreationException; import com.google.inject.Injector; import com.google.inject.Scopes; import com.netflix.governator.spi.LifecycleListener; public class LifecycleModuleTest { private static final Logger logger = LoggerFactory.getLogger(LifecycleModuleTest.class); static class TestRuntimeException extends RuntimeException { private static final long serialVersionUID = 1L; public TestRuntimeException() { super(); } public TestRuntimeException(String message) { super(message); } } private static enum Events { Injected, Initialized, Destroyed, Started, Stopped, Error } @Rule public final TestName name = new TestName(); static class TrackingLifecycleListener implements LifecycleListener { final List<Events> events = new ArrayList<>(); private String name; public TrackingLifecycleListener(String name) { this.name = name; } @Inject public void injected(Injector injector) { events.add(Events.Injected); } @PostConstruct public void initialized() { events.add(Events.Initialized); } @Override public void onStarted() { events.add(Events.Started); } @Override public void onStopped(Throwable t) { events.add(Events.Stopped); if (t != null) { events.add(Events.Error); } } @PreDestroy public void destroyed() { events.add(Events.Destroyed); } @Override public String toString() { return "TrackingLifecycleListener@" + name; } } @Test public void confirmLifecycleListenerEventsForSuccessfulStart() { final TrackingLifecycleListener listener = new TrackingLifecycleListener(name.getMethodName()); TestSupport.inject(listener).close(); assertThat(listener.events, equalTo( Arrays.asList(Events.Injected, Events.Initialized, Events.Started, Events.Stopped, Events.Destroyed))); } @Test public void confirmLifecycleListenerEventsForRTExceptionInjected() { final TrackingLifecycleListener listener = new TrackingLifecycleListener(name.getMethodName()) { @Override @Inject public void injected(Injector injector) { super.injected(injector); throw new TestRuntimeException("injected failed"); } }; try (LifecycleInjector injector = TestSupport.inject(listener)) { fail("expected exception injecting instance"); } catch (CreationException e) { // expected } catch (Exception e) { fail("expected CreationException injecting instance but got " + e); } finally { assertThat(listener.events, equalTo(Arrays.asList(Events.Injected))); } } @Test public void confirmLifecycleListenerEventsForRTExceptionPostConstruct() { final TrackingLifecycleListener listener = new TrackingLifecycleListener(name.getMethodName()) { @Override @PostConstruct public void initialized() { super.initialized(); throw new TestRuntimeException("postconstruct rt exception"); } }; try (LifecycleInjector injector = TestSupport.inject(listener)) { fail("expected rt exception starting injector"); } catch (CreationException e) { // expected } catch (Exception e) { fail("expected CreationException starting injector but got " + e); } finally { assertThat(listener.events, equalTo(Arrays.asList(Events.Injected, Events.Initialized, Events.Stopped, Events.Error))); } } @Test public void confirmLifecycleListenerEventsForRTExceptionOnStarted() { final TrackingLifecycleListener listener = new TrackingLifecycleListener(name.getMethodName()) { @Override public void onStarted() { super.onStarted(); throw new TestRuntimeException("onStarted rt exception"); } }; try (LifecycleInjector injector = TestSupport.inject(listener)) { fail("expected rt exception starting injector"); } catch (TestRuntimeException e) { // expected } catch (Exception e) { fail("expected TestRuntimeException starting injector but got " + e); } finally { assertThat(listener.events, equalTo(Arrays.asList(Events.Injected, Events.Initialized, Events.Started, Events.Stopped, Events.Error, Events.Destroyed))); } } @Test public void confirmLifecycleListenerEventsForRTExceptionOnStopped() { final TrackingLifecycleListener listener = new TrackingLifecycleListener(name.getMethodName()) { @Override public void onStopped(Throwable t) { super.onStopped(t); throw new TestRuntimeException("onStopped rt exception"); } }; try (LifecycleInjector injector = TestSupport.inject(listener)) { } finally { assertThat(listener.events, equalTo(Arrays.asList(Events.Injected, Events.Initialized, Events.Started, Events.Stopped, Events.Destroyed))); } } @Test public void confirmLifecycleListenerEventsForRTExceptionPreDestroy() { final TrackingLifecycleListener listener = new TrackingLifecycleListener(name.getMethodName()) { @PreDestroy @Override public void destroyed() { super.destroyed(); throw new TestRuntimeException("destroyed rt exception"); } }; try (LifecycleInjector injector = TestSupport.inject(listener)) { } finally { assertThat(listener.events, equalTo(Arrays.asList(Events.Injected, Events.Initialized, Events.Started, Events.Stopped, Events.Destroyed))); } } @Test(expected=AssertionError.class) public void assertionErrorInInject() { TrackingLifecycleListener listener = new TrackingLifecycleListener(name.getMethodName()) { @Inject @Override public void injected(Injector injector) { super.injected(injector); fail("injected exception"); } }; try (LifecycleInjector injector = TestSupport.inject(listener)) { fail("expected error provisioning injector"); } catch (Exception e) { fail("expected AssertionError provisioning injector but got " + e); } finally { assertThat(listener.events, equalTo( Arrays.asList(Events.Injected, Events.Initialized, Events.Stopped, Events.Error))); } } @Test(expected=AssertionError.class) public void assertionErrorInPostConstruct() { TrackingLifecycleListener listener = new TrackingLifecycleListener(name.getMethodName()) { @PostConstruct @Override public void initialized() { super.initialized(); fail("postconstruct exception"); } }; try (LifecycleInjector injector = TestSupport.inject(listener)) { fail("expected error creating injector"); } catch (Exception e) { fail("expected AssertionError destroying injector but got " + e); } finally { assertThat(listener.events, equalTo( Arrays.asList(Events.Injected, Events.Initialized, Events.Stopped, Events.Error))); } } @Test(expected=AssertionError.class) public void assertionErrorInOnStarted() { TrackingLifecycleListener listener = new TrackingLifecycleListener(name.getMethodName()) { @Override public void onStarted() { super.onStarted(); fail("onStarted exception"); } }; try (LifecycleInjector injector = TestSupport.inject(listener)) { fail("expected AssertionError starting injector"); } catch (Exception e) { fail("expected AssertionError starting injector but got " + e); } finally { assertThat(listener.events, equalTo( Arrays.asList(Events.Injected, Events.Initialized, Events.Started, Events.Stopped, Events.Error))); } } @Test(expected=AssertionError.class) public void assertionErrorInOnStopped() { TrackingLifecycleListener listener = new TrackingLifecycleListener(name.getMethodName()) { @Override public void onStopped(Throwable t) { super.onStopped(t); fail("onstopped exception"); } }; try (LifecycleInjector injector = TestSupport.inject(listener)) { fail("expected AssertionError stopping injector"); } catch (Exception e) { fail("expected AssertionError stopping injector but got " + e); } finally { assertThat(listener.events, equalTo( Arrays.asList(Events.Injected, Events.Initialized, Events.Stopped, Events.Error))); } } @Test(expected=AssertionError.class) public void assertionErrorInPreDestroy() { TrackingLifecycleListener listener = new TrackingLifecycleListener(name.getMethodName()) { @PreDestroy @Override public void destroyed() { super.destroyed(); fail("expected exception from predestroy"); } }; try { TestSupport.inject(listener).close(); } catch (Exception e) { e.printStackTrace(); fail("expected no exceptions for failed destroy method but got " + e); } finally { assertThat(listener.events, equalTo( Arrays.asList(Events.Injected, Events.Initialized, Events.Started, Events.Stopped, Events.Destroyed))); } } public static class Listener1 implements LifecycleListener { boolean wasStarted; boolean wasStopped; @Inject Provider<Listener2> nestedListener; @Override public void onStarted() { logger.info("starting listener1"); wasStarted = true; nestedListener.get(); } @Override public void onStopped(Throwable error) { logger.info("stopped listener1"); wasStopped = true; Assert.assertTrue(nestedListener.get().wasStopped); } } public static class Listener2 implements LifecycleListener { boolean wasStarted; boolean wasStopped; @Override public void onStarted() { logger.info("starting listener2"); wasStarted = true; } @Override public void onStopped(Throwable error) { logger.info("stopped listener2"); wasStopped = true; } } @Test public void testNestedLifecycleListeners() { Listener1 listener1; Listener2 listener2; try (LifecycleInjector injector = InjectorBuilder.fromModule(new AbstractModule() { @Override protected void configure() { bind(Listener1.class).asEagerSingleton(); bind(Listener2.class).in(Scopes.SINGLETON); } }).createInjector()) { listener1 = injector.getInstance(Listener1.class); listener2 = listener1.nestedListener.get(); Assert.assertNotNull(listener2); Assert.assertTrue(listener1.wasStarted); Assert.assertTrue(listener2.wasStarted); } Assert.assertTrue(listener1.wasStopped); Assert.assertTrue(listener2.wasStopped); } }