View Javadoc

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 }