3

I am implementing a state pattern in Java for my application and need few clarifications.

The state machine has 5 states State 1 to State 5. The are a total of 5 events(Event1 to Event5) which causes the state transition. Not all events are applicable in all the states. If the event is not applicable in that particular state the application will throw exception.

When the state machine gets initialized it starts with state1.

Following is the interface and the context class.

/*
 Interface defining the possible events in each state.
 Each Implementer will handle event in a different manner. 
*/
public interface State {
 /*
  Handlers for each event. Each Implementer will handle the vent in a different manner.
 */
 public void handleEvent1(StateContext context);
 public void handleEvent2(StateContext context);
 public void handleEvent3(StateContext context);
 public void handleEvent4(StateContext context);
 public void handleEvent5(StateContext context);
 // Method to enter state and do some action.
 public void enter(StateContext context);
 // Method to exit state and do some clean-up activity on exit .
 public void exit(StateContext context);
}

/*
  Context class which will handle the state change and delegate event to appropriate event handler of current state
*/
Class StateContext {

   private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

   private State currentState = null;

   StateContext() {
        currentState = new State1();
   }

   //Handle event1 and pass it to the appropriate event handler for the current state.
   public void handleEvent1() {
      currentState.handleEvent1(); 
   }
       .
       .
       .
   //Handle event5 and pass it to the appropriate event handler for the current state.
   public void handleEvent5() {
      currentState.handleEvent5(); 
   }

   // Method to change the state. 
   // This method will be called by each state when it needs to transit to a new state.
   public void changeState(State newState) {
          accquireLock();
          currentState.exit();
          currentState = newState;
          currentState.enter();           
   }

   // Release read lock and accquire write lock
   public void accquireLock() {
        lock.readLock().unlock()
        lock.writeLock().lock();
   }

   // accquire readlock and release write lock
   public void releaseLock() {
        lock.readLock().lock()
        lock.writeLock().unlock();
   }
}

To make it simple, I have provided implementation for only one state.

public class State1 implements State {
       public void handleEvent1(StateContext context) {
          //Hand1e Event 1
       }
              .
              .
              .
      public void handleEvent5(StateContext context) {
          //Handle Event 5
       }


       public void enter(StateContext context) {
           //Release the lock here
           context.releaseLock();
           /*Here is my question. Is it a  good java practice to expose accquire and release lock in Context object. And use the exposed method here to release lock. 
           */

           // Do some action on entering the state. This may take few seconds to finish

       }  
}

I want to release the lock only after entering state. Also I don't want to hold the lock till enter() finishes. If I hold the lock till enter finishes I cannot handle other events and it may get timed-out.For some events (which don't really change the state) we need to read the state and based on the state we can process them or ignore them. If I don't release lock they cannot be processed.Also in some other cases if an event comes to shutdown(this event changes the state) the state machine while enter() is in progress I cannot handle it. I have to shutdown the state machine immediately since it is not appropriate in continuing the enter() after shutdown event has come.

My Question: Is it good java programming practice to expose the accquireLock and releaseLock as an API in Context class and use them in each state class.

Thanks, Arun

jaco0646
  • 15,303
  • 7
  • 59
  • 83
Arun
  • 327
  • 1
  • 3
  • 12
  • Why don't you call `releaseLock()` after the code executes in your `changeState()` method? Since you acquire the lock at the start of that method, it would seem to be consistent design to release the lock at the end of it, no? – Deactivator2 Aug 07 '13 at 12:44
  • For my application if i receive a shutdown event to shutdown the machine while enter() is in progress, i should not continue with the enter(). Continuing may cause adverse effects or may not result anything fruitful except wasting the resource until enter() finishes. In some cases enter may take more than 5 minutes and send lot of request. If I continue the reqeusts may not reach the receiver or the receiver may discard my request in that case i should stop immediately and move to initial state(for which i need the lock). – Arun Aug 07 '13 at 13:14
  • Then what you need is a method of interrupting the state's `enter` method that surpasses any lock you have in place. Treat the lock object as a block for state changes, but a shutdown event is superior to any state change event, and thus should be able to take effect at any time. – Deactivator2 Aug 07 '13 at 13:20
  • Yes. This seems to be a good option. Will try to fit into my application. Thanks Deactivator2 :) – Arun Aug 07 '13 at 13:24
  • Sorry missed to make a point. There are some events for which we jsut read the state and process them or ignore them accordingly. If i use use interrupt mechanism, I cannot read the state since the write lock is held by the thread processing enter(). I have edited the post. – Arun Aug 07 '13 at 13:45
  • Hmm. I see what you're getting at, but again, if you're interrupting, does it matter what the state is? The point of the interrupt is to stop whatever is happening as fast as possible, right? – Deactivator2 Aug 07 '13 at 13:58
  • Yes Exactly. Stop what the state machine is doing and shutdown the state machine. However at this point of time, i don't know how to process events (for which only reading the state is needed). – Arun Aug 07 '13 at 14:28
  • Make a method in your `StateContext` class that will receive shutdown events. Make a method in the base `State` class that will be called from the first method, that would set an `interrupted` boolean in each `State` class to true. Then, after every functional block of code in each `State`'s `enter()`, perform a check for the `interrupted` boolean, and exit the method if it is true. – Deactivator2 Aug 07 '13 at 14:53

1 Answers1

0

To answer the question, if the lock is held by the state "manager" class, then that class should be the one to control the lock access, not any of the actual state classes.

With regards to your statement about holding the lock until enter finishes, that is precisely the point of a lock: you don't want other methods getting involved or interrupting. Instead, you should develop some sort of event queue to catch all events and wait to distribute them if the receiving object is busy. Either that, or make a specific exception for whichever events you know are going to interrupt, so that you can bypass the lock when the specified event(s) are fired.

If you choose to use a method of interrupting, you're going to have to implement a number of checks in each State class's enter method to check if the shutdown event has been triggered. The only other way I see it working is to make each State extend Thread so that you may interrupt/stop it at will, though that does not sound like a valid option in this instance.

Deactivator2
  • 311
  • 1
  • 10
  • I agree too your point that if manager is holding the lock, only he should release the lock. Is it a good practice to expose acquire and release lock as an API in Manager and acquire and release it in a State handler. for eg in State1 class an event handler would be like public void handleEvent1() { context.accquireLock(); //Do something; context.releaseLock(); } – Arun Aug 08 '13 at 06:06
  • @Arun Yes, that is a good design pattern. Another would be to have the lock code in the event handler of the context instead of the state, but the functionality is the same. – Deactivator2 Aug 08 '13 at 13:11
  • 1
    ok thanks... Then my design would be after stepping into enter() method spawn a new thread to perform the operation which needs to be done after entering each state. By doing this the locks will not be held until the operation finishes and i can process the events which needs to just read the current state. After finishing the operation i.e. at the end of newly spawned thread acquire lock change state (if needed). – Arun Aug 08 '13 at 14:28