/***************************************************************** 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 java.util.Date; /** Common base class for all classes implementing the Single Session version of the Responder role in interaction protocols where the responder is expected to receive more than one message from the initiator and reply to each of them. @author Giovanni Caire - TILAB */ abstract class SSResponder extends FSMBehaviour { /** Key to retrieve from the DataStore of the behaviour the initiation ACLMessage that triggered this responder session */ public final String INITIATION_KEY = "__Initiation_key" + hashCode(); /** 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 static final int OUT_OF_SEQUENCE_EXIT_CODE = -98765; // Very strange number //#APIDOC_EXCLUDE_BEGIN // FSM states names protected static final String RECEIVE_NEXT = "Receive-Next"; protected static final String CHECK_IN_SEQ = "Check-In-seq"; protected static final String HANDLE_OUT_OF_SEQUENCE = "Handle-Out-of-seq"; protected static final String SEND_REPLY = "Send-Reply"; protected static final String DUMMY_FINAL = "Dummy-Final"; private ACLMessage initiation; private String initiationKey; /** */ public SSResponder(Agent a, ACLMessage initiation, DataStore store, boolean useInitiationKey) { super(a); setDataStore(store); this.initiation = initiation; initiationKey = (useInitiationKey ? INITIATION_KEY : RECEIVED_KEY); registerDefaultTransition(RECEIVE_NEXT, CHECK_IN_SEQ); registerTransition(CHECK_IN_SEQ, HANDLE_OUT_OF_SEQUENCE, OUT_OF_SEQUENCE_EXIT_CODE); registerDefaultTransition(HANDLE_OUT_OF_SEQUENCE, RECEIVE_NEXT, new String[] {HANDLE_OUT_OF_SEQUENCE}); registerDefaultTransition(SEND_REPLY, DUMMY_FINAL); Behaviour b; // RECEIVE_NEXT b = new NextMsgReceiver(myAgent, getDataStore(), RECEIVED_KEY); registerState(b, RECEIVE_NEXT); // CHECK_IN_SEQ b = new SeqChecker(myAgent); registerDSState(b, CHECK_IN_SEQ); // HANDLE_OUT_OF_SEQUENCE b = new OutOfSeqHandler(myAgent); registerDSState(b, HANDLE_OUT_OF_SEQUENCE); // SEND_REPLY b = new NextReplySender(myAgent, REPLY_KEY, initiationKey); registerDSState(b, SEND_REPLY); // DUMMY_FINAL b = new DummyFinal(myAgent); registerLastState(b, DUMMY_FINAL); b.setDataStore(getDataStore()); } public void onStart() { getDataStore().put(initiationKey, initiation); super.onStart(); } //#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() { reinit(); super.reset(); } /** Re-initialize the internal state without performing a complete reset. */ protected void reinit() { DataStore ds = getDataStore(); ds.remove(RECEIVED_KEY); ds.remove(REPLY_KEY); setMessageToReplyKey(initiationKey); } //#APIDOC_EXCLUDE_BEGIN /** Check whether a received message complies with the protocol rules. */ protected boolean checkInSequence(ACLMessage received) { return false; } /** This method can be redefined by protocol specific implementations to customize a reply that is going to be sent back to the initiator. This default implementation does nothing. */ protected void beforeReply(ACLMessage reply) { } /** This method can be redefined by protocol specific implementations to update the status of the protocol just after a reply has been sent. This default implementation does nothing. */ protected void afterReply(ACLMessage reply) { } /** This method can be redefined by protocol specific implementations to take proper actions after the completion of the current protocol session. */ protected void sessionTerminated() { } /** 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 private void setMessageToReplyKey(String key) { ReplySender rs = (ReplySender) getState(SEND_REPLY); rs.setMsgKey(key); } /** Inner class NextMsgReceiver */ private static class NextMsgReceiver extends MsgReceiver { private static final long serialVersionUID = 4487495895818001L; public NextMsgReceiver(Agent a, DataStore ds, String key) { super(a, null, INFINITE, ds, key); } public int onEnd() { // The next reply (if any) will be a reply to the received message SSResponder parent = (SSResponder) getParent(); parent.setMessageToReplyKey((String) receivedMsgKey); return super.onEnd(); } } // End of inner class NextMsgReceiver /** Inner class SeqChecker */ private static class SeqChecker extends OneShotBehaviour { private int ret; private static final long serialVersionUID = 4487495895818002L; public SeqChecker(Agent a) { super(a); } public void action() { SSResponder parent = (SSResponder) getParent(); ACLMessage received = (ACLMessage)getDataStore().get(parent.RECEIVED_KEY); if (received != null && parent.checkInSequence(received)) { ret = received.getPerformative(); } else { ret = OUT_OF_SEQUENCE_EXIT_CODE; } } public int onEnd() { return ret; } } // End of inner class SeqChecker /** Inner class OutOfSeqHandler */ private static class OutOfSeqHandler extends OneShotBehaviour { private static final long serialVersionUID = 4487495895818003L; public OutOfSeqHandler(Agent a) { super(a); } public void action() { SSResponder parent = (SSResponder) getParent(); parent.handleOutOfSequence((ACLMessage)getDataStore().get(parent.RECEIVED_KEY)); } } // End of inner class OutOfSeqHandler /** Inner class NextReplySender */ private static class NextReplySender extends ReplySender { private static final long serialVersionUID = 4487495895818004L; public NextReplySender(Agent a, String replyKey, String msgKey) { super(a, replyKey, msgKey); } public void onStart() { SSResponder parent = (SSResponder) getParent(); ACLMessage reply = (ACLMessage)getDataStore().get(parent.REPLY_KEY); parent.beforeReply(reply); } public int onEnd() { int ret = super.onEnd(); SSResponder parent = (SSResponder) getParent(); // If a reply was sent back, adjust the template and deadline of the // RECEIVE_NEXT state ACLMessage reply = (ACLMessage)getDataStore().get(parent.REPLY_KEY); if (reply != null) { MsgReceiver mr = (MsgReceiver) parent.getState(RECEIVE_NEXT); mr.setTemplate(createNextMsgTemplate(reply)); Date d = reply.getReplyByDate(); if (d != null && d.getTime() > System.currentTimeMillis()) { mr.setDeadline(d.getTime()); } else { mr.setDeadline(MsgReceiver.INFINITE); } } parent.afterReply(reply); return ret; } private MessageTemplate createNextMsgTemplate(ACLMessage reply) { return MessageTemplate.and( MessageTemplate.MatchConversationId(reply.getConversationId()), MessageTemplate.not(MessageTemplate.MatchCustom(reply, true))); } } // End of inner class NextReplySender /** Inner class DummyFinal */ private static class DummyFinal extends OneShotBehaviour { private static final long serialVersionUID = 4487495895818005L; public DummyFinal(Agent a) { super(a); } public void action() { SSResponder parent = (SSResponder) getParent(); parent.sessionTerminated(); } } // End of inner class DummyFinal }