You are viewing a plain text version of this content. The canonical link for it is here.
Posted to mapreduce-commits@hadoop.apache.org by st...@apache.org on 2012/03/15 11:45:00 UTC
svn commit: r1300899 - in
/hadoop/common/trunk/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-common/src:
main/java/org/apache/hadoop/yarn/service/ServiceOperations.java
test/java/org/apache/hadoop/yarn/service/TestServiceOperations.java
Author: stevel
Date: Thu Mar 15 10:45:00 2012
New Revision: 1300899
URL: http://svn.apache.org/viewvc?rev=1300899&view=rev
Log:
MAPREDUCE-3970 ServiceOperations class
Added:
hadoop/common/trunk/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/service/ServiceOperations.java
hadoop/common/trunk/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-common/src/test/java/org/apache/hadoop/yarn/service/TestServiceOperations.java
Added: hadoop/common/trunk/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/service/ServiceOperations.java
URL: http://svn.apache.org/viewvc/hadoop/common/trunk/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/service/ServiceOperations.java?rev=1300899&view=auto
==============================================================================
--- hadoop/common/trunk/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/service/ServiceOperations.java (added)
+++ hadoop/common/trunk/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/service/ServiceOperations.java Thu Mar 15 10:45:00 2012
@@ -0,0 +1,140 @@
+/**
+ * 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.yarn.service;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.conf.Configuration;
+
+/**
+ * This class contains a set of methods to work with services, especially
+ * to walk them through their lifecycle.
+ */
+public final class ServiceOperations {
+ private static final Log LOG = LogFactory.getLog(AbstractService.class);
+
+ private ServiceOperations() {
+ }
+
+ /**
+ * Verify that that a service is in a given state.
+ * @param state the actual state a service is in
+ * @param expectedState the desired state
+ * @throws IllegalStateException if the service state is different from
+ * the desired state
+ */
+ public static void ensureCurrentState(Service.STATE state,
+ Service.STATE expectedState) {
+ if (state != expectedState) {
+ throw new IllegalStateException("For this operation, the " +
+ "current service state must be "
+ + expectedState
+ + " instead of " + state);
+ }
+ }
+
+ /**
+ * Initialize a service.
+ * <p/>
+ * The service state is checked <i>before</i> the operation begins.
+ * This process is <i>not</i> thread safe.
+ * @param service a service that must be in the state
+ * {@link Service.STATE#NOTINITED}
+ * @param configuration the configuration to initialize the service with
+ * @throws RuntimeException on a state change failure
+ * @throws IllegalStateException if the service is in the wrong state
+ */
+
+ public static void init(Service service, Configuration configuration) {
+ Service.STATE state = service.getServiceState();
+ ensureCurrentState(state, Service.STATE.NOTINITED);
+ service.init(configuration);
+ }
+
+ /**
+ * Start a service.
+ * <p/>
+ * The service state is checked <i>before</i> the operation begins.
+ * This process is <i>not</i> thread safe.
+ * @param service a service that must be in the state
+ * {@link Service.STATE#INITED}
+ * @throws RuntimeException on a state change failure
+ * @throws IllegalStateException if the service is in the wrong state
+ */
+
+ public static void start(Service service) {
+ Service.STATE state = service.getServiceState();
+ ensureCurrentState(state, Service.STATE.INITED);
+ service.start();
+ }
+
+ /**
+ * Initialize then start a service.
+ * <p/>
+ * The service state is checked <i>before</i> the operation begins.
+ * This process is <i>not</i> thread safe.
+ * @param service a service that must be in the state
+ * {@link Service.STATE#NOTINITED}
+ * @param configuration the configuration to initialize the service with
+ * @throws RuntimeException on a state change failure
+ * @throws IllegalStateException if the service is in the wrong state
+ */
+ public static void deploy(Service service, Configuration configuration) {
+ init(service, configuration);
+ start(service);
+ }
+
+ /**
+ * Stop a service.
+ * <p/>Do nothing if the service is null or not
+ * in a state in which it can be/needs to be stopped.
+ * <p/>
+ * The service state is checked <i>before</i> the operation begins.
+ * This process is <i>not</i> thread safe.
+ * @param service a service or null
+ */
+ public static void stop(Service service) {
+ if (service != null) {
+ Service.STATE state = service.getServiceState();
+ if (state == Service.STATE.STARTED) {
+ service.stop();
+ }
+ }
+ }
+
+ /**
+ * Stop a service; if it is null do nothing. Exceptions are caught and
+ * logged at warn level. (but not Throwables). This operation is intended to
+ * be used in cleanup operations
+ *
+ * @param service a service; may be null
+ * @return any exception that was caught; null if none was.
+ */
+ public static Exception stopQuietly(Service service) {
+ try {
+ stop(service);
+ } catch (Exception e) {
+ LOG.warn("When stopping the service " + service.getName()
+ + " : " + e,
+ e);
+ return e;
+ }
+ return null;
+ }
+}
Added: hadoop/common/trunk/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-common/src/test/java/org/apache/hadoop/yarn/service/TestServiceOperations.java
URL: http://svn.apache.org/viewvc/hadoop/common/trunk/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-common/src/test/java/org/apache/hadoop/yarn/service/TestServiceOperations.java?rev=1300899&view=auto
==============================================================================
--- hadoop/common/trunk/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-common/src/test/java/org/apache/hadoop/yarn/service/TestServiceOperations.java (added)
+++ hadoop/common/trunk/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-common/src/test/java/org/apache/hadoop/yarn/service/TestServiceOperations.java Thu Mar 15 10:45:00 2012
@@ -0,0 +1,312 @@
+/**
+ * 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.yarn.service;
+
+import org.apache.hadoop.conf.Configuration;
+import org.junit.Test;
+
+/**
+ * These tests verify that the {@link ServiceOperations} methods
+ * do a best-effort attempt to make the service state change operations
+ * idempotent. That is still best effort -there is no thread safety, and
+ * a failure during a state change does not prevent the operation
+ * being called again.
+ */
+public class TestServiceOperations extends ServiceAssert {
+
+ @Test
+ public void testWalkthrough() throws Throwable {
+ BreakableService svc = new BreakableService();
+ assertServiceStateCreated(svc);
+ Configuration conf = new Configuration();
+ conf.set("test.walkthrough","t");
+ ServiceOperations.init(svc, conf);
+ assertServiceStateInited(svc);
+ assertStateCount(svc, Service.STATE.INITED, 1);
+ //check the configuration made it all the way through.
+ assertServiceConfigurationContains(svc, "test.walkthrough");
+ ServiceOperations.start(svc);
+ assertServiceStateStarted(svc);
+ assertStateCount(svc, Service.STATE.STARTED, 1);
+ ServiceOperations.stop(svc);
+ assertServiceStateStopped(svc);
+ assertStateCount(svc, Service.STATE.STOPPED, 1);
+ }
+
+ /**
+ * Call init twice -expect a failure, and expect the count
+ * of initialization attempts to still be 1: the state
+ * check was made before the subclass method was called.
+ * @throws Throwable if need be
+ */
+ @Test
+ public void testInitTwice() throws Throwable {
+ BreakableService svc = new BreakableService();
+ Configuration conf = new Configuration();
+ conf.set("test.init", "t");
+ ServiceOperations.init(svc, conf);
+ try {
+ ServiceOperations.init(svc, new Configuration());
+ fail("Expected a failure, got " + svc);
+ } catch (IllegalStateException e) {
+ //expected
+ }
+ assertStateCount(svc, Service.STATE.INITED, 1);
+ assertServiceConfigurationContains(svc, "test.init");
+ }
+
+ /**
+ * call start twice; expect failures and the start invoke count to
+ * be exactly 1.
+ * @throws Throwable if necessary
+ */
+ @Test
+ public void testStartTwice() throws Throwable {
+ BreakableService svc = new BreakableService();
+ ServiceOperations.init(svc, new Configuration());
+ ServiceOperations.start(svc);
+ try {
+ ServiceOperations.start(svc);
+ fail("Expected a failure, got " + svc);
+ } catch (IllegalStateException e) {
+ //expected
+ }
+ assertStateCount(svc, Service.STATE.STARTED, 1);
+ }
+
+ /**
+ * Test that the deploy operation pushes a service into its started state
+ * @throws Throwable on any failure.
+ */
+ @Test
+ public void testDeploy() throws Throwable {
+ BreakableService svc = new BreakableService();
+ assertServiceStateCreated(svc);
+ ServiceOperations.deploy(svc, new Configuration());
+ assertServiceStateStarted(svc);
+ assertStateCount(svc, Service.STATE.INITED, 1);
+ assertStateCount(svc, Service.STATE.STARTED, 1);
+ ServiceOperations.stop(svc);
+ assertServiceStateStopped(svc);
+ assertStateCount(svc, Service.STATE.STOPPED, 1);
+ }
+
+ /**
+ * Demonstrate that the deploy operation fails when invoked twice,
+ * but the service method call counts are unchanged after the second call.
+ * @throws Throwable on any failure.
+ */
+ @Test
+ public void testDeployNotIdempotent() throws Throwable {
+ BreakableService svc = new BreakableService();
+ assertServiceStateCreated(svc);
+ ServiceOperations.deploy(svc, new Configuration());
+ try {
+ ServiceOperations.deploy(svc, new Configuration());
+ fail("Expected a failure, got " + svc);
+ } catch (IllegalStateException e) {
+ //expected
+ }
+ //verify state and values are unchanged
+ assertServiceStateStarted(svc);
+ assertStateCount(svc, Service.STATE.INITED, 1);
+ assertStateCount(svc, Service.STATE.STARTED, 1);
+ ServiceOperations.stop(svc);
+ }
+
+ /**
+ * Test that the deploy operation can fail part way through, in which
+ * case the service is in the state that it was in before the failing
+ * state method was called.
+ * @throws Throwable on any failure.
+ */
+ @Test
+ public void testDeployNotAtomic() throws Throwable {
+ //this instance is set to fail in the start() call.
+ BreakableService svc = new BreakableService(false, true, false);
+ try {
+ ServiceOperations.deploy(svc, new Configuration());
+ fail("Expected a failure, got " + svc);
+ } catch (BreakableService.BrokenLifecycleEvent expected) {
+ //expected
+ }
+ //now in the inited state
+ assertServiceStateInited(svc);
+ assertStateCount(svc, Service.STATE.INITED, 1);
+ assertStateCount(svc, Service.STATE.STARTED, 1);
+ //try again -expect a failure as the service is now inited.
+ try {
+ ServiceOperations.deploy(svc, new Configuration());
+ fail("Expected a failure, got " + svc);
+ } catch (IllegalStateException e) {
+ //expected
+ }
+ }
+
+ /**
+ * verify that when a service is stopped more than once, no exception
+ * is thrown, and the counter is not incremented
+ * this is because the state change operations happen after the counter in
+ * the subclass is incremented, even though stop is meant to be a no-op
+ * @throws Throwable on a failure
+ */
+ @Test
+ public void testStopTwice() throws Throwable {
+ BreakableService svc = new BreakableService();
+ ServiceOperations.deploy(svc, new Configuration());
+ ServiceOperations.stop(svc);
+ assertStateCount(svc, Service.STATE.STOPPED, 1);
+ assertServiceStateStopped(svc);
+ ServiceOperations.stop(svc);
+ assertStateCount(svc, Service.STATE.STOPPED, 1);
+ }
+
+ /**
+ * verify that when a service that is not started is stopped, it's counter
+ * is not incremented -the stop() method was not invoked.
+ * @throws Throwable on a failure
+ */
+ @Test
+ public void testStopInit() throws Throwable {
+ BreakableService svc = new BreakableService();
+ ServiceOperations.stop(svc);
+ assertServiceStateCreated(svc);
+ assertStateCount(svc, Service.STATE.STOPPED, 0);
+ ServiceOperations.stop(svc);
+ assertStateCount(svc, Service.STATE.STOPPED, 0);
+ }
+
+
+ /**
+ * Show that if the service failed during an init
+ * operation, it stays in the created state, even after stopping it
+ * @throws Throwable
+ */
+
+ @Test
+ public void testStopFailedInit() throws Throwable {
+ BreakableService svc = new BreakableService(true, false, false);
+ assertServiceStateCreated(svc);
+ try {
+ ServiceOperations.init(svc, new Configuration());
+ fail("Expected a failure, got " + svc);
+ } catch (BreakableService.BrokenLifecycleEvent e) {
+ //expected
+ }
+ //the service state wasn't passed
+ assertServiceStateCreated(svc);
+ //the init state got invoked once
+ assertStateCount(svc, Service.STATE.INITED, 1);
+ //now try to stop
+ ServiceOperations.stop(svc);
+ //even after the stop operation, we haven't entered the state
+ assertServiceStateCreated(svc);
+ }
+
+
+ /**
+ * Show that if the service failed during an init
+ * operation, it stays in the created state, even after stopping it
+ * @throws Throwable
+ */
+
+ @Test
+ public void testStopFailedStart() throws Throwable {
+ BreakableService svc = new BreakableService(false, true, false);
+ ServiceOperations.init(svc, new Configuration());
+ assertServiceStateInited(svc);
+ try {
+ ServiceOperations.start(svc);
+ fail("Expected a failure, got " + svc);
+ } catch (BreakableService.BrokenLifecycleEvent e) {
+ //expected
+ }
+ //the service state wasn't passed
+ assertServiceStateInited(svc);
+ assertStateCount(svc, Service.STATE.INITED, 1);
+ //now try to stop
+ ServiceOperations.stop(svc);
+ //even after the stop operation, we haven't entered the state
+ assertServiceStateInited(svc);
+ }
+
+ /**
+ * verify that when a service is stopped more than once, no exception
+ * is thrown, and the counter is incremented
+ * this is because the state change operations happen after the counter in
+ * the subclass is incremented, even though stop is meant to be a no-op.
+ *
+ * The {@link ServiceOperations#stop(Service)} operation does not prevent
+ * this from happening
+ * @throws Throwable
+ */
+ @Test
+ public void testFailingStop() throws Throwable {
+ BreakableService svc = new BreakableService(false, false, true);
+ ServiceOperations.deploy(svc, new Configuration());
+ try {
+ ServiceOperations.stop(svc);
+ fail("Expected a failure, got " + svc);
+ } catch (BreakableService.BrokenLifecycleEvent e) {
+ //expected
+ }
+ assertStateCount(svc, Service.STATE.STOPPED, 1);
+ //now try to stop, this time doing it quietly
+ Exception exception = ServiceOperations.stopQuietly(svc);
+ assertTrue("Wrong exception type : " + exception,
+ exception instanceof BreakableService.BrokenLifecycleEvent);
+ assertStateCount(svc, Service.STATE.STOPPED, 2);
+ }
+
+
+ /**
+ * verify that when a service that is not started is stopped, its counter
+ * of stop calls is still incremented-and the service remains in its
+ * original state..
+ * @throws Throwable on a failure
+ */
+ @Test
+ public void testStopUnstarted() throws Throwable {
+ BreakableService svc = new BreakableService();
+
+ //invocation in NOTINITED state should be no-op
+ ServiceOperations.stop(svc);
+ assertServiceStateCreated(svc);
+ assertStateCount(svc, Service.STATE.STOPPED, 0);
+
+ //stop failed, now it can be initialised
+ ServiceOperations.init(svc, new Configuration());
+
+ //again, no-op
+ ServiceOperations.stop(svc);
+ assertServiceStateInited(svc);
+ assertStateCount(svc, Service.STATE.STOPPED, 0);
+
+ //once started, the service can be stopped reliably
+ ServiceOperations.start(svc);
+ ServiceOperations.stop(svc);
+ assertServiceStateStopped(svc);
+ assertStateCount(svc, Service.STATE.STOPPED, 1);
+
+ //now stop one more time
+ ServiceOperations.stop(svc);
+ assertStateCount(svc, Service.STATE.STOPPED, 1);
+ }
+}