View Javadoc

1   /******************************************************************
2   JADE - Java Agent DEvelopment Framework is a framework to develop 
3   multi-agent systems in compliance with the FIPA specifications.
4   Copyright (C) 2000 CSELT S.p.A. 
5   
6   GNU Lesser General Public License
7   
8   This library is free software; you can redistribute it and/or
9   modify it under the terms of the GNU Lesser General Public
10  License as published by the Free Software Foundation, 
11  version 2.1 of the License. 
12  
13  This library is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  Lesser General Public License for more details.
17  
18  You should have received a copy of the GNU Lesser General Public
19  License along with this library; if not, write to the
20  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  Boston, MA  02111-1307, USA.
22  *****************************************************************/
23  
24  package JADE_SL;
25  
26  import java.io.*;
27  import JADE_SL.AID;
28  import JADE_SL.CaseInsensitiveString;
29  
30  import java.util.*;
31  import java.util.Date;
32  
33  import JADE_SL.Base64;
34  import zeus.concepts.FIPAParser;
35  import zeus.concepts.FIPAPerformative;
36  
37  /***
38    This class implements the FIPA String codec for ACLMessages.
39    Notice that it is not possible to convey 
40    a sequence of bytes over a StringACLCodec because the bytes with
41    the 8th bit ON cannot properly converted into a char.
42   @author Fabio Bellifemine - CSELT S.p.A.
43   @version $Date: 2003/10/09 13:00:35 $ $Revision: 1.1.1.1 $
44   **/
45  public class StringACLCodec{
46  
47    public static final String NAME ="STRING"; 
48  
49      /*** Key of the user-defined parameter used to signal the automatic JADE
50  	conversion of the content into Base64 encoding  **/
51      private static final String BASE64ENCODING_KEY = new String("JADE-Encoding");
52      /*** Value of the user-defined parameter used to signal the automatic JADE
53  	conversion of the content into Base64 encoding  **/
54      private static final String BASE64ENCODING_VALUE = new String("Base64");
55  
56    private static final String SENDER          = new String(" :sender ");
57    private static final String RECEIVER        = new String(" :receiver ");
58    private static final String CONTENT         = new String(" :content "); 
59    private static final String REPLY_WITH      = new String(" :reply-with ");
60    private static final String IN_REPLY_TO     = new String(" :in-reply-to ");
61    private static final String REPLY_TO        = new String(" :reply-to ");
62    private static final String LANGUAGE        = new String(" :language ");
63    private static final String ENCODING        = new String(" :encoding ");
64    private static final String ONTOLOGY        = new String(" :ontology ");
65    private static final String REPLY_BY        = new String(" :reply-by ");
66    private static final String PROTOCOL        = new String(" :protocol ");
67    private static final String CONVERSATION_ID = new String(" :conversation-id ");
68  
69    FIPAParser parser;
70    Writer out;
71  
72    /***
73     * constructor for the codec.
74     * The standard input is used as an input stream of ACL messages.
75     * The standard output is used to write encoded ACL messages.
76     */
77    public StringACLCodec() {
78      parser = new FIPAParser(System.in);
79      out = new OutputStreamWriter(System.out);
80    }
81  
82  
83    /***
84     * constructor for the codec.
85     * @parameter r is the input stream for the ACL Parser (pass 
86     * <code>new InputStreamReader(System.in)</code> 
87     * if you want to use the standard input)
88     * @parameter w is the writer to write encoded ACL messages (pass 
89     * <code>new OutputStreamWriter(System.out)</code> if you want to 
90     * use the standard output)
91     */
92    public StringACLCodec(Reader r, Writer w) {
93      parser = new FIPAParser(r);
94      out = w;
95    }
96  
97  
98      /***
99       * if there was an automatical Base64 encoding, then it performs
100      * automatic decoding.
101      **/
102     private void checkBase64Encoding(ACLMessage msg) {
103 	String encoding = msg.getUserDefinedParameter(BASE64ENCODING_KEY);
104 	if (CaseInsensitiveString.equalsIgnoreCase(BASE64ENCODING_VALUE,encoding)) {
105 	    try { // decode Base64
106 		String content = msg.getContent();
107 		if ((content != null) && (content.length() > 0)) {
108 		    char[] cc = new char[content.length()];
109 		    content.getChars(0,content.length(),cc,0);
110 		    msg.setByteSequenceContent(Base64.decode(cc));
111 		    msg.removeUserDefinedParameter(BASE64ENCODING_KEY); // reset the slot value for encoding
112 		}
113 	    } catch(java.lang.StringIndexOutOfBoundsException e){
114 		e.printStackTrace();
115 	    } catch(java.lang.NullPointerException e2){
116 		e2.printStackTrace();
117 	    } catch(java.lang.NoClassDefFoundError jlncdfe) {
118 		System.err.println("\t\t===== E R R O R !!! =======\n");
119 		System.err.println("Missing support for Base64 conversions");
120 		System.err.println("Please refer to the documentation for details.");
121 		System.err.println("=============================================\n\n");
122 		try {
123 		    Thread.currentThread().sleep(3000);
124 		}catch(InterruptedException ie) {
125 		}
126 	    }
127 	} //end of if CaseInsensitiveString
128     }
129 
130   /***
131    * decode and parses the next message from the Reader passed in the 
132    * constructor.
133    * @return the ACLMessage
134    * @throws ACLCodec.CodecException if any Exception occurs during the 
135    * parsing/reading operation
136    */
137   public FIPAPerformative decode() throws Exception {
138     try {
139       FIPAPerformative msg = parser.Message();
140       return msg;
141     }
142     catch (Exception e) {
143         throw e; }
144 
145   }
146 
147   /***
148    * encodes the message and writes it into the Writer passed in the 
149    * constructor.
150    * Notice that this method does not call <code>flush</code> on the writer.
151    @ param msg is the ACLMessage to encode and write into
152    */
153   public void write(ACLMessage msg) {
154       try {
155 	  out.write(toString(msg));
156       } catch (IOException ioe) {
157 	  ioe.printStackTrace();
158       }
159   }
160 
161 
162   static private String escape(String s) {
163     // Make the stringBuffer a little larger than strictly
164     // necessary in case we need to insert any additional
165     // characters.  (If our size estimate is wrong, the
166     // StringBuffer will automatically grow as needed).
167     StringBuffer result = new StringBuffer(s.length()+20);
168     for( int i=0; i<s.length(); i++)
169       if( s.charAt(i) == '"' ) 
170 	result.append("//\"");
171       else 
172 	result.append(s.charAt(i));
173     return result.toString();
174   }
175 
176 
177     /***
178      * @return a String encoded message
179      * @see ACLMessage#toString()
180      **/
181     static String toString(ACLMessage msg) {
182       StringBuffer str = new StringBuffer("(");
183       str.append(msg.getPerformative(msg.getPerformative()) + "\n");
184       AID sender = msg.getSender();
185       if (sender != null) 
186 	str.append(SENDER + " "+ sender.toString()+"\n");
187       Iterator it = msg.getAllReceiver();
188       if (it.hasNext()) {
189 	str.append(RECEIVER + " (set ");
190 	while(it.hasNext()) 
191 	  str.append(it.next().toString()+" ");
192 	str.append(")\n");
193       }
194       it = msg.getAllReplyTo();
195       if (it.hasNext()) {
196 	str.append(REPLY_TO + " (set \n");
197 	while(it.hasNext()) 
198 	  str.append(it.next().toString()+" ");
199 	str.append(")\n");
200       }
201       if (msg.hasByteSequenceContent()) {
202 	  str.append(":X-"+ BASE64ENCODING_KEY + " " + BASE64ENCODING_VALUE + "\n");
203 	  try {
204 	      String b64 = new String(Base64.encode(msg.getByteSequenceContent()));
205 	      str.append(CONTENT + " \"" + b64 + "\" \n");
206 	  } catch(java.lang.NoClassDefFoundError jlncdfe) {
207 	      System.err.println("\n\t===== E R R O R !!! =======\n");
208 	      System.err.println("Missing support for Base64 conversions");
209 	      System.err.println("Please refer to the documentation for details.");
210 	      System.err.println("=============================================\n\n");
211 	      System.err.println("");
212 	      try {
213 		  Thread.currentThread().sleep(3000);
214 	      } catch(InterruptedException ie) {
215 	      }
216 	  }
217       } else {
218 	  String content = msg.getContent();
219 	  if (content != null) {
220 	      content = content.trim();
221 	      if (content.length() > 0)
222 		  str.append(CONTENT + " \"" + escape(content) + "\" \n");
223 	  }
224       }
225       String tmp = msg.getReplyWith();
226       if (tmp != null) {
227 	  tmp = tmp.trim();
228 	  if (tmp.length() > 0)
229 	      str.append(REPLY_WITH + " " + tmp + "\n");
230       }
231       tmp = msg.getInReplyTo();
232       if (tmp != null) {
233 	  tmp = tmp.trim();
234 	  if (tmp.length() > 0)
235 	      str.append(IN_REPLY_TO + " " + tmp + "\n");
236       }
237       tmp = msg.getEncoding();
238       if (tmp != null) {
239 	  tmp = tmp.trim();
240 	  if (tmp.length() > 0)
241 	      str.append(ENCODING + " " + tmp + "\n");
242       }
243       tmp = msg.getLanguage();
244       if (tmp != null) {
245 	  tmp = tmp.trim();
246 	  if (tmp.length() > 0)
247 	      str.append(LANGUAGE + " " + tmp + "\n");
248       }
249       tmp = msg.getOntology();
250       if (tmp != null) {
251 	  tmp = tmp.trim();
252 	  if (tmp.length() > 0)
253 	      str.append(ONTOLOGY + " " + tmp + "\n");
254       }
255       Date d = msg.getReplyByDate();
256       if (d != null)
257 	  str.append(REPLY_BY + " " + ISO8601.toString(d) + "\n");
258       tmp = msg.getProtocol();
259       if (tmp != null) {
260 	  tmp = tmp.trim();
261 	  if (tmp.length() > 0)
262 	      str.append(PROTOCOL + " " + tmp + "\n");
263       }
264       tmp = msg.getConversationId();
265       if (tmp != null) {
266 	  tmp = tmp.trim();
267 	  if (tmp.length() > 0)
268 	      str.append(CONVERSATION_ID + " " + tmp + "\n");
269       }
270       Properties userDefProps = msg.getAllUserDefinedParameters();
271       Enumeration e = userDefProps.propertyNames();
272       while (e.hasMoreElements()) {
273 	String key = ((String)e.nextElement()).trim();
274 	String value = userDefProps.getProperty(key);
275 	if (value != null) {
276 	    value = value.trim();
277 	    if (value.length()>0)
278 		str.append(" :X-" + key + " " + value + "\n");
279 	}
280       }
281       str.append(")");
282 
283       return str.toString();
284     }
285 
286   /***
287    * If the content of the message is a byteSequence, then this
288    * method encodes the content in Base64 and automatically sets the value
289    * of the encoding slot.
290    * @see ACLCodec#encode(ACLMessage msg)
291    */
292   public byte[] encode(ACLMessage msg) {
293     try {
294 	return toString(msg).getBytes("US-ASCII");
295     }
296     catch(IOException ioe) {
297       ioe.printStackTrace();
298       return new byte[0];
299     }
300   }
301 
302   /***
303    * @see ACLCodec#decode(byte[] data)
304    */
305 /*  public FIPAPerformative decode(byte[] data) throws Exception {
306     try {
307       FIPAPerformative msg = ACLParser.Message(new InputStreamReader(new ByteArrayInputStream(data)));
308       checkBase64Encoding(msg);
309       return msg;
310     } catch (jade.lang.acl.TokenMgrError e1) {
311       throw new ACLCodec.CodecException(getName()+" ACLMessage decoding token exception",e1);
312     } catch (Exception e2) {
313       throw new ACLCodec.CodecException(getName()+" ACLMessage decoding exception",e2);
314     }
315   }*/
316 
317   /***
318    * @return the name of this encoding according to the FIPA specifications
319    */
320   public String getName() {
321     return NAME;
322   }
323 }