You are viewing a plain text version of this content. The canonical link for it is here.
Posted to common-commits@hadoop.apache.org by vi...@apache.org on 2013/06/17 08:39:35 UTC
svn commit: r1493650 [2/2] - in
/hadoop/common/trunk/hadoop-common-project/hadoop-common: ./ dev-support/
src/main/java/org/apache/hadoop/service/
src/test/java/org/apache/hadoop/service/
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();
+ }
+ }
+
+}