/*
* eID Applet Project.
* Copyright (C) 2008-2009 FedICT.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License version
* 3.0 as published by the Free Software Foundation.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, see
* http://www.gnu.org/licenses/.
*/
package be.fedict.eid.applet.shared.protocol;
import java.util.LinkedList;
import java.util.List;
import be.fedict.eid.applet.shared.annotation.ProtocolStateAllowed;
import be.fedict.eid.applet.shared.annotation.StartRequestMessage;
import be.fedict.eid.applet.shared.annotation.StateTransition;
import be.fedict.eid.applet.shared.annotation.StopResponseMessage;
/**
* Protocol State Machine.
*
* @author Frank Cornelis
*
*/
public class ProtocolStateMachine {
private final ProtocolContext protocolContext;
private final List<ProtocolStateListener> protocolStateListeners;
/**
* Main constructor.
*
* @param protocolContext
*/
public ProtocolStateMachine(ProtocolContext protocolContext) {
this.protocolContext = protocolContext;
this.protocolStateListeners = new LinkedList<ProtocolStateListener>();
}
/**
* Adds a protocol state listener.
*
* @param protocolStateListener
*/
public void addProtocolStateListener(ProtocolStateListener protocolStateListener) {
this.protocolStateListeners.add(protocolStateListener);
}
/**
* Checks the given response message against the protocol state rules.
*
* @param responseMessage
* @throws ServletException
*/
public void checkResponseMessage(Object responseMessage) {
ProtocolState protocolState = this.protocolContext.getProtocolState();
if (null == protocolState) {
throw new RuntimeException("responding without a protocol state");
}
Class<?> responseMessageClass = responseMessage.getClass();
StopResponseMessage stopResponseMessageAnnotation = responseMessageClass
.getAnnotation(StopResponseMessage.class);
if (null != stopResponseMessageAnnotation) {
notifyProtocolListenersStopProtocolRun();
this.protocolContext.removeProtocolState();
}
StateTransition stateTransitionAnnotation = responseMessageClass.getAnnotation(StateTransition.class);
if (null != stateTransitionAnnotation) {
ProtocolState newProtocolState = stateTransitionAnnotation.value();
this.protocolContext.setProtocolState(newProtocolState);
notifyProtocolListenersProtocolStateTransition(newProtocolState);
}
}
private void notifyProtocolListenersProtocolStateTransition(ProtocolState newProtocolState) {
for (ProtocolStateListener protocolStateListener : this.protocolStateListeners) {
protocolStateListener.protocolStateTransition(newProtocolState);
}
}
private void notifyProtocolListenersStartProtocolRun() {
for (ProtocolStateListener protocolStateListener : this.protocolStateListeners) {
protocolStateListener.startProtocolRun();
}
}
private void notifyProtocolListenersStopProtocolRun() {
for (ProtocolStateListener protocolStateListener : this.protocolStateListeners) {
protocolStateListener.stopProtocolRun();
}
}
/**
* Checks the given request message against protocol state rules.
*
* @param requestMessage
* @throws ServletException
*/
public void checkRequestMessage(Object requestMessage) {
// TODO return some non-runtime exception
ProtocolState protocolState = this.protocolContext.getProtocolState();
Class<?> requestMessageClass = requestMessage.getClass();
StartRequestMessage startRequestMessageAnnotation = requestMessageClass
.getAnnotation(StartRequestMessage.class);
if (null == startRequestMessageAnnotation) {
if (null == protocolState) {
throw new RuntimeException("expected a protocol start message");
}
ProtocolStateAllowed protocolStateAllowedAnnotation = requestMessageClass
.getAnnotation(ProtocolStateAllowed.class);
if (null == protocolStateAllowedAnnotation) {
throw new RuntimeException(
"cannot check protocol state for message: " + requestMessageClass.getSimpleName());
}
ProtocolState allowedProtocolState = protocolStateAllowedAnnotation.value();
if (protocolState != allowedProtocolState) {
throw new RuntimeException(
"protocol state incorrect. expected: " + allowedProtocolState + "; actual: " + protocolState);
}
} else {
if (null != protocolState) {
/*
* Throwing an exception here might be to strict since we want
* to allow easy recovery from a crashed eID Applet. I.e. no
* need to restart the web browser.
*/
}
ProtocolState initialState = startRequestMessageAnnotation.value();
this.protocolContext.setProtocolState(initialState);
notifyProtocolListenersStartProtocolRun();
notifyProtocolListenersProtocolStateTransition(initialState);
}
}
}