View Javadoc

1   /***
2    * Redistribution and use of this software and associated documentation
3    * ("Software"), with or without modification, are permitted provided
4    * that the following conditions are met:
5    *
6    * 1. Redistributions of source code must retain copyright
7    *    statements and notices.  Redistributions must also contain a
8    *    copy of this document.
9    *
10   * 2. Redistributions in binary form must reproduce the
11   *    above copyright notice, this list of conditions and the
12   *    following disclaimer in the documentation and/or other
13   *    materials provided with the distribution.
14   *
15   * 3. The name "Exolab" must not be used to endorse or promote
16   *    products derived from this Software without prior written
17   *    permission of Exoffice Technologies.  For written permission,
18   *    please contact info@exolab.org.
19   *
20   * 4. Products derived from this Software may not be called "Exolab"
21   *    nor may "Exolab" appear in their names without prior written
22   *    permission of Exoffice Technologies. Exolab is a registered
23   *    trademark of Exoffice Technologies.
24   *
25   * 5. Due credit should be given to the Exolab Project
26   *    (http://www.exolab.org/).
27   *
28   * THIS SOFTWARE IS PROVIDED BY EXOFFICE TECHNOLOGIES AND CONTRIBUTORS
29   * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
30   * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
31   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
32   * EXOFFICE TECHNOLOGIES OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
33   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
34   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
35   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
37   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
38   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
39   * OF THE POSSIBILITY OF SUCH DAMAGE.
40   *
41   * Copyright 2000-2003 (C) Exoffice Technologies Inc. All Rights Reserved.
42   *
43   * $Id: MapMessageImpl.java,v 1.1 2004/11/26 01:50:43 tanderson Exp $
44   *
45   * Date         Author  Changes
46   * 02/26/2000   jimm    Created
47   */
48  
49  package org.exolab.jms.message;
50  
51  import java.io.IOException;
52  import java.io.ObjectInput;
53  import java.io.ObjectOutput;
54  import java.util.Collections;
55  import java.util.Enumeration;
56  import java.util.HashMap;
57  
58  import javax.jms.JMSException;
59  import javax.jms.MapMessage;
60  import javax.jms.MessageFormatException;
61  import javax.jms.MessageNotWriteableException;
62  
63  
64  /***
65   * This class implements the {@link javax.jms.MapMessage} interface.
66   * <p>
67   * A MapMessage is used to send a set of name-value pairs where names are
68   * Strings and values are Java primitive types. The entries can be accessed
69   * sequentially or randomly by name. The order of the entries is undefined.
70   * It inherits from <code>Message</code> and adds a map message body.
71   * <p>
72   * The primitive types can be read or written explicitly using methods
73   * for each type. They may also be read or written generically as objects.
74   * For instance, a call to <code>MapMessage.setInt("foo", 6)</code> is
75   * equivalent to <code>MapMessage.setObject("foo", new Integer(6))</code>.
76   * Both forms are provided because the explicit form is convenient for
77   * static programming and the object form is needed when types are not known
78   * at compile time.
79   * <p>
80   * When a client receives a MapMessage, it is in read-only mode. If a
81   * client attempts to write to the message at this point, a
82   * MessageNotWriteableException is thrown. If {@link #clearBody} is
83   * called, the message can now be both read from and written to.
84   * <p>
85   * Map messages support the following conversion table. The marked cases
86   * must be supported. The unmarked cases must throw a JMSException. The
87   * String to primitive conversions may throw a runtime exception if the
88   * primitives <code>valueOf()</code> method does not accept it as a valid
89   * String representation of the primitive.
90   * <p>
91   * A value written as the row type can be read as the column type.
92   *
93   * <pre>
94   * |        | boolean byte short char int long float double String byte[]
95   * |----------------------------------------------------------------------
96   * |boolean |    X                                            X
97   * |byte    |          X     X         X   X                  X
98   * |short   |                X         X   X                  X
99   * |char    |                     X                           X
100  * |int     |                          X   X                  X
101  * |long    |                              X                  X
102  * |float   |                                    X     X      X
103  * |double  |                                          X      X
104  * |String  |    X     X     X         X   X     X     X      X
105  * |byte[]  |                                                        X
106  * |----------------------------------------------------------------------
107  * </pre>
108  *
109  * <p>
110  * Attempting to read a null value as a Java primitive type must be treated
111  * as calling the primitive's corresponding <code>valueOf(String)</code>
112  * conversion method with a null value. Since char does not support a
113  * String conversion, attempting to read a null value as a char must
114  * throw NullPointerException.
115  *
116  * @version     $Revision: 1.1 $ $Date: 2004/11/26 01:50:43 $
117  * @author      <a href="mailto:mourikis@exolab.org">Jim Mourikis</a>
118  * @see         javax.jms.MapMessage
119  */
120 public class MapMessageImpl extends MessageImpl implements MapMessage {
121 
122     /***
123      * Object version no. for serialization
124      */
125     static final long serialVersionUID = 2;
126 
127     /***
128      * The initial size of the map
129      */
130     private static final int INITIAL_SIZE = 20;
131 
132     /***
133      * The container for all message data
134      */
135     private HashMap _map = new HashMap(INITIAL_SIZE);
136 
137     /***
138      * Construct a new MapMessage
139      *
140      * @throws JMSException if the message type can't be set
141      */
142     public MapMessageImpl() throws JMSException {
143         setJMSType("MapMessage");
144     }
145 
146     /***
147      * Clone an instance of this object
148      *
149      * @return a copy of this object
150      * @throws CloneNotSupportedException if object or attributes aren't
151      * cloneable
152      */
153     public final Object clone() throws CloneNotSupportedException {
154         MapMessageImpl result = (MapMessageImpl) super.clone();
155         result._map = (HashMap) _map.clone();
156         return result;
157     }
158 
159     /***
160      * Serialize out this message's data
161      *
162      * @param out the stream to serialize out to
163      * @throws IOException if any I/O exceptions occurr
164      */
165     public final void writeExternal(ObjectOutput out) throws IOException {
166         super.writeExternal(out);
167         out.writeLong(serialVersionUID);
168         out.writeObject(_map);
169     }
170 
171     /***
172      * Serialize in this message's data
173      *
174      * @param in the stream to serialize in from
175      * @throws ClassNotFoundException if the class for an object being
176      * restored cannot be found.
177      * @throws IOException if any I/O exceptions occur
178      */
179     public final void readExternal(ObjectInput in)
180         throws ClassNotFoundException, IOException {
181         super.readExternal(in);
182         long version = in.readLong();
183         if (version == serialVersionUID) {
184             _map = (HashMap) in.readObject();
185         } else {
186             throw new IOException("Incorrect version enountered: " + version +
187                 ". This version = " + serialVersionUID);
188         }
189     }
190 
191     /***
192      * Return the boolean value with the given name
193      *
194      * @param name the name of the boolean
195      * @return the boolean value with the given name
196      * @throws JMSException if JMS fails to read the message due to some
197      * internal JMS error
198      * @throws MessageFormatException if this type conversion is invalid
199      */
200     public final boolean getBoolean(String name)
201         throws JMSException, MessageFormatException {
202         return FormatConverter.getBoolean(_map.get(name));
203     }
204 
205     /***
206      * Return the byte value with the given name
207      *
208      * @param name the name of the byte
209      * @return the byte value with the given name
210      * @throws JMSException if JMS fails to read the message due to some
211      * internal JMS error
212      * @throws MessageFormatException if this type conversion is invalid
213      */
214     public final byte getByte(String name)
215         throws JMSException, MessageFormatException {
216         return FormatConverter.getByte(_map.get(name));
217     }
218 
219     /***
220      * Return the short value with the given name
221      *
222      * @param name the name of the short
223      * @return the short value with the given name
224      * @throws JMSException if JMS fails to read the message due to some
225      * internal JMS error
226      * @throws MessageFormatException if this type conversion is invalid
227      */
228     public final short getShort(String name)
229         throws JMSException, MessageFormatException {
230         return FormatConverter.getShort(_map.get(name));
231     }
232 
233     /***
234      * Return the Unicode character value with the given name
235      *
236      * @param name the name of the Unicode character
237      * @return the Unicode character value with the given name
238      * @throws JMSException if JMS fails to read the message due to some
239      * internal JMS error
240      * @throws MessageFormatException if this type conversion is invalid
241      */
242     public final char getChar(String name)
243         throws JMSException, MessageFormatException {
244         return FormatConverter.getChar(_map.get(name));
245     }
246 
247     /***
248      * Return the integer value with the given name
249      *
250      * @param name the name of the integer
251      * @return the integer value with the given name
252      * @throws JMSException if JMS fails to read the message due to some
253      * internal JMS error
254      * @throws MessageFormatException if this type conversion is invalid
255      */
256     public final int getInt(String name)
257         throws JMSException, MessageFormatException {
258         return FormatConverter.getInt(_map.get(name));
259     }
260 
261     /***
262      * Return the long value with the given name
263      *
264      * @param name the name of the long
265      * @return the long value with the given name
266      * @throws JMSException if JMS fails to read the message due to some
267      * internal JMS error
268      * @throws MessageFormatException if this type conversion is invalid
269      */
270     public final long getLong(String name)
271         throws JMSException, MessageFormatException {
272         return FormatConverter.getLong(_map.get(name));
273     }
274 
275     /***
276      * Return the float value with the given name
277      *
278      * @param name the name of the float
279      * @return the float value with the given name
280      * @throws JMSException if JMS fails to read the message due to some
281      * internal JMS error
282      * @throws MessageFormatException if this type conversion is invalid
283      */
284     public final float getFloat(String name)
285         throws JMSException, MessageFormatException {
286         return FormatConverter.getFloat(_map.get(name));
287     }
288 
289     /***
290      * Return the double value with the given name
291      *
292      * @param name the name of the double
293      * @return the double value with the given name
294      * @throws JMSException if JMS fails to read the message due to some
295      * internal JMS error
296      * @throws MessageFormatException if this type conversion is invalid
297      */
298     public final double getDouble(String name)
299         throws JMSException, MessageFormatException {
300         return FormatConverter.getDouble(_map.get(name));
301     }
302 
303     /***
304      * Return the String value with the given name
305      *
306      * @param name the name of the String
307      * @return the String value with the given name. If there is no item
308      * by this name, a null value is returned.
309      * @throws JMSException if JMS fails to read the message due to some
310      * internal JMS error
311      * @throws MessageFormatException if this type conversion is invalid
312      */
313     public final String getString(String name)
314         throws JMSException, MessageFormatException {
315         return FormatConverter.getString(_map.get(name));
316     }
317 
318     /***
319      * Return the byte array value with the given name
320      *
321      * @param name the name of the byte array
322      * @return a copy of the byte array value with the given name.
323      * If there is no item by this name, a null value is returned.
324      * @throws JMSException if JMS fails to read the message due to some
325      * internal JMS error
326      * @throws MessageFormatException if this type conversion is invalid
327      */
328     public final byte[] getBytes(String name)
329         throws JMSException, MessageFormatException {
330         return FormatConverter.getBytes(_map.get(name));
331     }
332 
333     /***
334      * Return the Java object value with the given name
335      * <p>
336      * Note that this method can be used to return in objectified format,
337      * an object that had been stored in the Map with the equivalent
338      * <code>setObject</code> method call, or it's equivalent primitive
339      * set<type> method.
340      *
341      * @param name the name of the Java object
342      * @return a copy of the Java object value with the given name, in
343      * objectified format (eg. if it set as an int, then an Integer is
344      * returned).
345      * Note that byte values are returned as byte[], not Byte[].
346      * If there is no item by this name, a null value is returned.
347      * @throws JMSException if JMS fails to read the message due to some
348      * internal JMS error
349      */
350     public final Object getObject(String name) throws JMSException {
351         Object result = null;
352         Object value = _map.get(name);
353         if (value != null) {
354             if (value instanceof Boolean) {
355                 result = new Boolean(((Boolean) value).booleanValue());
356             } else if (value instanceof Byte) {
357                 result = new Byte(((Byte) value).byteValue());
358             } else if (value instanceof Short) {
359                 result = new Short(((Short) value).shortValue());
360             } else if (value instanceof Character) {
361                 result = new Character(((Character) value).charValue());
362             } else if (value instanceof Integer) {
363                 result = new Integer(((Integer) value).intValue());
364             } else if (value instanceof Long) {
365                 result = new Long(((Long) value).longValue());
366             } else if (value instanceof Float) {
367                 result = new Float(((Float) value).floatValue());
368             } else if (value instanceof Double) {
369                 result = new Double(((Double) value).doubleValue());
370             } else if (value instanceof String) {
371                 result = (String) value;
372             } else if (value instanceof byte[]) {
373                 result = getBytes(name);
374             } else {
375                 throw new MessageFormatException(
376                     "MapMessage contains an unsupported object of type=" +
377                     value.getClass().getName());
378             }
379         }
380         return result;
381     }
382 
383     /***
384      * Return an Enumeration of all the Map message's names.
385      *
386      * @return an enumeration of all the names in this Map message.
387      */
388     public final Enumeration getMapNames() {
389         return Collections.enumeration(_map.keySet());
390     }
391 
392     /***
393      * Set a boolean value with the given name, into the Map
394      *
395      * @param name the name of the boolean
396      * @param value the boolean value to set in the Map
397      * @throws MessageNotWriteableException if the message is in read-only mode
398      */
399     public final void setBoolean(String name, boolean value)
400         throws MessageNotWriteableException {
401         checkWrite();
402         _map.put(name, new Boolean(value));
403     }
404 
405     /***
406      * Set a byte value with the given name, into the Map
407      *
408      * @param name the name of the byte
409      * @param value the byte value to set in the Map
410      * @throws MessageNotWriteableException if the message is in read-only mode
411      */
412     public final void setByte(String name, byte value)
413         throws MessageNotWriteableException {
414         checkWrite();
415         _map.put(name, new Byte(value));
416     }
417 
418     /***
419      * Set a short value with the given name, into the Map
420      *
421      * @param name the name of the short
422      * @param value the short value to set in the Map
423      * @throws MessageNotWriteableException if the message is in read-only mode
424      */
425     public final void setShort(String name, short value)
426         throws MessageNotWriteableException {
427         checkWrite();
428         _map.put(name, new Short(value));
429     }
430 
431     /***
432      * Set a Unicode character value with the given name, into the Map
433      *
434      * @param name the name of the Unicode character
435      * @param value the Unicode character value to set in the Map
436      * @throws MessageNotWriteableException if the message is in read-only mode
437      */
438     public final void setChar(String name, char value)
439         throws MessageNotWriteableException {
440         checkWrite();
441         _map.put(name, new Character(value));
442     }
443 
444     /***
445      * Set an integer value with the given name, into the Map
446      *
447      * @param name the name of the integer
448      * @param value the integer value to set in the Map
449      * @throws MessageNotWriteableException if the message is in read-only mode
450      */
451     public final void setInt(String name, int value)
452         throws MessageNotWriteableException {
453         checkWrite();
454         _map.put(name, new Integer(value));
455     }
456 
457     /***
458      * Set a long value with the given name, into the Map
459      *
460      * @param name the name of the long
461      * @param value the long value to set in the Map
462      * @throws MessageNotWriteableException if the message is in read-only mode
463      */
464     public final void setLong(String name, long value)
465         throws MessageNotWriteableException {
466         checkWrite();
467         _map.put(name, new Long(value));
468     }
469 
470     /***
471      * Set a float value with the given name, into the Map
472      *
473      * @param name the name of the float
474      * @param value the float value to set in the Map
475      * @throws MessageNotWriteableException if the message is in read-only mode
476      */
477     public final void setFloat(String name, float value)
478         throws MessageNotWriteableException {
479         checkWrite();
480         _map.put(name, new Float(value));
481     }
482 
483     /***
484      * Set a double value with the given name, into the Map
485      *
486      * @param name the name of the double
487      * @param value the double value to set in the Map
488      * @throws MessageNotWriteableException if the message is in read-only mode
489      */
490     public final void setDouble(String name, double value)
491         throws MessageNotWriteableException {
492         checkWrite();
493         _map.put(name, new Double(value));
494     }
495 
496     /***
497      * Set a String value with the given name, into the Map
498      *
499      * @param name the name of the String
500      * @param value the String value to set in the Map
501      * @throws MessageNotWriteableException if the message is in read-only mode
502      */
503     public final void setString(String name, String value)
504         throws MessageNotWriteableException {
505         checkWrite();
506         _map.put(name, value);
507     }
508 
509     /***
510      * Set a byte array value with the given name, into the Map
511      *
512      * @param name the name of the byte array
513      * @param value the byte array value to set in the Map. The array is
514      * copied so the value for name will not be altered by future
515      * modifications.
516      * @throws MessageNotWriteableException if the message is in read-only mode
517      */
518     public final void setBytes(String name, byte[] value)
519         throws MessageNotWriteableException {
520         checkWrite();
521         byte[] bytes = null;
522         if (value != null) {
523             bytes = new byte[value.length];
524             System.arraycopy(value, 0, bytes, 0, bytes.length);
525         }
526         _map.put(name, bytes);
527     }
528 
529     /***
530      * Set a portion of the byte array value with the given name, into the Map
531      *
532      * @param name the name of the byte array
533      * @param value the byte array value to set in the Map.
534      * @param offset the initial offset within the byte array.
535      * @param length the number of bytes to use.
536      * @throws MessageNotWriteableException if the message is in read-only mode
537      */
538     public final void setBytes(String name, byte[] value,
539                                int offset, int length)
540         throws MessageNotWriteableException {
541         checkWrite();
542         byte[] bytes = null;
543         if (value != null) {
544             bytes = new byte[length];
545             System.arraycopy(value, offset, bytes, 0, length);
546         }
547         _map.put(name, bytes);
548     }
549 
550     /***
551      * Set a Java object value with the given name, into the Map
552      * <p>
553      * Note that this method only works for the objectified primitive
554      * object types (Integer, Double, Long ...), String's and byte arrays.
555      *
556      * @param name the name of the Java object
557      * @param value the Java object value to set in the Map
558      * @throws MessageFormatException if object is invalid
559      * @throws MessageNotWriteableException if message in read-only mode.
560      */
561     public final void setObject(String name, Object value)
562         throws MessageFormatException, MessageNotWriteableException {
563         checkWrite();
564         if (value == null) {
565             _map.put(name, null);
566         } else if (value instanceof Boolean) {
567             setBoolean(name, ((Boolean) value).booleanValue());
568         } else if (value instanceof Byte) {
569             setByte(name, ((Byte) value).byteValue());
570         } else if (value instanceof Short) {
571             setShort(name, ((Short) value).shortValue());
572         } else if (value instanceof Character) {
573             setChar(name, ((Character) value).charValue());
574         } else if (value instanceof Integer) {
575             setInt(name, ((Integer) value).intValue());
576         } else if (value instanceof Long) {
577             setLong(name, ((Long) value).longValue());
578         } else if (value instanceof Float) {
579             setFloat(name, ((Float) value).floatValue());
580         } else if (value instanceof Double) {
581             setDouble(name, ((Double) value).doubleValue());
582         } else if (value instanceof String) {
583             setString(name, (String) value);
584         } else if (value instanceof byte[]) {
585             setBytes(name, (byte[]) value);
586         } else {
587             throw new MessageFormatException(
588                 "MapMessage does not support objects of type=" +
589                 value.getClass().getName());
590         }
591     }
592 
593     /***
594      * Check if an item exists in this MapMessage
595      *
596      * @param name the name of the item to test
597      * @return true if the item exists
598      */
599     public final boolean itemExists(String name) {
600         return _map.containsKey(name);
601     }
602 
603     /***
604      * Clear out the message body. Clearing a message's body does not clear
605      * its header values or property entries.
606      * If this message body was read-only, calling this method leaves the
607      * message body is in the same state as an empty body in a newly created
608      * message
609      */
610     public final void clearBody() throws JMSException {
611         super.clearBody();
612         _map = new HashMap(INITIAL_SIZE);
613     }
614 
615 } // End MapMessageImpl