Added: hadoop/common/trunk/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/service/TestServiceLifecycle.java URL: http://svn.apache.org/viewvc/hadoop/common/trunk/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/service/TestServiceLifecycle.java?rev=1493650&view=auto ============================================================================== --- hadoop/common/trunk/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/service/TestServiceLifecycle.java (added) +++ hadoop/common/trunk/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/service/TestServiceLifecycle.java Mon Jun 17 06:39:33 2013 @@ -0,0 +1,468 @@ +/** + * 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.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +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.apache.hadoop.service.ServiceStateException; +import org.junit.Test; + +public class TestServiceLifecycle extends ServiceAssert { + private static Log LOG = LogFactory.getLog(TestServiceLifecycle.class); + + /** + * Walk the {@link BreakableService} through it's lifecycle, + * more to verify that service's counters work than anything else + * @throws Throwable if necessary + */ + @Test + public void testWalkthrough() throws Throwable { + + BreakableService svc = new BreakableService(); + assertServiceStateCreated(svc); + assertStateCount(svc, Service.STATE.NOTINITED, 1); + assertStateCount(svc, Service.STATE.INITED, 0); + assertStateCount(svc, Service.STATE.STARTED, 0); + assertStateCount(svc, Service.STATE.STOPPED, 0); + svc.init(new Configuration()); + assertServiceStateInited(svc); + assertStateCount(svc, Service.STATE.INITED, 1); + svc.start(); + assertServiceStateStarted(svc); + assertStateCount(svc, Service.STATE.STARTED, 1); + svc.stop(); + assertServiceStateStopped(svc); + assertStateCount(svc, Service.STATE.STOPPED, 1); + } + + /** + * call init twice + * @throws Throwable if necessary + */ + @Test + public void testInitTwice() throws Throwable { + BreakableService svc = new BreakableService(); + Configuration conf = new Configuration(); + conf.set("test.init","t"); + svc.init(conf); + svc.init(new Configuration()); + assertStateCount(svc, Service.STATE.INITED, 1); + assertServiceConfigurationContains(svc, "test.init"); + } + + /** + * Call start twice + * @throws Throwable if necessary + */ + @Test + public void testStartTwice() throws Throwable { + BreakableService svc = new BreakableService(); + svc.init(new Configuration()); + svc.start(); + svc.start(); + assertStateCount(svc, Service.STATE.STARTED, 1); + } + + + /** + * Verify that when a service is stopped more than once, no exception + * is thrown. + * @throws Throwable if necessary + */ + @Test + public void testStopTwice() throws Throwable { + BreakableService svc = new BreakableService(); + svc.init(new Configuration()); + svc.start(); + svc.stop(); + assertStateCount(svc, Service.STATE.STOPPED, 1); + svc.stop(); + assertStateCount(svc, Service.STATE.STOPPED, 1); + } + + + /** + * Show that if the service failed during an init + * operation, it stays in the created state, even after stopping it + * @throws Throwable if necessary + */ + + @Test + public void testStopFailedInit() throws Throwable { + BreakableService svc = new BreakableService(true, false, false); + assertServiceStateCreated(svc); + try { + svc.init(new Configuration()); + fail("Expected a failure, got " + svc); + } catch (BreakableService.BrokenLifecycleEvent e) { + //expected + } + //the service state wasn't passed + assertServiceStateStopped(svc); + assertStateCount(svc, Service.STATE.INITED, 1); + assertStateCount(svc, Service.STATE.STOPPED, 1); + //now try to stop + svc.stop(); + assertStateCount(svc, Service.STATE.STOPPED, 1); + } + + + /** + * Show that if the service failed during an init + * operation, it stays in the created state, even after stopping it + * @throws Throwable if necessary + */ + + @Test + public void testStopFailedStart() throws Throwable { + BreakableService svc = new BreakableService(false, true, false); + svc.init(new Configuration()); + assertServiceStateInited(svc); + try { + svc.start(); + fail("Expected a failure, got " + svc); + } catch (BreakableService.BrokenLifecycleEvent e) { + //expected + } + //the service state wasn't passed + assertServiceStateStopped(svc); + } + + /** + * verify that when a service fails during its stop operation, + * its state does not change. + * @throws Throwable if necessary + */ + @Test + public void testFailingStop() throws Throwable { + BreakableService svc = new BreakableService(false, false, true); + svc.init(new Configuration()); + svc.start(); + try { + svc.stop(); + fail("Expected a failure, got " + svc); + } catch (BreakableService.BrokenLifecycleEvent e) { + //expected + } + assertStateCount(svc, Service.STATE.STOPPED, 1); + } + + /** + * verify that when a service that is not started is stopped, the + * service enters the stopped state + * @throws Throwable on a failure + */ + @Test + public void testStopUnstarted() throws Throwable { + BreakableService svc = new BreakableService(); + svc.stop(); + assertServiceStateStopped(svc); + assertStateCount(svc, Service.STATE.INITED, 0); + assertStateCount(svc, Service.STATE.STOPPED, 1); + } + + /** + * Show that if the service failed during an init + * operation, stop was called. + */ + + @Test + public void testStopFailingInitAndStop() throws Throwable { + BreakableService svc = new BreakableService(true, false, true); + svc.registerServiceListener(new LoggingStateChangeListener()); + try { + svc.init(new Configuration()); + fail("Expected a failure, got " + svc); + } catch (BreakableService.BrokenLifecycleEvent e) { + assertEquals(Service.STATE.INITED, e.state); + } + //the service state is stopped + assertServiceStateStopped(svc); + assertEquals(Service.STATE.INITED, svc.getFailureState()); + + Throwable failureCause = svc.getFailureCause(); + assertNotNull("Null failure cause in " + svc, failureCause); + BreakableService.BrokenLifecycleEvent cause = + (BreakableService.BrokenLifecycleEvent) failureCause; + assertNotNull("null state in " + cause + " raised by " + svc, cause.state); + assertEquals(Service.STATE.INITED, cause.state); + } + + @Test + public void testInitNullConf() throws Throwable { + BreakableService svc = new BreakableService(false, false, false); + try { + svc.init(null); + LOG.warn("Null Configurations are permitted "); + } catch (ServiceStateException e) { + //expected + } + } + + @Test + public void testServiceNotifications() throws Throwable { + BreakableService svc = new BreakableService(false, false, false); + BreakableStateChangeListener listener = new BreakableStateChangeListener(); + svc.registerServiceListener(listener); + svc.init(new Configuration()); + assertEventCount(listener, 1); + svc.start(); + assertEventCount(listener, 2); + svc.stop(); + assertEventCount(listener, 3); + svc.stop(); + assertEventCount(listener, 3); + } + + /** + * Test that when a service listener is unregistered, it stops being invoked + * @throws Throwable on a failure + */ + @Test + public void testServiceNotificationsStopOnceUnregistered() throws Throwable { + BreakableService svc = new BreakableService(false, false, false); + BreakableStateChangeListener listener = new BreakableStateChangeListener(); + svc.registerServiceListener(listener); + svc.init(new Configuration()); + assertEventCount(listener, 1); + svc.unregisterServiceListener(listener); + svc.start(); + assertEventCount(listener, 1); + svc.stop(); + assertEventCount(listener, 1); + svc.stop(); + } + + /** + * This test uses a service listener that unregisters itself during the callbacks. + * This a test that verifies the concurrency logic on the listener management + * code, that it doesn't throw any immutable state change exceptions + * if you change list membership during the notifications. + * The standard <code>AbstractService</code> implementation copies the list + * to an array in a <code>synchronized</code> block then iterates through + * the copy precisely to prevent this problem. + * @throws Throwable on a failure + */ + @Test + public void testServiceNotificationsUnregisterDuringCallback() throws Throwable { + BreakableService svc = new BreakableService(false, false, false); + BreakableStateChangeListener listener = + new SelfUnregisteringBreakableStateChangeListener(); + BreakableStateChangeListener l2 = + new BreakableStateChangeListener(); + svc.registerServiceListener(listener); + svc.registerServiceListener(l2); + svc.init(new Configuration()); + assertEventCount(listener, 1); + assertEventCount(l2, 1); + svc.unregisterServiceListener(listener); + svc.start(); + assertEventCount(listener, 1); + assertEventCount(l2, 2); + svc.stop(); + assertEventCount(listener, 1); + svc.stop(); + } + + private static class SelfUnregisteringBreakableStateChangeListener + extends BreakableStateChangeListener { + + @Override + public synchronized void stateChanged(Service service) { + super.stateChanged(service); + service.unregisterServiceListener(this); + } + } + + private void assertEventCount(BreakableStateChangeListener listener, + int expected) { + assertEquals(listener.toString(), expected, listener.getEventCount()); + } + + @Test + public void testServiceFailingNotifications() throws Throwable { + BreakableService svc = new BreakableService(false, false, false); + BreakableStateChangeListener listener = new BreakableStateChangeListener(); + listener.setFailingState(Service.STATE.STARTED); + svc.registerServiceListener(listener); + svc.init(new Configuration()); + assertEventCount(listener, 1); + //start this; the listener failed but this won't show + svc.start(); + //counter went up + assertEventCount(listener, 2); + assertEquals(1, listener.getFailureCount()); + //stop the service -this doesn't fail + svc.stop(); + assertEventCount(listener, 3); + assertEquals(1, listener.getFailureCount()); + svc.stop(); + } + + /** + * This test verifies that you can block waiting for something to happen + * and use notifications to manage it + * @throws Throwable on a failure + */ + @Test + public void testListenerWithNotifications() throws Throwable { + //this tests that a listener can get notified when a service is stopped + AsyncSelfTerminatingService service = new AsyncSelfTerminatingService(2000); + NotifyingListener listener = new NotifyingListener(); + service.registerServiceListener(listener); + service.init(new Configuration()); + service.start(); + assertServiceInState(service, Service.STATE.STARTED); + long start = System.currentTimeMillis(); + synchronized (listener) { + listener.wait(20000); + } + long duration = System.currentTimeMillis() - start; + assertEquals(Service.STATE.STOPPED, listener.notifyingState); + assertServiceInState(service, Service.STATE.STOPPED); + assertTrue("Duration of " + duration + " too long", duration < 10000); + } + + @Test + public void testSelfTerminatingService() throws Throwable { + SelfTerminatingService service = new SelfTerminatingService(); + BreakableStateChangeListener listener = new BreakableStateChangeListener(); + service.registerServiceListener(listener); + service.init(new Configuration()); + assertEventCount(listener, 1); + //start the service + service.start(); + //and expect an event count of exactly two + assertEventCount(listener, 2); + } + + @Test + public void testStartInInitService() throws Throwable { + Service service = new StartInInitService(); + BreakableStateChangeListener listener = new BreakableStateChangeListener(); + service.registerServiceListener(listener); + service.init(new Configuration()); + assertServiceInState(service, Service.STATE.STARTED); + assertEventCount(listener, 1); + } + + @Test + public void testStopInInitService() throws Throwable { + Service service = new StopInInitService(); + BreakableStateChangeListener listener = new BreakableStateChangeListener(); + service.registerServiceListener(listener); + service.init(new Configuration()); + assertServiceInState(service, Service.STATE.STOPPED); + assertEventCount(listener, 1); + } + + /** + * Listener that wakes up all threads waiting on it + */ + private static class NotifyingListener implements ServiceStateChangeListener { + public Service.STATE notifyingState = Service.STATE.NOTINITED; + + public synchronized void stateChanged(Service service) { + notifyingState = service.getServiceState(); + this.notifyAll(); + } + } + + /** + * Service that terminates itself after starting and sleeping for a while + */ + private static class AsyncSelfTerminatingService extends AbstractService + implements Runnable { + final int timeout; + private AsyncSelfTerminatingService(int timeout) { + super("AsyncSelfTerminatingService"); + this.timeout = timeout; + } + + @Override + protected void serviceStart() throws Exception { + new Thread(this).start(); + super.serviceStart(); + } + + @Override + public void run() { + try { + Thread.sleep(timeout); + } catch (InterruptedException ignored) { + + } + this.stop(); + } + } + + /** + * Service that terminates itself in startup + */ + private static class SelfTerminatingService extends AbstractService { + private SelfTerminatingService() { + super("SelfTerminatingService"); + } + + @Override + protected void serviceStart() throws Exception { + //start + super.serviceStart(); + //then stop + stop(); + } + } + + /** + * Service that starts itself in init + */ + private static class StartInInitService extends AbstractService { + private StartInInitService() { + super("StartInInitService"); + } + + @Override + protected void serviceInit(Configuration conf) throws Exception { + super.serviceInit(conf); + start(); + } + } + + /** + * Service that starts itself in init + */ + private static class StopInInitService extends AbstractService { + private StopInInitService() { + super("StopInInitService"); + } + + @Override + protected void serviceInit(Configuration conf) throws Exception { + super.serviceInit(conf); + stop(); + } + } + +}
