/***************************************************************** JADE - Java Agent DEvelopment Framework is a framework to develop multi-agent systems in compliance with the FIPA specifications. Copyright (C) 2000 CSELT S.p.A. GNU Lesser General Public License This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, version 2.1 of the License. This library 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 library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *****************************************************************/ package jade.proto; //#CUSTOM_EXCLUDE_FILE import jade.core.*; import jade.core.behaviours.*; import jade.lang.acl.*; import jade.proto.states.*; import jade.domain.FIPAException; import jade.domain.FIPAAgentManagement.RefuseException; import jade.domain.FIPAAgentManagement.FailureException; import jade.domain.FIPAAgentManagement.NotUnderstoodException; /** Single Session version of the Responder role in the Iterated-Fipa-Request protocol. @author Giovanni Caire - TILAB */ public class SSIteratedAchieveREResponder extends SSResponder { /** Key to retrieve from the DataStore of the behaviour the last received REQUEST ACLMessage */ public final String REQUEST_KEY = RECEIVED_KEY; /** Key to retrieve from the DataStore of the behaviour the last received CANCEL ACLMessage */ public final String CANCEL_KEY = RECEIVED_KEY; public static final String HANDLE_REQUEST = "Handle-Request"; public static final String HANDLE_CANCEL = "Handle-Cancel"; static final String ACL_USERDEF_TERMINATED_SESSION = "iterated-fipa-request-terminated-session"; private boolean sessionClosed = false; private int initiationPerformative; /** Construct a SSIteratedAchieveREResponder that is activated by the reception of a given initiation REQUEST message. */ public SSIteratedAchieveREResponder(Agent a, ACLMessage request) { this(a, request, new DataStore()); } /** Construct a SSIteratedAchieveREResponder that is activated by the reception of a given initiation REQUEST message and uses a given DataStore. */ public SSIteratedAchieveREResponder(Agent a, ACLMessage request, DataStore store) { // 4th parameter is false since in this protocol we treat the initiation message exactly as all subsequent incoming messages super(a, request, store, false); initiationPerformative = request.getPerformative(); registerDefaultTransition(HANDLE_REQUEST, SEND_REPLY); registerTransition(SEND_REPLY, RECEIVE_NEXT, ACLMessage.INFORM); registerTransition(RECEIVE_NEXT, HANDLE_CANCEL, MsgReceiver.TIMEOUT_EXPIRED); registerTransition(CHECK_IN_SEQ, HANDLE_REQUEST, initiationPerformative, new String[]{HANDLE_REQUEST, SEND_REPLY, RECEIVE_NEXT, CHECK_IN_SEQ}); registerTransition(CHECK_IN_SEQ, HANDLE_CANCEL, ACLMessage.CANCEL); registerDefaultTransition(HANDLE_CANCEL, DUMMY_FINAL); Behaviour b; // HANDLE_REQUEST b = new RequestHandler(myAgent); registerFirstState(b, HANDLE_REQUEST); b.setDataStore(getDataStore()); // HANDLE_CANCEL b = new CancelHandler(myAgent); registerDSState(b, HANDLE_CANCEL); } /** This method is called to handle the initial REQUEST message and then again whenever a REQUEST message is received. This default implementation does nothing and returns null. Programmers have to override it to react to this event. @param request the REQUEST message to handle. @return the reply message to be sent back to the initiator. Returning a message defferent from INFORM (or returning null) terminates the protocol. An optional AGREE message can be sent back to the initiator by calling the <code>sendAgree()</code> method. @exception RefuseException if the REQUEST is refused. Throwing a RefuseException has the same effect as returning a REFUSE message, but automatically manages the <code>:content</code> slot. @exception FailureException if there is an error serving the REQUEST. Throwing a FailureException has the same effect as returning a FAILURE message, but automatically manages the <code>:content</code> slot. @exception NotUnderstoodException if the REQUEST content is not undrerstood. Throwing a NotUnderstoodException has the same effect as returning a NOT_UNDERSTOOD message, but automatically manages the <code>:content</code> slot. */ protected ACLMessage handleRequest(ACLMessage request) throws RefuseException, FailureException, NotUnderstoodException { return null; } /** This method is called when a CANCEL message is received from the initiator. This default implementation does nothing. Programmers may override it to react to this event. @param cancel the received CANCEL message or null if no further REQUEST message is received from the initiator within the timeout specified in the <code>:reply-by</code> slot of the last INFORM message. */ protected void handleCancel(ACLMessage cancel) { } /** This method allows to register a user defined <code>Behaviour</code> in the HANDLE_REQUEST state. This behaviour would override the homonymous method. This method also sets the data store of the registered <code>Behaviour</code> to the DataStore of this current behaviour. <br> The registered behaviour can retrieve the received <code>REQUEST</code> message from the datastore at the <code>REQUEST_KEY</code> key. <br> It is responsibility of the registered behaviour to put the reply to be sent back to the initiator into the datastore at the <code>REPLY_KEY</code> key. Putting a message defferent from INFORM (or null) terminates the protocol. An optional AGREE message can be sent back to the initiator by calling the <code>sendAgree()</code> method. @param b the Behaviour that will handle this state */ public void registerHandleRequest(Behaviour b) { registerFirstState(b, HANDLE_REQUEST); b.setDataStore(getDataStore()); } /** This method allows to register a user defined <code>Behaviour</code> in the HANDLE_CANCEL state. This behaviour would override the homonymous method. This method also sets the data store of the registered <code>Behaviour</code> to the DataStore of this current behaviour. <br> The registered behaviour can retrieve the <code>CANCEL</code> message received from the datastore at the <code>CANCEL_KEY</code> key. <br> @param b the Behaviour that will handle this state */ public void registerHandleCancel(Behaviour b) { registerDSState(b, HANDLE_CANCEL); } /** Utility method to send an optional AGREE message back to the initiator ensuring that all protocol fields are properly set. */ public void sendAgree(ACLMessage agree) { agree.setPerformative(ACLMessage.AGREE); ReplySender.adjustReply(myAgent, agree, (ACLMessage) getDataStore().get(REQUEST_KEY)); myAgent.send(agree); } /** Close the ongoing session, as soon as the next INFORM will be sent back to the initiator without the need for an explicit CANCEL message. The initiator will be able to detect that the session has been closed by calling the <code>isSessionTerminated()</code> method of the <code>SSIteratedAchieveREInitiator</code> class. */ public void closeSessionOnNextReply() { sessionClosed = true; } /** Reset this protocol behaviour */ public void reset() { sessionClosed = false; super.reset(); } //#APIDOC_EXCLUDE_BEGIN protected boolean checkInSequence(ACLMessage received) { return received.getPerformative() == initiationPerformative || received.getPerformative() == ACLMessage.CANCEL; } protected void beforeReply(ACLMessage reply) { if (sessionClosed && reply != null) { // Set the user defined parameter that will allow the // initiator to detect that the session has been closed reply.addUserDefinedParameter(ACL_USERDEF_TERMINATED_SESSION, String.valueOf(true)); // Terminate the protocol by forcing a transition to the // DUMMY_FINAL state forceTransitionTo(DUMMY_FINAL); } } //#APIDOC_EXCLUDE_END /** Inner class RequestHandler */ private static class RequestHandler extends OneShotBehaviour { private static final long serialVersionUID = 5463827646358001L; public RequestHandler(Agent a) { super(a); } public void action() { SSIteratedAchieveREResponder parent = (SSIteratedAchieveREResponder) getParent(); ACLMessage reply = null; try { reply = parent.handleRequest((ACLMessage) getDataStore().get(parent.REQUEST_KEY)); } catch (FIPAException fe) { reply = fe.getACLMessage(); } getDataStore().put(parent.REPLY_KEY, reply); } } // End of inner class RequestHandler /** Inner class CancelHandler */ private static class CancelHandler extends OneShotBehaviour { private static final long serialVersionUID = 5463827646358002L; public CancelHandler(Agent a) { super(a); } public void action() { SSIteratedAchieveREResponder parent = (SSIteratedAchieveREResponder) getParent(); parent.handleCancel((ACLMessage) getDataStore().get(parent.CANCEL_KEY)); } } // End of inner class CancelHandler }