/***************************************************************** 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 Fipa-Contract-Net protocol. @author Giovanni Caire - TILAB */ public class SSContractNetResponder extends SSResponder { /** Key to retrieve from the DataStore of the behaviour the last received CFP ACLMessage */ public final String CFP_KEY = INITIATION_KEY; /** Key to retrieve from the DataStore of the behaviour the last sent PROPOSE ACLMessage */ public final String PROPOSE_KEY = REPLY_KEY; /** Key to retrieve from the DataStore of the behaviour the last received ACCEPT_PROPOSAL ACLMessage */ public final String ACCEPT_PROPOSAL_KEY = RECEIVED_KEY; /** Key to retrieve from the DataStore of the behaviour the last received REJECT_PROPOSAL ACLMessage */ public final String REJECT_PROPOSAL_KEY = RECEIVED_KEY; public static final String HANDLE_CFP = "Handle-Cfp"; public static final String HANDLE_ACCEPT_PROPOSAL = "Handle-Accept-Proposal"; public static final String HANDLE_REJECT_PROPOSAL = "Handle-Reject-Proposal"; private boolean proposeSent = false; /** Construct a SSContractNetResponder that is activated by the reception of a given initiation CFP message. */ public SSContractNetResponder(Agent a, ACLMessage cfp) { this(a, cfp, new DataStore()); } /** Construct a SSContractNetResponder that is activated by the reception of a given initiation CFP message and uses a given DataStore. */ public SSContractNetResponder(Agent a, ACLMessage cfp, DataStore store) { super(a, cfp, store, true); registerDefaultTransition(HANDLE_CFP, SEND_REPLY); registerTransition(SEND_REPLY, RECEIVE_NEXT, ACLMessage.PROPOSE); registerTransition(RECEIVE_NEXT, HANDLE_REJECT_PROPOSAL, MsgReceiver.TIMEOUT_EXPIRED); registerTransition(CHECK_IN_SEQ, HANDLE_ACCEPT_PROPOSAL, ACLMessage.ACCEPT_PROPOSAL, new String[]{SEND_REPLY}); registerTransition(CHECK_IN_SEQ, HANDLE_REJECT_PROPOSAL, ACLMessage.REJECT_PROPOSAL); registerDefaultTransition(HANDLE_ACCEPT_PROPOSAL, SEND_REPLY); registerDefaultTransition(HANDLE_REJECT_PROPOSAL, DUMMY_FINAL); Behaviour b; // HANDLE_CFP b = new CfpHandler(myAgent); registerFirstState(b, HANDLE_CFP); b.setDataStore(getDataStore()); // HANDLE_ACCEPT_PROPOSAL b = new AcceptHandler(myAgent); registerDSState(b, HANDLE_ACCEPT_PROPOSAL); // HANDLE_REJECT_PROPOSAL b = new RejectHandler(myAgent); registerDSState(b, HANDLE_REJECT_PROPOSAL); } /** This method is called to handle the initial CFP message. This default implementation does nothing and returns null. Programmers have to override it to react to this event. @param cfp the initial CFP message to handle. @return the reply message to be sent back to the initiator. Returning a message defferent from PROPOSE (or returning null) terminates the protocol. @exception RefuseException if the CFP 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 CFP. Throwing a FailureException has the same effect as returning a FAILURE message, but automatically manages the <code>:content</code> slot. @exception NotUnderstoodException if the CFP 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 handleCfp(ACLMessage cfp) throws RefuseException, FailureException, NotUnderstoodException { return null; } /** This method is called when an ACCEPT_PROPOSAL message is received from the initiator. This default implementation does nothing and returns null. Programmers have to override it to react to this event. @param cfp the initial CFP message. @param propose the PROPOSE message sent back as reply to the initial CFP message. @param accept the received ACCEPT_PROPOSAL message. @return the reply message to be sent back to the initiator. @exception FailureException if there is an error serving the ACCEPT_PROPOSAL. Throwing a FailureException has the same effect as returning a FAILURE message, but automatically manages the <code>:content</code> slot. */ protected ACLMessage handleAcceptProposal(ACLMessage cfp, ACLMessage propose, ACLMessage accept) throws FailureException { return null; } /** This method is called when a REJECT_PROPOSAL message is received from the initiator. This default implementation does nothing. Programmers may override it to react to this event. @param cfp the initial CFP message. @param propose the PROPOSE message sent back as reply to the initial CFP message. @param reject the received REJECT_PROPOSAL message or null if no acceptance message is received from the initiator within the timeout specified in the <code>:reply-by</code> slot of the PROPOSE message. */ protected void handleRejectProposal(ACLMessage cfp, ACLMessage propose, ACLMessage reject) { } /** 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 cfp the initial CFP message. @param propose the PROPOSE message sent back as reply to the initial CFP message. @param msg the received out-of-sequence message. */ protected void handleOutOfSequence(ACLMessage cfp,ACLMessage propose,ACLMessage msg){ } /** This method allows to register a user defined <code>Behaviour</code> in the HANDLE_CFP 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 initial <code>CFP</code> message from the datastore at the <code>CFP_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 PROPOSE (or putting no message) terminates the protocol. @param b the Behaviour that will handle this state */ public void registerHandleCfp(Behaviour b) { registerFirstState(b, HANDLE_CFP); b.setDataStore(getDataStore()); } /** This method allows to register a user defined <code>Behaviour</code> in the HANDLE_ACCEPT_PROPOSAL 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>ACCEPT_PROPOSAL</code> message from the datastore at the <code>ACCEPT_PROPOSAL_KEY</code> key, the initial <code>CFP</code> message at the <code>CFP_KEY</code> and the previously sent <code>PROPOSE</code> message at the <code>PROPOSE_KEY</code>. <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. @param b the Behaviour that will handle this state */ public void registerHandleAcceptProposal(Behaviour b) { registerDSState(b, HANDLE_ACCEPT_PROPOSAL); } /** This method allows to register a user defined <code>Behaviour</code> in the HANDLE_REJECT_PROPOSAL 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>REJECT_PROPOSAL</code> message from the datastore at the <code>REJECT_PROPOSAL_KEY</code> key, the initial <code>CFP</code> message at the <code>CFP_KEY</code> and the previously sent <code>PROPOSE</code> message at the <code>PROPOSE_KEY</code>. @param b the Behaviour that will handle this state */ public void registerHandleRejectProposal(Behaviour b) { registerDSState(b, HANDLE_REJECT_PROPOSAL); } /** Re-initialize the internal state without performing a complete reset. */ protected void reinit() { proposeSent = false; getDataStore().remove(CFP_KEY); super.reinit(); } //#APIDOC_EXCLUDE_BEGIN /** Redefine this method to call the overloaded version with 3 parameters. */ protected void handleOutOfSequence(ACLMessage msg) { ACLMessage cfp = (ACLMessage) getDataStore().get(CFP_KEY); ACLMessage propose = (ACLMessage) getDataStore().get(PROPOSE_KEY); handleOutOfSequence(cfp, propose, msg); } protected boolean checkInSequence(ACLMessage received) { return received.getPerformative() == ACLMessage.ACCEPT_PROPOSAL || received.getPerformative() == ACLMessage.REJECT_PROPOSAL; } protected void beforeReply(ACLMessage reply) { if (proposeSent) { // If this is the reply to an ACCEPT_PROPOSAL force the // protocol termination to avoid (in case the user erroneously // sent back another PROPOSE message) ending up in a MsgReceiver // state that will never exit. forceTransitionTo(DUMMY_FINAL); } } protected void afterReply(ACLMessage reply) { if (reply != null && reply.getPerformative() == ACLMessage.PROPOSE) { proposeSent = true; } } //#APIDOC_EXCLUDE_END /** Inner class CfpHandler */ private static class CfpHandler extends OneShotBehaviour { private static final long serialVersionUID = 4766407563773001L; public CfpHandler(Agent a) { super(a); } public void action() { SSContractNetResponder parent = (SSContractNetResponder) getParent(); ACLMessage reply = null; try { reply = parent.handleCfp((ACLMessage) getDataStore().get(parent.CFP_KEY)); } catch (FIPAException fe) { reply = fe.getACLMessage(); } getDataStore().put(parent.REPLY_KEY, reply); } } // End of inner class CfpHandler /** Inner class AcceptHandler */ private static class AcceptHandler extends OneShotBehaviour { private static final long serialVersionUID = 4766407563773002L; public AcceptHandler(Agent a) { super(a); } public void action() { SSContractNetResponder parent = (SSContractNetResponder) getParent(); ACLMessage reply = null; try { ACLMessage cfp = (ACLMessage) getDataStore().get(parent.CFP_KEY); ACLMessage propose = (ACLMessage) getDataStore().get(parent.PROPOSE_KEY); ACLMessage accept = (ACLMessage) getDataStore().get(parent.ACCEPT_PROPOSAL_KEY); reply = parent.handleAcceptProposal(cfp, propose, accept); } catch (FIPAException fe) { reply = fe.getACLMessage(); } getDataStore().put(parent.REPLY_KEY, reply); } } // End of inner class AcceptHandler /** Inner class RejectHandler */ private static class RejectHandler extends OneShotBehaviour { private static final long serialVersionUID = 4766407563773003L; public RejectHandler(Agent a) { super(a); } public void action() { SSContractNetResponder parent = (SSContractNetResponder) getParent(); ACLMessage cfp = (ACLMessage) getDataStore().get(parent.CFP_KEY); ACLMessage propose = (ACLMessage) getDataStore().get(parent.PROPOSE_KEY); ACLMessage reject = (ACLMessage) getDataStore().get(parent.REJECT_PROPOSAL_KEY); parent.handleRejectProposal(cfp, propose, reject); } } // End of inner class RejectHandler }