| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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.apache.hadoop.service; |
| |
| import org.apache.hadoop.conf.Configuration; |
| import org.apache.hadoop.service.AbstractService; |
| import org.apache.hadoop.service.LoggingStateChangeListener; |
| import org.apache.hadoop.service.Service; |
| import org.apache.hadoop.service.ServiceStateChangeListener; |
| import org.junit.After; |
| import org.junit.Test; |
| |
| /** |
| * Test global state changes. It is critical for all tests to clean up the |
| * global listener afterwards to avoid interfering with follow-on tests. |
| * |
| * One listener, {@link #listener} is defined which is automatically |
| * unregistered on cleanup. All other listeners must be unregistered in the |
| * finally clauses of the tests. |
| */ |
| public class TestGlobalStateChangeListener extends ServiceAssert { |
| |
| BreakableStateChangeListener listener = new BreakableStateChangeListener("listener"); |
| |
| |
| private void register() { |
| register(listener); |
| } |
| |
| private boolean unregister() { |
| return unregister(listener); |
| } |
| |
| private void register(ServiceStateChangeListener l) { |
| AbstractService.registerGlobalListener(l); |
| } |
| |
| private boolean unregister(ServiceStateChangeListener l) { |
| return AbstractService.unregisterGlobalListener(l); |
| } |
| |
| /** |
| * After every test case reset the list of global listeners. |
| */ |
| @After |
| public void cleanup() { |
| AbstractService.resetGlobalListeners(); |
| } |
| |
| /** |
| * Assert that the last state of the listener is that the test expected. |
| * @param breakable a breakable listener |
| * @param state the expected state |
| */ |
| public void assertListenerState(BreakableStateChangeListener breakable, |
| Service.STATE state) { |
| assertEquals("Wrong state in " + breakable, state, breakable.getLastState()); |
| } |
| |
| /** |
| * Assert that the number of state change notifications matches expectations. |
| * @param breakable the listener |
| * @param count the expected count. |
| */ |
| public void assertListenerEventCount(BreakableStateChangeListener breakable, |
| int count) { |
| assertEquals("Wrong event count in " + breakable, count, |
| breakable.getEventCount()); |
| } |
| |
| /** |
| * Test that register/unregister works |
| */ |
| @Test |
| public void testRegisterListener() { |
| register(); |
| assertTrue("listener not registered", unregister()); |
| } |
| |
| /** |
| * Test that double registration results in one registration only. |
| */ |
| @Test |
| public void testRegisterListenerTwice() { |
| register(); |
| register(); |
| assertTrue("listener not registered", unregister()); |
| //there should be no listener to unregister the second time |
| assertFalse("listener double registered", unregister()); |
| } |
| |
| /** |
| * Test that the {@link BreakableStateChangeListener} is picking up |
| * the state changes and that its last event field is as expected. |
| */ |
| @Test |
| public void testEventHistory() { |
| register(); |
| BreakableService service = new BreakableService(); |
| assertListenerState(listener, Service.STATE.NOTINITED); |
| assertEquals(0, listener.getEventCount()); |
| service.init(new Configuration()); |
| assertListenerState(listener, Service.STATE.INITED); |
| assertSame(service, listener.getLastService()); |
| assertListenerEventCount(listener, 1); |
| |
| service.start(); |
| assertListenerState(listener, Service.STATE.STARTED); |
| assertListenerEventCount(listener, 2); |
| |
| service.stop(); |
| assertListenerState(listener, Service.STATE.STOPPED); |
| assertListenerEventCount(listener, 3); |
| } |
| |
| /** |
| * This test triggers a failure in the listener - the expectation is that the |
| * service has already reached it's desired state, purely because the |
| * notifications take place afterwards. |
| * |
| */ |
| @Test |
| public void testListenerFailure() { |
| listener.setFailingState(Service.STATE.INITED); |
| register(); |
| BreakableStateChangeListener l2 = new BreakableStateChangeListener(); |
| register(l2); |
| BreakableService service = new BreakableService(); |
| service.init(new Configuration()); |
| //expected notifications to fail |
| |
| //still should record its invocation |
| assertListenerState(listener, Service.STATE.INITED); |
| assertListenerEventCount(listener, 1); |
| |
| //and second listener didn't get notified of anything |
| assertListenerEventCount(l2, 0); |
| |
| //service should still consider itself started |
| assertServiceStateInited(service); |
| service.start(); |
| service.stop(); |
| } |
| |
| /** |
| * Create a chain of listeners and set one in the middle to fail; verify that |
| * those in front got called, and those after did not. |
| */ |
| @Test |
| public void testListenerChain() { |
| |
| //create and register the listeners |
| LoggingStateChangeListener logListener = new LoggingStateChangeListener(); |
| register(logListener); |
| BreakableStateChangeListener l0 = new BreakableStateChangeListener("l0"); |
| register(l0); |
| listener.setFailingState(Service.STATE.STARTED); |
| register(); |
| BreakableStateChangeListener l3 = new BreakableStateChangeListener("l3"); |
| register(l3); |
| |
| //create and init a service. |
| BreakableService service = new BreakableService(); |
| service.init(new Configuration()); |
| assertServiceStateInited(service); |
| assertListenerState(l0, Service.STATE.INITED); |
| assertListenerState(listener, Service.STATE.INITED); |
| assertListenerState(l3, Service.STATE.INITED); |
| |
| service.start(); |
| //expect that listener l1 and the failing listener are in start, but |
| //not the final one |
| assertServiceStateStarted(service); |
| assertListenerState(l0, Service.STATE.STARTED); |
| assertListenerEventCount(l0, 2); |
| assertListenerState(listener, Service.STATE.STARTED); |
| assertListenerEventCount(listener, 2); |
| //this is the listener that is not expected to have been invoked |
| assertListenerState(l3, Service.STATE.INITED); |
| assertListenerEventCount(l3, 1); |
| |
| //stop the service |
| service.stop(); |
| //listeners are all updated |
| assertListenerEventCount(l0, 3); |
| assertListenerEventCount(listener, 3); |
| assertListenerEventCount(l3, 2); |
| //can all be unregistered in any order |
| unregister(logListener); |
| unregister(l0); |
| unregister(l3); |
| |
| //check that the listeners are all unregistered, even |
| //though they were registered in a different order. |
| //rather than do this by doing unregister checks, a new service is created |
| service = new BreakableService(); |
| //this service is initialized |
| service.init(new Configuration()); |
| //it is asserted that the event count has not changed for the unregistered |
| //listeners |
| assertListenerEventCount(l0, 3); |
| assertListenerEventCount(l3, 2); |
| //except for the one listener that was not unregistered, which |
| //has incremented by one |
| assertListenerEventCount(listener, 4); |
| } |
| |
| } |