/***************************************************************** 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; //#MIDP_EXCLUDE_FILE import jade.core.*; import jade.core.behaviours.*; import jade.lang.acl.*; import jade.proto.states.*; import java.util.Date; /** Common base class for all classes implementing the Responder role in interaction protocols where the responder is expected to receive more than one message from the initiator and reply to them. @author Elena Quarantotto - TILAB @author Giovanni Caire - TILAB */ abstract class Responder extends FSMBehaviour { // Data store keys /** Key to retrieve from the DataStore of the behaviour the last received ACLMessage */ public final String RECEIVED_KEY = "__Received_key" + hashCode(); /** Key to set into the DataStore of the behaviour the new ACLMessage to be sent back to the initiator as a reply. */ public final String REPLY_KEY = "__Reply_key" + hashCode(); // private inner classes for the FSM states private static class CfpReceiver extends MsgReceiver { public CfpReceiver(Agent myAgent, MessageTemplate mt, long deadline, DataStore s, Object msgKey) { super(myAgent, mt, deadline, s, msgKey); } // For persistence service private CfpReceiver() { } public int onEnd() { Responder fsm = (Responder)getParent(); MsgReceiver nextRecv = (MsgReceiver)fsm.getState(RECEIVE_NEXT); // Set the template to receive next messages ACLMessage received = (ACLMessage)getDataStore().get(fsm.RECEIVED_KEY); nextRecv.setTemplate(MessageTemplate.MatchConversationId(received.getConversationId())); return super.onEnd(); } } // End of CfpReceiver class private static class NextReceiver extends MsgReceiver { public NextReceiver(Agent myAgent, MessageTemplate mt, long deadline, DataStore s, Object msgKey) { super(myAgent, mt, deadline, s, msgKey); } // For persistence service private NextReceiver() { } public void onStart() { // Set the deadline for receiving the next message on the basis // of the last reply sent Responder fsm = (Responder)getParent(); ACLMessage reply = (ACLMessage)getDataStore().get(fsm.REPLY_KEY); if (reply != null) { Date d = reply.getReplyByDate(); if (d != null && d.getTime() > System.currentTimeMillis()) { setDeadline(d.getTime()); } } } } // End of NextReceiver class private static class CheckInSeq extends OneShotBehaviour { private int ret; private static final long serialVersionUID = 4487495895818000L; public CheckInSeq(Agent a) { super(a); } // For persistence service private CheckInSeq() { } public void action() { Responder fsm = (Responder)getParent(); ACLMessage received = (ACLMessage)getDataStore().get(fsm.RECEIVED_KEY); if (fsm.checkInSequence(received)) { ret = received.getPerformative(); } else { ret = -1; } } public int onEnd() { return ret; } } // End of CheckInSeq class private static class HandleOutOfSeq extends OneShotBehaviour { private static final long serialVersionUID = 4487495895818005L; public HandleOutOfSeq(Agent a) { super(a); } // For persistence service private HandleOutOfSeq() { } public void action() { Responder fsm = (Responder)getParent(); fsm.handleOutOfSequence((ACLMessage)getDataStore().get(fsm.RECEIVED_KEY)); } } // End of HandleOutOfSeq class private static class SendReply extends ReplySender { public SendReply(Agent a, String replyKey, String msgKey) { super(a, replyKey, msgKey); } // For persistence service private SendReply() { } public int onEnd() { int ret = super.onEnd(); Responder fsm = (Responder)getParent(); fsm.replySent(ret); return ret; } } // End of SendReply class //#APIDOC_EXCLUDE_BEGIN // FSM states names protected static final String RECEIVE_INITIATION = "Receive-Initiation"; protected static final String RECEIVE_NEXT = "Receive-Next"; protected static final String HANDLE_OUT_OF_SEQUENCE = "Handle-Out-of-seq"; protected static final String CHECK_IN_SEQ = "Check-In-seq"; protected static final String SEND_REPLY = "Send-Reply"; /** * Constructor of the behaviour that creates a new empty DataStore * @see #Responder(Agent a, MessageTemplate mt, DataStore store) **/ public Responder(Agent a, MessageTemplate mt) { this(a, mt, new DataStore()); } /** * Constructor of the behaviour. * @param a is the reference to the Agent object * @param mt is the MessageTemplate that must be used to match * the initiation message. Take care that if mt is null every message is * consumed by this protocol. * @param store the DataStore for this protocol behaviour **/ public Responder(Agent a, MessageTemplate mt, DataStore store) { super(a); setDataStore(store); registerDefaultTransition(RECEIVE_INITIATION, CHECK_IN_SEQ); registerDefaultTransition(RECEIVE_NEXT, CHECK_IN_SEQ); registerDefaultTransition(CHECK_IN_SEQ, HANDLE_OUT_OF_SEQUENCE); registerDefaultTransition(HANDLE_OUT_OF_SEQUENCE, RECEIVE_NEXT, new String[] {HANDLE_OUT_OF_SEQUENCE}); Behaviour b; // RECEIVE_INITIATION b = new CfpReceiver(myAgent, mt, -1, getDataStore(), RECEIVED_KEY); registerFirstState(b, RECEIVE_INITIATION); // RECEIVE_NEXT b = new NextReceiver(myAgent, null, -1, getDataStore(), RECEIVED_KEY); registerState(b, RECEIVE_NEXT); // CHECK_IN_SEQ b = new CheckInSeq(myAgent); registerDSState(b, CHECK_IN_SEQ); // HANDLE_OUT_OF_SEQUENCE b = new HandleOutOfSeq(myAgent); registerDSState(b, HANDLE_OUT_OF_SEQUENCE); // SEND_REPLY b = new SendReply(myAgent, REPLY_KEY, RECEIVED_KEY); registerDSState(b, SEND_REPLY); } // For persistence service private Responder() { } //#APIDOC_EXCLUDE_END /** This method is called whenever a message is received that does not comply to the protocol rules. This default implementation does nothing. Programmers may override it in case they need to react to this event. @param msg the received out-of-sequence message. */ protected void handleOutOfSequence(ACLMessage msg) { } /** This method allows to register a user defined <code>Behaviour</code> in the HANDLE_OUT_OF_SEQ 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. The registered behaviour can retrieve the <code>out of sequence</code> ACLMessage object received from the datastore at the <code>RECEIVED_KEY</code> key. @param b the Behaviour that will handle this state */ public void registerHandleOutOfSequence(Behaviour b) { registerDSState(b, HANDLE_OUT_OF_SEQUENCE); } /** Reset this behaviour. */ public void reset() { super.reset(); DataStore ds = getDataStore(); ds.remove(RECEIVED_KEY); ds.remove(REPLY_KEY); } //#APIDOC_EXCLUDE_BEGIN /** Check whether a received message complies with the protocol rules. */ protected abstract boolean checkInSequence(ACLMessage received); /** This method can be redefined by protocol specific implementations to update the status of the protocol after a reply has been sent. This default implementation does nothing. */ protected void replySent(int exitValue) { } /** Utility method to register a behaviour in a state of the protocol and set the DataStore appropriately */ protected void registerDSState(Behaviour b, String name) { b.setDataStore(getDataStore()); registerState(b,name); } //#APIDOC_EXCLUDE_END }