package org.openamq.client.state;
import edu.emory.mathcs.backport.java.util.concurrent.CopyOnWriteArraySet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.openamq.AMQException;
import org.openamq.framing.AMQMethodBody;
import org.openamq.client.protocol.*;
import org.openamq.client.handler.*;
import org.openamq.framing.*;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* The state manager is responsible for managing the state of the protocol session.
* <p/>
* For each AMQProtocolHandler there is a separate state manager.
*
*/
public class AMQStateManager implements AMQMethodListener
{
private static final Logger _logger = LoggerFactory.getLogger(AMQStateManager.class);
/**
* The current state
*/
private AMQState _currentState;
/**
* Maps from an AMQState instance to a Map from Class to StateTransitionHandler.
* The class must be a subclass of AMQFrame.
*/
private final Map _state2HandlersMap = new HashMap();
private final CopyOnWriteArraySet _stateListeners = new CopyOnWriteArraySet();
public AMQStateManager()
{
this(AMQState.CONNECTION_NOT_STARTED, true);
}
protected AMQStateManager(AMQState state, boolean register)
{
_currentState = state;
if(register)
{
registerListeners();
}
}
protected void registerListeners()
{
Map frame2handlerMap = new HashMap();
// we need to register a map for the null (i.e. all state) handlers otherwise you get
// a stack overflow in the handler searching code when you present it with a frame for which
// no handlers are registered
//
_state2HandlersMap.put(null, frame2handlerMap);
frame2handlerMap = new HashMap();
frame2handlerMap.put(ConnectionStartBody.class, ConnectionStartMethodHandler.getInstance());
frame2handlerMap.put(ConnectionCloseBody.class, ConnectionCloseMethodHandler.getInstance());
_state2HandlersMap.put(AMQState.CONNECTION_NOT_STARTED, frame2handlerMap);
frame2handlerMap = new HashMap();
frame2handlerMap.put(ConnectionTuneBody.class, ConnectionTuneMethodHandler.getInstance());
frame2handlerMap.put(ConnectionSecureBody.class, ConnectionSecureMethodHandler.getInstance());
frame2handlerMap.put(ConnectionCloseBody.class, ConnectionCloseMethodHandler.getInstance());
_state2HandlersMap.put(AMQState.CONNECTION_NOT_TUNED, frame2handlerMap);
frame2handlerMap = new HashMap();
frame2handlerMap.put(ConnectionOpenOkBody.class, ConnectionOpenOkMethodHandler.getInstance());
frame2handlerMap.put(ConnectionCloseBody.class, ConnectionCloseMethodHandler.getInstance());
_state2HandlersMap.put(AMQState.CONNECTION_NOT_OPENED, frame2handlerMap);
//
// ConnectionOpen handlers
//
frame2handlerMap = new HashMap();
frame2handlerMap.put(ChannelCloseBody.class, ChannelCloseMethodHandler.getInstance());
frame2handlerMap.put(ConnectionCloseBody.class, ConnectionCloseMethodHandler.getInstance());
frame2handlerMap.put(BasicDeliverBody.class, BasicDeliverMethodHandler.getInstance());
frame2handlerMap.put(BasicReturnBody.class, BasicReturnMethodHandler.getInstance());
frame2handlerMap.put(ChannelFlowOkBody.class, ChannelFlowOkMethodHandler.getInstance());
_state2HandlersMap.put(AMQState.CONNECTION_OPEN, frame2handlerMap);
}
public AMQState getCurrentState()
{
return _currentState;
}
public void changeState(AMQState newState) throws AMQException
{
_logger.debug("State changing to " + newState + " from old state " + _currentState);
final AMQState oldState = _currentState;
_currentState = newState;
synchronized (_stateListeners)
{
final Iterator it = _stateListeners.iterator();
while (it.hasNext())
{
final StateListener l = (StateListener) it.next();
l.stateChanged(oldState, newState);
}
}
}
public void error(Exception e)
{
_logger.debug("State manager receive error notification: " + e);
synchronized (_stateListeners)
{
final Iterator it = _stateListeners.iterator();
while (it.hasNext())
{
final StateListener l = (StateListener) it.next();
l.error(e);
}
}
}
public boolean methodReceived(AMQMethodEvent evt) throws AMQException
{
StateAwareMethodListener handler = findStateTransitionHandler(_currentState, evt.getMethod());
if (handler != null)
{
handler.methodReceived(this, evt);
return true;
}
return false;
}
protected StateAwareMethodListener findStateTransitionHandler(AMQState currentState,
AMQMethodBody frame)
throws IllegalStateTransitionException
{
final Class clazz = frame.getClass();
if (_logger.isDebugEnabled())
{
_logger.debug("Looking for state transition handler for frame " + clazz);
}
final Map classToHandlerMap = (Map) _state2HandlersMap.get(currentState);
if (classToHandlerMap == null)
{
// if no specialised per state handler is registered look for a
// handler registered for "all" states
return findStateTransitionHandler(null, frame);
}
final StateAwareMethodListener handler = (StateAwareMethodListener) classToHandlerMap.get(clazz);
if (handler == null)
{
if (currentState == null)
{
_logger.debug("No state transition handler defined for receiving frame " + frame);
return null;
}
else
{
// if no specialised per state handler is registered look for a
// handler registered for "all" states
return findStateTransitionHandler(null, frame);
}
}
else
{
return handler;
}
}
public void addStateListener(StateListener listener)
{
_logger.debug("Adding state listener");
_stateListeners.add(listener);
}
public void removeStateListener(StateListener listener)
{
_stateListeners.remove(listener);
}
public void attainState(AMQState s) throws AMQException
{
boolean needToWait = false;
StateWaiter sw = null;
synchronized (_stateListeners)
{
if (_currentState != s)
{
_logger.debug("Adding state wait to reach state " + s);
sw = new StateWaiter(s);
addStateListener(sw);
// we use a boolean since we must release the lock before starting to wait
needToWait = true;
}
}
if (needToWait)
{
sw.waituntilStateHasChanged();
}
// at this point the state will have changed.
}
}