/*****************************************************************
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.core.messaging;
//#MIDP_EXCLUDE_FILE
import java.util.Date;
import jade.domain.FIPAAgentManagement.Envelope;
import jade.core.VerticalCommand;
import jade.core.AgentContainer;
import jade.core.Filter;
import jade.core.AID;
import jade.lang.acl.ACLMessage;
import jade.lang.acl.ACLCodec;
import jade.lang.acl.StringACLCodec;
import jade.lang.acl.LEAPACLCodec;
import jade.util.leap.Iterator;
import jade.util.leap.Map;
/**
* Class that filters outgoing commands related to the encoding of ACL messages
*
* @author Jerome Picault - Motorola Labs
* @author Nicolas Lhuillier - Motorola Labs
* @version $Date: 2009-04-08 11:41:41 +0200 (mer, 08 apr 2009) $ $Revision: 6110 $
*/
public class OutgoingEncodingFilter extends Filter {
private Map messageEncodings;
private AgentContainer myAgentContainer;
private MessagingService myService;
public OutgoingEncodingFilter(Map m, AgentContainer ac, MessagingService ms){
messageEncodings = m;
myAgentContainer = ac;
myService = ms;
setPreferredPosition(10);
}
/**
* Process the SEND_MESSAGE VCommand encoding the ACLMessage with the
* proper representation and adjusting Envelope fields:
* 1) If the receiver lives in the local container
* --> Do not encode (to speed up performances)
* --> Don't touch the envelope
* 2) If the receiver lives in a remote container
* --> Encode using the specified representation or "LEAP" if no representation is specified
* --> If an envelope is present adjust its fields
* 3) If the receiver lives in a remote platform
* --> Encode using the specified representation or "String" if no representation is specified
* --> Create a default envelope if not present and adjust its fields
*/
public boolean accept(VerticalCommand cmd) {
String name = cmd.getName();
Object[] params = cmd.getParams();
if (name.equals(MessagingSlice.SEND_MESSAGE)) {
GenericMessage gmsg = (GenericMessage)params[1];
AID sender = (AID) params[0];
AID receiver = (AID) params[2];
ACLMessage msg = gmsg.getACLMessage();
// Set the sender unless already set
try {
if (msg.getSender().getName().length() < 1)
msg.setSender(sender);
}
catch (NullPointerException e) {
msg.setSender(sender);
}
// Check if the receiver is on the same container or not
if (myAgentContainer.isLocalAgent(receiver)){
// Receiver is local --> do not encode the message
return true;
}
else {
// add necessary fields to the envelope
prepareEnvelope(msg, receiver, gmsg);
}
// Encode the message using the specified encoding
try{
byte[] payload = encodeMessage(msg);
Envelope env = msg.getEnvelope();
if (env!=null)
env.setPayloadLength(new Long(payload.length));
// Update the ACLMessage: some information is kept because it is
// required in other services
((GenericMessage)cmd.getParams()[1]).update(msg,env,payload);
} catch (MessagingService.UnknownACLEncodingException ee){
//FIXME
ee.printStackTrace();
}
}
return true;
}
/**
* This method puts into the envelope the missing information if required
*/
public void prepareEnvelope(ACLMessage msg, AID receiver, GenericMessage gmsg) {
Envelope env = msg.getEnvelope();
String defaultRepresentation = null;
if (myService.livesHere(receiver)) {
// The agent lives in the platform
if (env == null) {
// Nothing to do
return;
}
else {
defaultRepresentation = LEAPACLCodec.NAME;
}
}
else {
// The agent lives outside the platform
gmsg.setForeignReceiver(true);
if (env == null) {
msg.setDefaultEnvelope();
env = msg.getEnvelope();
}
else {
defaultRepresentation = StringACLCodec.NAME;
}
}
// If no ACL representation is set, use the default one ("LEAP" for
// local receivers and "String" for foreign receivers)
String rep = env.getAclRepresentation();
if(rep == null)
env.setAclRepresentation(defaultRepresentation);
// If no 'to' slot is present, copy the 'to' slot from the
// 'receiver' slot of the ACL message
Iterator itTo = env.getAllTo();
if(!itTo.hasNext()) {
Iterator itReceiver = msg.getAllReceiver();
while(itReceiver.hasNext())
env.addTo((AID)itReceiver.next());
}
// If no 'from' slot is present, copy the 'from' slot from the
// 'sender' slot of the ACL message
AID from = env.getFrom();
if(from == null) {
env.setFrom(msg.getSender());
}
// Set the 'date' slot to 'now' if not present already
Date d = env.getDate();
if(d == null)
env.setDate(new Date());
// Write 'intended-receiver' slot as per 'FIPA Agent Message
// Transport Service Specification': this ACC splits all
// multicasts, since JADE has already split them in the
// handleSend() method
env.clearAllIntendedReceiver();
env.addIntendedReceiver(receiver);
Long payloadLength = env.getPayloadLength();
if(payloadLength == null)
env.setPayloadLength(new Long(-1));
}
/**
* Encodes an ACL message according to the acl-representation described
* in the envelope. If there is no explicit acl-representation in the
* envelope, uses the String representation
* @param msg the message to be encoded
* @return the payload of the message
*/
public byte[] encodeMessage(ACLMessage msg) throws MessagingService.UnknownACLEncodingException{
Envelope env = msg.getEnvelope();
String enc = (env != null ? env.getAclRepresentation() : LEAPACLCodec.NAME);
if(enc != null) { // A Codec was selected
ACLCodec codec =(ACLCodec)messageEncodings.get(enc.toLowerCase());
if(codec!=null) {
// Supported Codec
// FIXME: should verify that the receivers supports this Codec
String charset;
if ((env == null) ||
((charset = env.getPayloadEncoding()) == null)) {
charset = ACLCodec.DEFAULT_CHARSET;
}
return codec.encode(msg,charset);
}
else {
// Unsupported Codec
//FIXME: find the best according to the supported, the MTP (and the receivers Codec)
throw new MessagingService.UnknownACLEncodingException("Unknown ACL encoding: " + enc + ".");
}
}
else {
// no codec indicated.
//FIXME: find the better according to the supported Codec, the MTP (and the receiver codec)
throw new MessagingService.UnknownACLEncodingException("No ACL encoding set.");
}
}
} // End of EncodingOutgoingFilter class