I try to answer myself. I'm not sure it's the best solution, but certainly works.
Note that before coming to this solution I am well documented with the guides and tutorials found (on http://jade.tilab.com/), and confronting myself with other
JADE developers (in mailing lists http://jade.tilab.com/pipermail/jade-develop/)
The answer is complicated, so I'll try to be exhaustive.
In my project I have to deal with two different types of agents.
- The ShipperAgent, which represents one shipper: it keeps track of the vehicles owned by the shipper, those available, and the goods "reserved" from it.

- The BuyerAgent, which represents customers (or buyers): each customer has a list of goods that want to move from point A to point B.

The two agents are registered to the yellow pages service.
In ShipperAgent, clicking on the "SEARCH" button you start a search: start a Contract Net Interaction Protocol.
Explain the Contract Net Interaction Protocol and my case
In the standard FIPA: http://www.fipa.org/specs/fipa00029/SC00029H.html

In JADE guide can be found here: http://jade.tilab.com/doc/programmersguide.pdf (p. 35)
Further on you will notice the changes that I had to take.
The ShipperAgent sends CFP each BuyerAgent.
Each BuyerAgent:
2.1 if he has goods, send a PROPOSE to ShipperAgent.
2.2 if does not have the goods, send a REFUSE to ShipperAgent. And for buyer, the protocol ends.
Since here is easy. With the sniffer, we can observe:

Now:
The ShipperAgent:
3.1 receives one or more PROPOSE by buyers, and displays (see image below).
3.2 if it receives the REFUSE (or does not receive anything after a certain time), ending communication with those buyer.
Here's how the Shipper graphically displays the proposals:

Now it's up to the user to choose which goods wants and what not.
To achieve this, I had to create some kind of "internal communication" to the agent himself: the GUI (in 3.1), once clicked Execute, sends a message to the agent. It may seem inelegant, but it seems to be the only way to not crash the protocol side ShipperAgent.

The ShipperAgent:
4.1 if the user has selected one or more goods proposals (and click Execute), sends to the corresponding BuyerAgent an ACCEPT_PROPOSAL, where specific goods that want to (a subset of the previous proposal).
4.2 if the user does not select any good (or click on Cancel), sends to the corresponding BuyerAgent an REJECT_PROPOSAL. Ends communication to that buyer.
The BuyerAgent:
5.1 if receives an ACCEPT_PROPOSAL, check that the goods are still available (any other shippers could have them "reserved" in the meanwhile) and, if so, sends an INFORM.
5.2 if receives an ACCEPT_PROPOSAL ma one or more goods are no longer available, sends FAILURE.
5.3 if it receives an REJECT_PROPOSAL, ends communication with the ShipperAgent.

In brief this (for example):

The code
BuyerAgent.java
I create a dispatcher who is always ready to receive the CFP. As soon as it receives and start protocol, buyer-side: start SearchJobResponder.
/*
* ...
*/
final MessageTemplate template = MessageTemplate.and(
MessageTemplate.MatchProtocol(FIPANames.InteractionProtocol.FIPA_CONTRACT_NET),
MessageTemplate.MatchPerformative(ACLMessage.CFP) );
// SSResponderDispatcher:
SSResponderDispatcher dispatcher = new SSResponderDispatcher(this, template) {
BuyerAgent b = (BuyerAgent) this.myAgent;
protected Behaviour createResponder(ACLMessage initiationMsg) {
// SearchJobResponder for single cfp:
return new SearchJobResponder(b, initiationMsg);
}
};
addBehaviour(dispatcher);
/*
* ...
*/
ShipperAgent.java
Search all buyer, creates a CFP and start the protocol, shipper-side: start SearchJobInitiator.
/*
* ...
*/
ACLMessage cfp = new ACLMessage(ACLMessage.CFP);
AID[] buyerAgents = searchBuyers(); // search all buyerAgents
for (AID buyer : buyerAgents)
cfp.addReceiver(buyer);
addBehaviour(new SearchJobInitiator(this, cfp));
/*
* ...
*/
SearchJobInitiator.java
This was the hard part...
/*
* ...
*/
public class SearchJobInitiator extends ContractNetInitiator {
ShipperAgent shipperAgent;
public SearchJobInitiator(ShipperAgent a, ACLMessage cfp) {
super(a, cfp);
shipperAgent=a;
// Very important:
registerHandleAllResponses(new HandleProposes());
}
@Override
protected Vector<?> prepareCfps(ACLMessage cfp) {
long now = System.currentTimeMillis();
cfp.setConversationId("contractNet-by-"
+shipperAgent.getAID().getLocalName()+now);
cfp.setContent("Fammi delle proposte di lavoro");
/*
* filtering...
*/
cfp.setProtocol(FIPANames.InteractionProtocol.FIPA_CONTRACT_NET);
cfp.setReplyByDate(new Date(now+10000));
//cfp.setReplyWith("cfp"+System.currentTimeMillis()) //useless, is overwrited at the end
return super.prepareCfps(cfp);
}
//inner class for handling a single proposal
public class HandleProposes extends Behaviour {
private static final long serialVersionUID = 1L;
private Vector<ACLMessage> proposes;
private Vector<ACLMessage> acceptances;
private int numberOfProposes;
public void onStart() {
proposes = (Vector<ACLMessage>) getDataStore().get(ALL_RESPONSES_KEY);
acceptances = (Vector<ACLMessage>) getDataStore().get(ALL_ACCEPTANCES_KEY);
numberOfProposes=proposes.size();
for (Iterator I=proposes.iterator(); I.hasNext();) {
ACLMessage propose = (ACLMessage) I.next();
// Very important:
if (propose.getPerformative()==ACLMessage.PROPOSE)
myAgent.addBehaviour(new HandleSinglePropose(propose, acceptances));
else
numberOfProposes--;
}
}
public void action() {
if (!done())
block();
}
public boolean done() {
return (acceptances.size()==numberOfProposes);
}
/*
* Inner class for handle a single proposal and display it:
*/
public class HandleSinglePropose extends Behaviour {
private ACLMessage propose;
private Vector<ACLMessage> acceptances;
private boolean finish=false;
public HandleSinglePropose (ACLMessage propose, Vector<ACLMessage> acceptances) {
this.propose=propose;
this.acceptances=acceptances;
// This is GUI in 3.1 point
GoodsChoiceBox gcb = new GoodsChoiceBox(shipperAgent, this, propose); // fill the JTable
gcb.setVisible(true);
}
@Override
public void action() {
MessageTemplate mt = MessageTemplate.and(
MessageTemplate.MatchSender(shipperAgent.getAID()),
MessageTemplate.and(
MessageTemplate.MatchReplyWith("response"+propose.getReplyWith()),
MessageTemplate.or(
MessageTemplate.MatchPerformative(ACLMessage.ACCEPT_PROPOSAL),
MessageTemplate.MatchPerformative(ACLMessage.REJECT_PROPOSAL)
) ) ) ;
// Read data from GUI. The user accept or reject:
ACLMessage decisionFromGUI = shipperAgent.receive(mt);
if (decisionFromGUI != null) {
ACLMessage reply = propose.createReply();
// bla bla...
finish=true;
HandleProposes.this.restart();
} else {
block();
}
}
public boolean done() {
return finish;
}
public void handleChoice(ACLMessage propose, boolean bool, Vector<Goods> selectedGoods) {
ACLMessage reply;
if (bool){
reply = new ACLMessage(ACLMessage.ACCEPT_PROPOSAL);
//...
} else {
reply = new ACLMessage(ACLMessage.REJECT_PROPOSAL);
//...
}
reply.addReceiver(shipperAgent.getAID());
reply.setReplyWith("response"+propose.getReplyWith());
shipperAgent.send(reply);
}
} // closes HandleSinglePropose
} // closes HandleProposes
}
SearchJobResponder.java
The responder is simple. The only thing of note: I extends SSContractNetResponder, don't extends ContractNetResponder.
public class SearchJobResponder extends SSContractNetResponder {
BuyerAgent buyerAgent;
public SearchJobResponder(BuyerAgent a, ACLMessage cfp) {
super(a, cfp);
buyerAgent = a;
}
/*
* override methods...
*/
}
GoodsChoiceBox.java
The GUI for show the proposals...
public GoodsChoiceBox(final Agent agent, final HandleSinglePropose behaviour, final ACLMessage propose){
/*
* graphics stuff
*/
// if goods selected and press Execute
behaviour.handleChoice(propose,true,selectedGoods);
//else
behaviour.handleChoice(propose,false,null);
/*
* bla bla
*/
}
I know, I have dwelt much, but I did not know how else to explain.
However, now my project work. But I'm open to any suggestions.