/* * 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.classification.InterfaceAudience.Public; import org.apache.hadoop.classification.InterfaceStability.Evolving; /** * Implements the service state model. */ @Public @Evolving public class ServiceStateModel { /** * Map of all valid state transitions * [current] [proposed1, proposed2, ...] */ private static final boolean[][] statemap = { // uninited inited started stopped /* uninited */ {false, true, false, true}, /* inited */ {false, true, true, true}, /* started */ {false, false, true, true}, /* stopped */ {false, false, false, true}, }; /** * The state of the service */ private volatile Service.STATE state; /** * The name of the service: used in exceptions */ private String name; /** * Create the service state model in the {@link Service.STATE#NOTINITED} * state. */ public ServiceStateModel(String name) { this(name, Service.STATE.NOTINITED); } /** * Create a service state model instance in the chosen state * @param state the starting state */ public ServiceStateModel(String name, Service.STATE state) { this.state = state; this.name = name; } /** * Query the service state. This is a non-blocking operation. * @return the state */ public Service.STATE getState() { return state; } /** * Query that the state is in a specific state * @param proposed proposed new state * @return the state */ public boolean isInState(Service.STATE proposed) { return state.equals(proposed); } /** * Verify that that a service is in a given state. * @param expectedState the desired state * @throws ServiceStateException if the service state is different from * the desired state */ public void ensureCurrentState(Service.STATE expectedState) { if (state != expectedState) { throw new ServiceStateException(name+ ": for this operation, the " + "current service state must be " + expectedState + " instead of " + state); } } /** * Enter a state -thread safe. * * @param proposed proposed new state * @return the original state * @throws ServiceStateException if the transition is not permitted */ public synchronized Service.STATE enterState(Service.STATE proposed) { checkStateTransition(name, state, proposed); Service.STATE oldState = state; //atomic write of the new state state = proposed; return oldState; } /** * Check that a state tansition is valid and * throw an exception if not * @param name name of the service (can be null) * @param state current state * @param proposed proposed new state */ public static void checkStateTransition(String name, Service.STATE state, Service.STATE proposed) { if (!isValidStateTransition(state, proposed)) { throw new ServiceStateException(name + " cannot enter state " + proposed + " from state " + state); } } /** * Is a state transition valid? * There are no checks for current==proposed * as that is considered a non-transition. * * using an array kills off all branch misprediction costs, at the expense * of cache line misses. * * @param current current state * @param proposed proposed new state * @return true if the transition to a new state is valid */ public static boolean isValidStateTransition(Service.STATE current, Service.STATE proposed) { boolean[] row = statemap[current.getValue()]; return row[proposed.getValue()]; } /** * return the state text as the toString() value * @return the current state's description */ @Override public String toString() { return (name.isEmpty() ? "" : ((name) + ": ")) + state.toString(); } }