/* * Copyright Terracotta, 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 org.ehcache.core; import org.ehcache.Status; import org.ehcache.core.events.StateChangeListener; import org.ehcache.StateTransitionException; import org.ehcache.core.spi.LifeCycled; import org.ehcache.core.spi.LifeCycledAdapter; import org.hamcrest.CoreMatchers; import org.junit.Test; import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import static java.util.Arrays.asList; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; public class StatusTransitionerTest { @Test public void testTransitionsToLowestStateOnFailure() { StatusTransitioner transitioner = new StatusTransitioner(LoggerFactory.getLogger(StatusTransitionerTest.class)); assertThat(transitioner.currentStatus(), CoreMatchers.is(Status.UNINITIALIZED)); transitioner.init().failed(new Throwable()); assertThat(transitioner.currentStatus(), is(Status.UNINITIALIZED)); transitioner.init().succeeded(); assertThat(transitioner.currentStatus(), is(Status.AVAILABLE)); transitioner.close().failed(new Throwable()); assertThat(transitioner.currentStatus(), is(Status.UNINITIALIZED)); } @Test public void testFiresListeners() { StatusTransitioner transitioner = new StatusTransitioner(LoggerFactory.getLogger(StatusTransitionerTest.class)); final StateChangeListener listener = mock(StateChangeListener.class); transitioner.registerListener(listener); transitioner.init().succeeded(); verify(listener).stateTransition(Status.UNINITIALIZED, Status.AVAILABLE); reset(listener); transitioner.deregisterListener(listener); transitioner.close().succeeded(); verify(listener, never()).stateTransition(Status.AVAILABLE, Status.UNINITIALIZED); } @Test public void testFinishesTransitionOnListenerThrowing() { StatusTransitioner transitioner = new StatusTransitioner(LoggerFactory.getLogger(StatusTransitionerTest.class)); final StateChangeListener listener = mock(StateChangeListener.class); final RuntimeException runtimeException = new RuntimeException(); doThrow(runtimeException).when(listener).stateTransition(Status.UNINITIALIZED, Status.AVAILABLE); transitioner.registerListener(listener); try { transitioner.init().succeeded(); fail(); } catch (RuntimeException e) { assertThat(e, is(runtimeException)); } assertThat(transitioner.currentStatus(), is(Status.AVAILABLE)); } @Test public void testMaintenanceOnlyLetsTheOwningThreadInteract() throws InterruptedException { final StatusTransitioner transitioner = new StatusTransitioner(LoggerFactory.getLogger(StatusTransitionerTest.class)); transitioner.maintenance().succeeded(); transitioner.checkMaintenance(); Thread thread = new Thread() { @Override public void run() { try { transitioner.checkMaintenance(); fail(); } catch (IllegalStateException e) { assertThat(e.getMessage().contains(Status.MAINTENANCE.name()), is(true)); assertThat(e.getMessage().contains("own"), is(true)); } } }; thread.start(); thread.join(); } @Test public void testMaintenanceOnlyOwningThreadCanClose() throws InterruptedException { final StatusTransitioner transitioner = new StatusTransitioner(LoggerFactory.getLogger(StatusTransitionerTest.class)); transitioner.maintenance().succeeded(); Thread thread = new Thread() { @Override public void run() { try { transitioner.close(); fail(); } catch (IllegalStateException e) { assertThat(e.getMessage().contains(Status.MAINTENANCE.name()), is(true)); assertThat(e.getMessage().contains("own"), is(true)); } } }; thread.start(); thread.join(); transitioner.close(); } @Test public void testCheckAvailableAccountsForThreadLease() throws InterruptedException { final StatusTransitioner transitioner = new StatusTransitioner(LoggerFactory.getLogger(StatusTransitionerTest.class)); transitioner.maintenance().succeeded(); transitioner.checkAvailable(); Thread thread = new Thread() { @Override public void run() { try { transitioner.checkAvailable(); fail(); } catch (IllegalStateException e) { assertThat(e.getMessage().contains(Status.MAINTENANCE.name()), is(true)); assertThat(e.getMessage().contains("own"), is(true)); } } }; thread.start(); thread.join(); transitioner.close(); } @Test public void testHookThrowingVetosTransition() throws Exception { final StatusTransitioner transitioner = new StatusTransitioner(LoggerFactory.getLogger(StatusTransitionerTest.class)); final LifeCycled mock = mock(LifeCycled.class); transitioner.addHook(mock); final Exception toBeThrown = new Exception(); doThrow(toBeThrown).when(mock).init(); try { transitioner.init().succeeded(); fail(); } catch (StateTransitionException e) { assertThat(e.getCause(), CoreMatchers.<Throwable>sameInstance(toBeThrown)); } assertThat(transitioner.currentStatus(), is(Status.UNINITIALIZED)); reset(mock); doThrow(toBeThrown).when(mock).close(); transitioner.init().succeeded(); try { transitioner.close().succeeded(); fail(); } catch (StateTransitionException e) { assertThat(e.getCause(), CoreMatchers.<Throwable>sameInstance(toBeThrown)); } } @Test public void testRepectRegistrationOrder() { final List<LifeCycled> order = new ArrayList<LifeCycled>(); final StatusTransitioner transitioner = new StatusTransitioner(LoggerFactory.getLogger(StatusTransitionerTest.class)); final Recorder first = new Recorder(order, "first"); final Recorder second = new Recorder(order, "second"); transitioner.addHook(first); transitioner.addHook(second); transitioner.init().succeeded(); assertThat(order.get(0), CoreMatchers.<LifeCycled>sameInstance(first)); assertThat(order.get(1), CoreMatchers.<LifeCycled>sameInstance(second)); order.clear(); transitioner.close().succeeded(); assertThat(order.get(0), CoreMatchers.<LifeCycled>sameInstance(second)); assertThat(order.get(1), CoreMatchers.<LifeCycled>sameInstance(first)); } @Test public void testStopsInitedHooksOnFailure() throws Exception { final StatusTransitioner transitioner = new StatusTransitioner(LoggerFactory.getLogger(StatusTransitionerTest.class)); final LifeCycled first = mock(LifeCycled.class); final LifeCycled second = mock(LifeCycled.class); transitioner.addHook(first); transitioner.addHook(second); final Exception toBeThrown = new Exception(); doThrow(toBeThrown).when(second).init(); try { transitioner.init().succeeded(); fail(); } catch (StateTransitionException e) { // expected } verify(first).init(); verify(first).close(); } @Test public void testDoesNoReInitsClosedHooksOnFailure() throws Exception { final StatusTransitioner transitioner = new StatusTransitioner(LoggerFactory.getLogger(StatusTransitionerTest.class)); final LifeCycled first = mock(LifeCycled.class); final LifeCycled second = mock(LifeCycled.class); transitioner.addHook(first); transitioner.addHook(second); transitioner.init().succeeded(); reset(first); reset(second); final Exception toBeThrown = new Exception(); doThrow(toBeThrown).when(first).close(); try { transitioner.close().succeeded(); fail(); } catch (StateTransitionException e) { // expected } verify(second).close(); verify(second, never()).init(); } @Test public void testClosesAllHooksOnFailure() throws Exception { final StatusTransitioner transitioner = new StatusTransitioner(LoggerFactory.getLogger(StatusTransitionerTest.class)); final LifeCycled first = mock(LifeCycled.class); final LifeCycled second = mock(LifeCycled.class); transitioner.addHook(first); transitioner.addHook(second); transitioner.init().succeeded(); reset(first); reset(second); final Exception toBeThrown = new Exception(); doThrow(toBeThrown).when(second).close(); try { transitioner.close().succeeded(); fail(); } catch (StateTransitionException e) { // expected } verify(first).close(); } @Test public void testLifeCycledAdapterCanBeUsedInsteadOfLifeCycled() { StatusTransitioner transitioner = new StatusTransitioner(LoggerFactory.getLogger(StatusTransitionerTest.class)); final List<String> calls = new LinkedList<String>(); LifeCycledAdapter adapter1 = new LifeCycledAdapter() { }; LifeCycledAdapter adapter2 = new LifeCycledAdapter() { @Override public void init() throws Exception { calls.add("adapter2-init"); } }; LifeCycledAdapter adapter3 = new LifeCycledAdapter() { @Override public void close() throws Exception { calls.add("adapter3-close"); } }; LifeCycledAdapter adapter4 = new LifeCycledAdapter() { @Override public void init() throws Exception { calls.add("adapter4-init"); } @Override public void close() throws Exception { calls.add("adapter4-close"); } }; transitioner.addHook(adapter1); transitioner.addHook(adapter2); transitioner.addHook(adapter3); transitioner.addHook(adapter4); transitioner.init().succeeded(); assertThat(calls, equalTo(asList("adapter2-init", "adapter4-init"))); transitioner.close().succeeded(); assertThat(calls, equalTo(asList("adapter2-init", "adapter4-init", "adapter4-close", "adapter3-close"))); } @Test public void testTransitionDuringFailures() { StatusTransitioner transitioner = new StatusTransitioner(LoggerFactory.getLogger(StatusTransitionerTest.class)); assertThat(transitioner.currentStatus(), CoreMatchers.is(Status.UNINITIALIZED)); StatusTransitioner.Transition st = transitioner.init(); st.failed(new Throwable()); assertThat(transitioner.currentStatus(), is(Status.UNINITIALIZED)); try { st.failed(new Throwable()); fail(); } catch (AssertionError err) { assertThat(err.getMessage(), is("Throwable cannot be thrown if Transition is done.")); } st.failed(null); assertThat(transitioner.currentStatus(), is(Status.UNINITIALIZED)); StatusTransitioner.Transition st1 = transitioner.init(); assertThat(transitioner.currentStatus(), is(Status.AVAILABLE)); st1.failed(null); assertThat(transitioner.currentStatus(), is(Status.UNINITIALIZED)); } private static class Recorder implements LifeCycled { private final List<LifeCycled> order; private final String name; public Recorder(final List<LifeCycled> order, final String name) { this.order = order; this.name = name; } @Override public void init() throws Exception { order.add(this); } @Override public void close() throws Exception { order.add(this); } @Override public String toString() { return "Recorder{" + name + '}'; } } }