/*****************************************************************
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
}