package org.jdiameter.server.impl.fsm; import static org.jdiameter.client.impl.fsm.PeerFSMImpl.CIntState.*; import static org.jdiameter.server.impl.helpers.StatisticTypes.PEER_QUEUE_SIZE; import org.jdiameter.api.Configuration; import org.jdiameter.api.ConfigurationListener; import org.jdiameter.api.MutableConfiguration; import org.jdiameter.api.ResultCode; import org.jdiameter.api.Statistic; import org.jdiameter.api.app.State; import org.jdiameter.api.app.StateEvent; import org.jdiameter.client.api.IMessage; import org.jdiameter.client.api.fsm.ExecutorFactory; import org.jdiameter.client.api.fsm.IContext; import org.jdiameter.server.api.IStatistic; import org.jdiameter.server.api.IStatisticRecord; import org.jdiameter.server.impl.StatisticImpl; import org.jdiameter.server.impl.StatisticRecordImpl; public class PeerFSMImpl extends org.jdiameter.client.impl.fsm.PeerFSMImpl implements ConfigurationListener { protected IStatisticRecord queueSize = new StatisticRecordImpl("FsmQueue", "Peer FSM queue size", PEER_QUEUE_SIZE, new StatisticRecordImpl.Counter() { public int getValueAsInt() { return eventQueue.size(); } } ); protected IStatistic queueStat = new StatisticImpl("PeerFSM" , "PeerFSM statistic", queueSize); public Statistic getStatistic() { return queueStat; } public PeerFSMImpl(IContext context, ExecutorFactory executor, Configuration config) { super(context, executor, config); } protected void loadTimeOuts(Configuration config) { super.loadTimeOuts(config); if (config instanceof MutableConfiguration) { ((MutableConfiguration)config).addChangeListener(this, 0); } } public boolean elementChanged(int i, Object data) { Configuration newConfig = (Configuration) data; super.loadTimeOuts(newConfig); return true; } protected State[] getStates() { if (states == null) { states = new State[] { new MyState() // OKEY { public void entryAction() { // todo send buffered messages setInActiveTimer(); watchdogSent = false; } public boolean processEvent(StateEvent event) { switch (type(event)) { case DISCONNECT_EVENT: doEndConnection(); break; case TIMEOUT_EVENT: try { context.sendDwrMessage(); setTimer(DWA_TIMEOUT); if (watchdogSent) { swithToNextState(SUSPECT); } else { watchdogSent = true; } } catch (Throwable e) { logger.debug("Can not send DWR", e); doDisconnect(); doEndConnection(); } break; case STOP_EVENT: try { // TODO: send user code; context.sendDprMessage(ResultCode.SUCCESS); setTimer(DPA_TIMEOUT); swithToNextState(STOPPING); } catch (Throwable e) { logger.debug("Can not send DPR", e); doDisconnect(); swithToNextState(DOWN); } break; case RECEIVE_MSG_EVENT: setInActiveTimer(); context.receiveMessage( message(event) ); break; case CEA_EVENT: setInActiveTimer(); if ( context.processCeaMessage(key(event), message(event)) ) { doDisconnect(); // ! doEndConnection(); } break; case CER_EVENT: setInActiveTimer(); // Skip break; case DPR_EVENT: try { int code = context.processDprMessage((IMessage) event.getData()); context.sendDpaMessage( message(event), code, null ); } catch (Throwable e) { logger.debug("Can not send DPA", e); } doDisconnect(); swithToNextState(DOWN); break; case DWR_EVENT: setInActiveTimer(); try { context.sendDwaMessage( message(event), ResultCode.SUCCESS, null); } catch (Throwable e) { logger.debug("Can not send DWA", e); doDisconnect(); swithToNextState(DOWN); } break; case DWA_EVENT: setInActiveTimer(); watchdogSent = false; break; case SEND_MSG_EVENT: try { context.sendMessage( message(event) ); } catch (Throwable e) { logger.debug("Can not send message", e); doDisconnect(); doEndConnection(); } break; default: logger.debug("Unknown event type {} in state {}", type(event), state); return false; } return true; } }, new MyState() // SUSPECT { public boolean processEvent(StateEvent event) { switch (type(event)) { case DISCONNECT_EVENT: doEndConnection(); break; case TIMEOUT_EVENT: doDisconnect(); doEndConnection(); break; case STOP_EVENT: try { context.sendDprMessage(ResultCode.SUCCESS); setInActiveTimer(); swithToNextState(STOPPING); } catch (Throwable e) { logger.debug("Can not send DPR", e); doDisconnect(); swithToNextState(DOWN); } break; case CER_EVENT: case CEA_EVENT: case DWA_EVENT: clearTimer(); swithToNextState(OKAY); break; case DPR_EVENT: try { int code = context.processDprMessage((IMessage) event.getData()); context.sendDpaMessage( message(event), code, null); } catch (Throwable e) { logger.debug("Can not send DPA", e); } doDisconnect(); swithToNextState(DOWN); break; case DWR_EVENT: try { int code = context.processDwrMessage((IMessage) event.getData()); context.sendDwaMessage( message(event),code, null); swithToNextState(OKAY); } catch (Throwable e) { logger.debug("Can not send DWA", e); doDisconnect(); swithToNextState(DOWN); } break; case RECEIVE_MSG_EVENT: clearTimer(); context.receiveMessage( message(event) ); swithToNextState(OKAY); break; case SEND_MSG_EVENT: // todo buffering throw new IllegalStateException("Connection is down"); default: logger.debug("Unknown event type {} in state {}", type(event), state); return false; } return true; } }, new MyState() // DOWN { public void entryAction() { setTimer(0); } public boolean processEvent(StateEvent event) { switch (type(event)) { case START_EVENT: try { if ( !context.isConnected() ) { context.connect(); } context.sendCerMessage(); setTimer(CEA_TIMEOUT); swithToNextState(INITIAL); } catch (Throwable e) { logger.debug( "Connect error", e); doEndConnection(); } break; case CER_EVENT: int resultCode = context.processCerMessage(key(event), message(event)); if ( resultCode == ResultCode.SUCCESS ) { try { context.sendCeaMessage(resultCode, message(event), null); swithToNextState(OKAY); } catch (Exception e) { logger.debug("Failed to send CEA.", e); doDisconnect(); // ! doEndConnection(); } } else { try { context.sendCeaMessage(resultCode, message(event), null); } catch (Exception e) { logger.debug("Failed to send CEA.", e); } doDisconnect(); // ! doEndConnection(); } break; case SEND_MSG_EVENT: // todo buffering throw new IllegalStateException("Connection is down"); case STOP_EVENT: case TIMEOUT_EVENT: case DISCONNECT_EVENT: break; default: logger.debug("Unknown event type {} in state {}", type(event), state); return false; } return true; } }, new MyState() // REOPEN { public boolean processEvent(StateEvent event) { switch (type(event)) { case CONNECT_EVENT: try { context.sendCerMessage(); setTimer(CEA_TIMEOUT); swithToNextState(INITIAL); } catch(Throwable e) { logger.debug("Can not send CER", e); setTimer(REC_TIMEOUT); } break; case TIMEOUT_EVENT: try { context.connect(); } catch (Exception e) { logger.debug("Can not connect to remote peer", e); setTimer(REC_TIMEOUT); } break; case STOP_EVENT: setTimer(0); doDisconnect(); swithToNextState(DOWN); break; case DISCONNECT_EVENT: break; case SEND_MSG_EVENT: // todo buffering throw new IllegalStateException("Connection is down"); default: logger.debug("Unknown event type {} in state {}", type(event), state); return false; } return true; } }, new MyState() // INITIAL { public void entryAction() { setTimer(CEA_TIMEOUT); } public boolean processEvent(StateEvent event) { switch (type(event)) { case DISCONNECT_EVENT: setTimer(0); doEndConnection(); break; case TIMEOUT_EVENT: doDisconnect(); doEndConnection(); break; case STOP_EVENT: setTimer(0); doDisconnect(); swithToNextState(DOWN); break; case CEA_EVENT: setTimer(0); if ( context.processCeaMessage(key(event), message(event)) ) { swithToNextState(OKAY); } else { doDisconnect(); // ! doEndConnection(); } break; case CER_EVENT: int resultCode = context.processCerMessage(key(event), message(event)); if ( resultCode == ResultCode.SUCCESS ) { try { context.sendCeaMessage(resultCode, message(event), null); swithToNextState(OKAY); // if other connection is win } catch (Exception e) { logger.debug("Can not send CEA", e); doDisconnect(); doEndConnection(); } } else if (resultCode == -1 || resultCode == ResultCode.NO_COMMON_APPLICATION) { doDisconnect(); doEndConnection(); } break; case SEND_MSG_EVENT: // todo buffering throw new IllegalStateException("Connection is down"); default: logger.debug("Unknown event type {} in state {}", type(event), state); return false; } return true; } }, new MyState() // STOPPING { public boolean processEvent(StateEvent event) { switch (type(event)) { case TIMEOUT_EVENT: case DPA_EVENT: swithToNextState(DOWN); break; case RECEIVE_MSG_EVENT: context.receiveMessage( message(event) ); break; case SEND_MSG_EVENT: throw new IllegalStateException("Stack now is stopping"); case STOP_EVENT: case DISCONNECT_EVENT: break; default: logger.debug("Unknown event type {} in state {}", type(event), state); return false; } return true; } } }; } return states; } }