From 293c425b25b1c2ceff2eaa726e804ede767e371c Mon Sep 17 00:00:00 2001 From: Anu Engineer Date: Wed, 16 Aug 2017 14:36:34 -0700 Subject: [PATCH] HDFS-12305. Ozone: SCM: Add StateMachine for pipeline/container. Contributed by Xiaoyu Yao. --- .../InvalidStateTransitionException.java | 42 ++++++++ .../helpers/StateMachine/StateMachine.java | 69 ++++++++++++++ .../apache/hadoop/scm/TestStateMachine.java | 95 +++++++++++++++++++ 3 files changed, 206 insertions(+) create mode 100644 hadoop-hdfs-client/src/main/java/org/apache/hadoop/scm/container/common/helpers/StateMachine/InvalidStateTransitionException.java create mode 100644 hadoop-hdfs-client/src/main/java/org/apache/hadoop/scm/container/common/helpers/StateMachine/StateMachine.java create mode 100644 hadoop-hdfs-client/src/test/java/org/apache/hadoop/scm/TestStateMachine.java diff --git a/hadoop-hdfs-client/src/main/java/org/apache/hadoop/scm/container/common/helpers/StateMachine/InvalidStateTransitionException.java b/hadoop-hdfs-client/src/main/java/org/apache/hadoop/scm/container/common/helpers/StateMachine/InvalidStateTransitionException.java new file mode 100644 index 00000000000..1fab16b5ccf --- /dev/null +++ b/hadoop-hdfs-client/src/main/java/org/apache/hadoop/scm/container/common/helpers/StateMachine/InvalidStateTransitionException.java @@ -0,0 +1,42 @@ +/* + * 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.scm.container.common.helpers.StateMachine; + +/** + * Class wraps invalid state transition exception. + */ +public class InvalidStateTransitionException extends Exception { + private Enum currentState; + private Enum event; + + public InvalidStateTransitionException(Enum currentState, Enum event) { + super("Invalid event: " + event + " at " + currentState + " state."); + this.currentState = currentState; + this.event = event; + } + + public Enum getCurrentState() { + return currentState; + } + + public Enum getEvent() { + return event; + } + +} diff --git a/hadoop-hdfs-client/src/main/java/org/apache/hadoop/scm/container/common/helpers/StateMachine/StateMachine.java b/hadoop-hdfs-client/src/main/java/org/apache/hadoop/scm/container/common/helpers/StateMachine/StateMachine.java new file mode 100644 index 00000000000..1d5436fe1ff --- /dev/null +++ b/hadoop-hdfs-client/src/main/java/org/apache/hadoop/scm/container/common/helpers/StateMachine/StateMachine.java @@ -0,0 +1,69 @@ +/* + * 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.scm.container.common.helpers.StateMachine; + +import com.google.common.base.Supplier; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +/** + * Template class that wraps simple event driven state machine. + * @param states allowed + * @param events allowed + */ +public class StateMachine, EVENT extends Enum> { + private STATE initialState; + private Set finalStates; + + private final Cache> transitions = + CacheBuilder.newBuilder().build( + CacheLoader.from((Supplier>) () -> new HashMap())); + + public StateMachine(STATE initState, Set finalStates) { + this.initialState = initState; + this.finalStates = finalStates; + } + + public STATE getInitialState() { + return initialState; + } + + public Set getFinalStates() { + return finalStates; + } + + public STATE getNextState(STATE from, EVENT e) + throws InvalidStateTransitionException { + STATE target = transitions.getUnchecked(e).get(from); + if (target == null) { + throw new InvalidStateTransitionException(from, e); + } + return target; + } + + public void addTransition(STATE from, STATE to, EVENT e) + throws InvalidStateTransitionException { + transitions.getUnchecked(e).put(from, to); + } +} \ No newline at end of file diff --git a/hadoop-hdfs-client/src/test/java/org/apache/hadoop/scm/TestStateMachine.java b/hadoop-hdfs-client/src/test/java/org/apache/hadoop/scm/TestStateMachine.java new file mode 100644 index 00000000000..f9e1d5a91c3 --- /dev/null +++ b/hadoop-hdfs-client/src/test/java/org/apache/hadoop/scm/TestStateMachine.java @@ -0,0 +1,95 @@ +/** + * 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.scm; + +import org.apache.commons.collections.SetUtils; +import org.apache.hadoop.scm.container.common.helpers.StateMachine.InvalidStateTransitionException; +import org.apache.hadoop.scm.container.common.helpers.StateMachine.StateMachine; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import java.util.HashSet; +import java.util.Set; + +import static org.apache.hadoop.scm.TestStateMachine.STATES.INIT; +import static org.apache.hadoop.scm.TestStateMachine.STATES.CREATING; +import static org.apache.hadoop.scm.TestStateMachine.STATES.OPERATIONAL; +import static org.apache.hadoop.scm.TestStateMachine.STATES.CLOSED; +import static org.apache.hadoop.scm.TestStateMachine.STATES.CLEANUP; +import static org.apache.hadoop.scm.TestStateMachine.STATES.FINAL; + +public class TestStateMachine { + + public enum STATES {INIT, CREATING, OPERATIONAL, CLOSED, CLEANUP, FINAL}; + + public enum EVENTS {ALLOCATE, CREATE, UPDATE, CLOSE, DELETE, TIMEOUT}; + + @Rule + public ExpectedException exception = ExpectedException.none(); + + @Test + public void testStateMachineStates() throws InvalidStateTransitionException { + Set finals = new HashSet<>(); + finals.add(FINAL); + + StateMachine stateMachine = + new StateMachine<>(INIT, finals); + + stateMachine.addTransition(INIT, CREATING, EVENTS.ALLOCATE); + stateMachine.addTransition(CREATING, OPERATIONAL, EVENTS.CREATE); + stateMachine.addTransition(OPERATIONAL, OPERATIONAL, EVENTS.UPDATE); + stateMachine.addTransition(OPERATIONAL, CLEANUP, EVENTS.DELETE); + stateMachine.addTransition(OPERATIONAL, CLOSED, EVENTS.CLOSE); + stateMachine.addTransition(CREATING, CLEANUP, EVENTS.TIMEOUT); + + // Initial and Final states + Assert.assertEquals("Initial State", INIT, stateMachine.getInitialState()); + Assert.assertTrue("Final States", SetUtils.isEqualSet(finals, + stateMachine.getFinalStates())); + + // Valid state transitions + Assert.assertEquals("STATE should be OPERATIONAL after being created", + OPERATIONAL, stateMachine.getNextState(CREATING, EVENTS.CREATE)); + Assert.assertEquals("STATE should be OPERATIONAL after being updated", + OPERATIONAL, stateMachine.getNextState(OPERATIONAL, EVENTS.UPDATE)); + Assert.assertEquals("STATE should be CLEANUP after being deleted", + CLEANUP, stateMachine.getNextState(OPERATIONAL, EVENTS.DELETE)); + Assert.assertEquals("STATE should be CLEANUP after being timeout", + CLEANUP, stateMachine.getNextState(CREATING, EVENTS.TIMEOUT)); + Assert.assertEquals("STATE should be CLOSED after being closed", + CLOSED, stateMachine.getNextState(OPERATIONAL, EVENTS.CLOSE)); + + // Negative cases: invalid transition + expectException(); + stateMachine.getNextState(OPERATIONAL, EVENTS.CREATE); + + expectException(); + stateMachine.getNextState(CREATING, EVENTS.CLOSE); + } + + /** + * We expect an InvalidStateTransitionException. + */ + private void expectException() { + exception.expect(InvalidStateTransitionException.class); + exception.expectMessage("Invalid event"); + } + +}