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 }