1 /**
2 This file is part of GoldenGate Project (named also GoldenGate or GG).
3
4 Copyright 2009, Frederic Bregier, and individual contributors by the @author
5 tags. See the COPYRIGHT.txt in the distribution for a full listing of
6 individual contributors.
7
8 All GoldenGate Project is free software: you can redistribute it and/or
9 modify it under the terms of the GNU General Public License as published
10 by the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
12
13 GoldenGate is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with GoldenGate . If not, see <http://www.gnu.org/licenses/>.
20 */
21 package goldengate.common.state;
22
23 import goldengate.common.exception.IllegalFiniteStateException;
24 import goldengate.common.logging.GgInternalLogger;
25 import goldengate.common.logging.GgInternalLoggerFactory;
26
27 import java.util.EnumSet;
28 import java.util.concurrent.ConcurrentHashMap;
29
30 /**
31 * This is the base class for the basic support of Finite State Machine in GoldenGate.
32 * One need to implement an Enum class to use with it.
33 * <br><br>
34 * Note: the type EnumSet< ? > is in fact of type EnumSet< EnumState >
35 *
36 * @author Frederic Bregier
37 * @param <EnumState>
38 *
39 */
40 public class MachineState<EnumState> {
41 /**
42 * Internal Logger
43 */
44 private static final GgInternalLogger logger = GgInternalLoggerFactory
45 .getLogger(MachineState.class);
46
47 private ConcurrentHashMap<EnumState, EnumSet<?>> statemap;
48 private EnumState currentState;
49
50 /**
51 * Initialize with an initialState
52 * @param initialState initial MachineState
53 * @param map the association of state and set of acceptable following states
54 */
55 public MachineState(EnumState initialState, ConcurrentHashMap<EnumState, EnumSet<?>> map) {
56 statemap = map;
57 currentState = initialState;
58 }
59 /**
60 * Initialize with an initialState but no association (Machine State is empty)
61 * @param initialState initial MachineState
62 */
63 public MachineState(EnumState initialState) {
64 statemap = new ConcurrentHashMap<EnumState, EnumSet<?>>();
65 currentState = initialState;
66 }
67 /**
68 * Add a new association from one state to a set of acceptable following states
69 * (can replace an existing association)
70 * @param state
71 * @param set the new association
72 * @return the previous association if any
73 */
74 public EnumSet<?> addNewAssociation(EnumState state, EnumSet<?> set) {
75 return statemap.put(state, set);
76 }
77 /**
78 * Add a new association from one state to a set of acceptable following states
79 * (can replace an existing association)
80 * @param elt
81 * @return the previous association if any
82 */
83 public EnumSet<?> addNewAssociation(Transition<EnumState> elt) {
84 return statemap.put(elt.state, elt.set);
85 }
86 /**
87 * Remove an association from one state to any acceptable following states
88 *
89 * @param state the state to remove any acceptable following states
90 * @return the previous association if any
91 */
92 public EnumSet<?> removeAssociation(EnumState state) {
93 return statemap.remove(state);
94 }
95 /**
96 * Return the current application state.
97 *
98 * @return the current State
99 */
100 public EnumState getCurrent() {
101 return currentState;
102 }
103 /**
104 * Sets the current application state.
105 *
106 * @param desiredState
107 * @return the requested state, if it was reachable
108 * @throws IllegalFiniteStateException if the state is not allowed
109 */
110 public EnumState setCurrent(EnumState desiredState) throws IllegalFiniteStateException {
111 if (!isReachable(desiredState)) {
112 logger.debug("State "+desiredState+" not reachable from: "+currentState);
113 throw new IllegalFiniteStateException(desiredState+" not allowed from "+currentState);
114 }
115 return setAsFinal(desiredState);
116 }
117 /**
118 * Sets the current application state, but no exception if not compatible.
119 * @param desiredState
120 * @return the requested state, even if it was not reachable
121 */
122 public EnumState setDryCurrent(EnumState desiredState) {
123 return setAsFinal(desiredState);
124 }
125 /**
126 * Determine if the given state is allowed to be next.
127 *
128 * @param desiredState desired MachineState
129 * @return True if the desiredState is valid from currentState
130 */
131 private boolean isReachable(EnumState desiredState) {
132 if (currentState == null || statemap == null) {
133 return false;
134 }
135 EnumSet<?> set = statemap.get(currentState);
136 if (set != null) {
137 return set.contains(desiredState);
138 }
139 return false;
140 }
141 /**
142 * Finalizes the new requested state
143 *
144 * @param desiredState
145 * @return the requested state
146 */
147 private EnumState setAsFinal(EnumState desiredState) {
148 logger.debug("New State: "+desiredState+" from "+currentState);
149 currentState = desiredState;
150 return currentState;
151 }
152 /**
153 * Release the Machine State
154 */
155 public void release() {
156 this.currentState = null;
157 this.statemap = null;
158 }
159 }