001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019package org.apache.hadoop.service; 020 021import org.apache.hadoop.classification.InterfaceAudience.Public; 022import org.apache.hadoop.classification.InterfaceStability.Evolving; 023 024/** 025 * Implements the service state model. 026 */ 027@Public 028@Evolving 029public class ServiceStateModel { 030 031 /** 032 * Map of all valid state transitions 033 * [current] [proposed1, proposed2, ...] 034 */ 035 private static final boolean[][] statemap = 036 { 037 // uninited inited started stopped 038 /* uninited */ {false, true, false, true}, 039 /* inited */ {false, true, true, true}, 040 /* started */ {false, false, true, true}, 041 /* stopped */ {false, false, false, true}, 042 }; 043 044 /** 045 * The state of the service 046 */ 047 private volatile Service.STATE state; 048 049 /** 050 * The name of the service: used in exceptions 051 */ 052 private String name; 053 054 /** 055 * Create the service state model in the {@link Service.STATE#NOTINITED} 056 * state. 057 */ 058 public ServiceStateModel(String name) { 059 this(name, Service.STATE.NOTINITED); 060 } 061 062 /** 063 * Create a service state model instance in the chosen state 064 * @param state the starting state 065 */ 066 public ServiceStateModel(String name, Service.STATE state) { 067 this.state = state; 068 this.name = name; 069 } 070 071 /** 072 * Query the service state. This is a non-blocking operation. 073 * @return the state 074 */ 075 public Service.STATE getState() { 076 return state; 077 } 078 079 /** 080 * Query that the state is in a specific state 081 * @param proposed proposed new state 082 * @return the state 083 */ 084 public boolean isInState(Service.STATE proposed) { 085 return state.equals(proposed); 086 } 087 088 /** 089 * Verify that that a service is in a given state. 090 * @param expectedState the desired state 091 * @throws ServiceStateException if the service state is different from 092 * the desired state 093 */ 094 public void ensureCurrentState(Service.STATE expectedState) { 095 if (state != expectedState) { 096 throw new ServiceStateException(name+ ": for this operation, the " + 097 "current service state must be " 098 + expectedState 099 + " instead of " + state); 100 } 101 } 102 103 /** 104 * Enter a state -thread safe. 105 * 106 * @param proposed proposed new state 107 * @return the original state 108 * @throws ServiceStateException if the transition is not permitted 109 */ 110 public synchronized Service.STATE enterState(Service.STATE proposed) { 111 checkStateTransition(name, state, proposed); 112 Service.STATE oldState = state; 113 //atomic write of the new state 114 state = proposed; 115 return oldState; 116 } 117 118 /** 119 * Check that a state tansition is valid and 120 * throw an exception if not 121 * @param name name of the service (can be null) 122 * @param state current state 123 * @param proposed proposed new state 124 */ 125 public static void checkStateTransition(String name, 126 Service.STATE state, 127 Service.STATE proposed) { 128 if (!isValidStateTransition(state, proposed)) { 129 throw new ServiceStateException(name + " cannot enter state " 130 + proposed + " from state " + state); 131 } 132 } 133 134 /** 135 * Is a state transition valid? 136 * There are no checks for current==proposed 137 * as that is considered a non-transition. 138 * 139 * using an array kills off all branch misprediction costs, at the expense 140 * of cache line misses. 141 * 142 * @param current current state 143 * @param proposed proposed new state 144 * @return true if the transition to a new state is valid 145 */ 146 public static boolean isValidStateTransition(Service.STATE current, 147 Service.STATE proposed) { 148 boolean[] row = statemap[current.getValue()]; 149 return row[proposed.getValue()]; 150 } 151 152 /** 153 * return the state text as the toString() value 154 * @return the current state's description 155 */ 156 @Override 157 public String toString() { 158 return (name.isEmpty() ? "" : ((name) + ": ")) 159 + state.toString(); 160 } 161 162}