001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.activemq.command;
018    
019    import java.io.DataInputStream;
020    import java.io.DataOutputStream;
021    import java.io.IOException;
022    import java.io.InputStream;
023    import java.io.ObjectStreamException;
024    import java.io.OutputStream;
025    import java.util.Collections;
026    import java.util.Enumeration;
027    import java.util.HashMap;
028    import java.util.Map;
029    import java.util.zip.DeflaterOutputStream;
030    import java.util.zip.InflaterInputStream;
031    
032    import javax.jms.JMSException;
033    import javax.jms.MapMessage;
034    import javax.jms.MessageFormatException;
035    import javax.jms.MessageNotWriteableException;
036    
037    import org.apache.activemq.ActiveMQConnection;
038    import org.apache.activemq.util.ByteArrayInputStream;
039    import org.apache.activemq.util.ByteArrayOutputStream;
040    import org.apache.activemq.util.ByteSequence;
041    import org.apache.activemq.util.JMSExceptionSupport;
042    import org.apache.activemq.util.MarshallingSupport;
043    import org.apache.activemq.wireformat.WireFormat;
044    
045    /**
046     * A <CODE>MapMessage</CODE> object is used to send a set of name-value pairs.
047     * The names are <CODE>String</CODE> objects, and the values are primitive
048     * data types in the Java programming language. The names must have a value that
049     * is not null, and not an empty string. The entries can be accessed
050     * sequentially or randomly by name. The order of the entries is undefined.
051     * <CODE>MapMessage</CODE> inherits from the <CODE>Message</CODE> interface
052     * and adds a message body that contains a Map.
053     * <P>
054     * The primitive types can be read or written explicitly using methods for each
055     * type. They may also be read or written generically as objects. For instance,
056     * a call to <CODE>MapMessage.setInt("foo", 6)</CODE> is equivalent to
057     * <CODE> MapMessage.setObject("foo", new Integer(6))</CODE>. Both forms are
058     * provided, because the explicit form is convenient for static programming, and
059     * the object form is needed when types are not known at compile time.
060     * <P>
061     * When a client receives a <CODE>MapMessage</CODE>, it is in read-only mode.
062     * If a client attempts to write to the message at this point, a
063     * <CODE>MessageNotWriteableException</CODE> is thrown. If
064     * <CODE>clearBody</CODE> is called, the message can now be both read from and
065     * written to.
066     * <P>
067     * <CODE>MapMessage</CODE> objects support the following conversion table. The
068     * marked cases must be supported. The unmarked cases must throw a
069     * <CODE>JMSException</CODE>. The <CODE>String</CODE> -to-primitive
070     * conversions may throw a runtime exception if the primitive's
071     * <CODE>valueOf()</CODE> method does not accept it as a valid
072     * <CODE> String</CODE> representation of the primitive.
073     * <P>
074     * A value written as the row type can be read as the column type. <p/>
075     *
076     * <PRE>
077     * | | boolean byte short char int long float double String byte[] |----------------------------------------------------------------------
078     * |boolean | X X |byte | X X X X X |short | X X X X |char | X X |int | X X X |long | X X |float | X X X |double | X X
079     * |String | X X X X X X X X |byte[] | X |----------------------------------------------------------------------
080     * &lt;p/&gt;
081     * </PRE>
082     *
083     * <p/>
084     * <P>
085     * Attempting to read a null value as a primitive type must be treated as
086     * calling the primitive's corresponding <code>valueOf(String)</code>
087     * conversion method with a null value. Since <code>char</code> does not
088     * support a <code>String</code> conversion, attempting to read a null value
089     * as a <code>char</code> must throw a <code>NullPointerException</code>.
090     *
091     * @openwire:marshaller code="25"
092     * @see javax.jms.Session#createMapMessage()
093     * @see javax.jms.BytesMessage
094     * @see javax.jms.Message
095     * @see javax.jms.ObjectMessage
096     * @see javax.jms.StreamMessage
097     * @see javax.jms.TextMessage
098     */
099    public class ActiveMQMapMessage extends ActiveMQMessage implements MapMessage {
100    
101        public static final byte DATA_STRUCTURE_TYPE = CommandTypes.ACTIVEMQ_MAP_MESSAGE;
102    
103        protected transient Map<String, Object> map = new HashMap<String, Object>();
104    
105        private Object readResolve() throws ObjectStreamException {
106            if(this.map == null) {
107                this.map = new HashMap<String, Object>();
108            }
109            return this;
110        }
111    
112        public Message copy() {
113            ActiveMQMapMessage copy = new ActiveMQMapMessage();
114            copy(copy);
115            return copy;
116        }
117    
118        private void copy(ActiveMQMapMessage copy) {
119            storeContent();
120            super.copy(copy);
121        }
122    
123        // We only need to marshal the content if we are hitting the wire.
124        public void beforeMarshall(WireFormat wireFormat) throws IOException {
125            super.beforeMarshall(wireFormat);
126            storeContent();
127        }
128    
129        public void clearMarshalledState() throws JMSException {
130            super.clearMarshalledState();
131            map.clear();
132        }
133    
134        private void storeContent() {
135            try {
136                if (getContent() == null && !map.isEmpty()) {
137                    ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
138                    OutputStream os = bytesOut;
139                    ActiveMQConnection connection = getConnection();
140                    if (connection != null && connection.isUseCompression()) {
141                        compressed = true;
142                        os = new DeflaterOutputStream(os);
143                    }
144                    DataOutputStream dataOut = new DataOutputStream(os);
145                    MarshallingSupport.marshalPrimitiveMap(map, dataOut);
146                    dataOut.close();
147                    setContent(bytesOut.toByteSequence());
148                }
149            } catch (IOException e) {
150                throw new RuntimeException(e);
151            }
152        }
153    
154        /**
155         * Builds the message body from data
156         *
157         * @throws JMSException
158         * @throws IOException
159         */
160        private void loadContent() throws JMSException {
161            try {
162                if (getContent() != null && map.isEmpty()) {
163                    ByteSequence content = getContent();
164                    InputStream is = new ByteArrayInputStream(content);
165                    if (isCompressed()) {
166                        is = new InflaterInputStream(is);
167                    }
168                    DataInputStream dataIn = new DataInputStream(is);
169                    map = MarshallingSupport.unmarshalPrimitiveMap(dataIn);
170                    dataIn.close();
171                }
172            } catch (IOException e) {
173                throw JMSExceptionSupport.create(e);
174            }
175        }
176    
177        public byte getDataStructureType() {
178            return DATA_STRUCTURE_TYPE;
179        }
180    
181        public String getJMSXMimeType() {
182            return "jms/map-message";
183        }
184    
185        /**
186         * Clears out the message body. Clearing a message's body does not clear its
187         * header values or property entries.
188         * <P>
189         * If this message body was read-only, calling this method leaves the
190         * message body in the same state as an empty body in a newly created
191         * message.
192         */
193        public void clearBody() throws JMSException {
194            super.clearBody();
195            map.clear();
196        }
197    
198        /**
199         * Returns the <CODE>boolean</CODE> value with the specified name.
200         *
201         * @param name the name of the <CODE>boolean</CODE>
202         * @return the <CODE>boolean</CODE> value with the specified name
203         * @throws JMSException if the JMS provider fails to read the message due to
204         *                 some internal error.
205         * @throws MessageFormatException if this type conversion is invalid.
206         */
207        public boolean getBoolean(String name) throws JMSException {
208            initializeReading();
209            Object value = map.get(name);
210            if (value == null) {
211                return false;
212            }
213            if (value instanceof Boolean) {
214                return ((Boolean)value).booleanValue();
215            }
216            if (value instanceof String) {
217                return Boolean.valueOf(value.toString()).booleanValue();
218            } else {
219                throw new MessageFormatException(" cannot read a boolean from " + value.getClass().getName());
220            }
221        }
222    
223        /**
224         * Returns the <CODE>byte</CODE> value with the specified name.
225         *
226         * @param name the name of the <CODE>byte</CODE>
227         * @return the <CODE>byte</CODE> value with the specified name
228         * @throws JMSException if the JMS provider fails to read the message due to
229         *                 some internal error.
230         * @throws MessageFormatException if this type conversion is invalid.
231         */
232        public byte getByte(String name) throws JMSException {
233            initializeReading();
234            Object value = map.get(name);
235            if (value == null) {
236                return 0;
237            }
238            if (value instanceof Byte) {
239                return ((Byte)value).byteValue();
240            }
241            if (value instanceof String) {
242                return Byte.valueOf(value.toString()).byteValue();
243            } else {
244                throw new MessageFormatException(" cannot read a byte from " + value.getClass().getName());
245            }
246        }
247    
248        /**
249         * Returns the <CODE>short</CODE> value with the specified name.
250         *
251         * @param name the name of the <CODE>short</CODE>
252         * @return the <CODE>short</CODE> value with the specified name
253         * @throws JMSException if the JMS provider fails to read the message due to
254         *                 some internal error.
255         * @throws MessageFormatException if this type conversion is invalid.
256         */
257        public short getShort(String name) throws JMSException {
258            initializeReading();
259            Object value = map.get(name);
260            if (value == null) {
261                return 0;
262            }
263            if (value instanceof Short) {
264                return ((Short)value).shortValue();
265            }
266            if (value instanceof Byte) {
267                return ((Byte)value).shortValue();
268            }
269            if (value instanceof String) {
270                return Short.valueOf(value.toString()).shortValue();
271            } else {
272                throw new MessageFormatException(" cannot read a short from " + value.getClass().getName());
273            }
274        }
275    
276        /**
277         * Returns the Unicode character value with the specified name.
278         *
279         * @param name the name of the Unicode character
280         * @return the Unicode character value with the specified name
281         * @throws JMSException if the JMS provider fails to read the message due to
282         *                 some internal error.
283         * @throws MessageFormatException if this type conversion is invalid.
284         */
285        public char getChar(String name) throws JMSException {
286            initializeReading();
287            Object value = map.get(name);
288            if (value == null) {
289                throw new NullPointerException();
290            }
291            if (value instanceof Character) {
292                return ((Character)value).charValue();
293            } else {
294                throw new MessageFormatException(" cannot read a short from " + value.getClass().getName());
295            }
296        }
297    
298        /**
299         * Returns the <CODE>int</CODE> value with the specified name.
300         *
301         * @param name the name of the <CODE>int</CODE>
302         * @return the <CODE>int</CODE> value with the specified name
303         * @throws JMSException if the JMS provider fails to read the message due to
304         *                 some internal error.
305         * @throws MessageFormatException if this type conversion is invalid.
306         */
307        public int getInt(String name) throws JMSException {
308            initializeReading();
309            Object value = map.get(name);
310            if (value == null) {
311                return 0;
312            }
313            if (value instanceof Integer) {
314                return ((Integer)value).intValue();
315            }
316            if (value instanceof Short) {
317                return ((Short)value).intValue();
318            }
319            if (value instanceof Byte) {
320                return ((Byte)value).intValue();
321            }
322            if (value instanceof String) {
323                return Integer.valueOf(value.toString()).intValue();
324            } else {
325                throw new MessageFormatException(" cannot read an int from " + value.getClass().getName());
326            }
327        }
328    
329        /**
330         * Returns the <CODE>long</CODE> value with the specified name.
331         *
332         * @param name the name of the <CODE>long</CODE>
333         * @return the <CODE>long</CODE> value with the specified name
334         * @throws JMSException if the JMS provider fails to read the message due to
335         *                 some internal error.
336         * @throws MessageFormatException if this type conversion is invalid.
337         */
338        public long getLong(String name) throws JMSException {
339            initializeReading();
340            Object value = map.get(name);
341            if (value == null) {
342                return 0;
343            }
344            if (value instanceof Long) {
345                return ((Long)value).longValue();
346            }
347            if (value instanceof Integer) {
348                return ((Integer)value).longValue();
349            }
350            if (value instanceof Short) {
351                return ((Short)value).longValue();
352            }
353            if (value instanceof Byte) {
354                return ((Byte)value).longValue();
355            }
356            if (value instanceof String) {
357                return Long.valueOf(value.toString()).longValue();
358            } else {
359                throw new MessageFormatException(" cannot read a long from " + value.getClass().getName());
360            }
361        }
362    
363        /**
364         * Returns the <CODE>float</CODE> value with the specified name.
365         *
366         * @param name the name of the <CODE>float</CODE>
367         * @return the <CODE>float</CODE> value with the specified name
368         * @throws JMSException if the JMS provider fails to read the message due to
369         *                 some internal error.
370         * @throws MessageFormatException if this type conversion is invalid.
371         */
372        public float getFloat(String name) throws JMSException {
373            initializeReading();
374            Object value = map.get(name);
375            if (value == null) {
376                return 0;
377            }
378            if (value instanceof Float) {
379                return ((Float)value).floatValue();
380            }
381            if (value instanceof String) {
382                return Float.valueOf(value.toString()).floatValue();
383            } else {
384                throw new MessageFormatException(" cannot read a float from " + value.getClass().getName());
385            }
386        }
387    
388        /**
389         * Returns the <CODE>double</CODE> value with the specified name.
390         *
391         * @param name the name of the <CODE>double</CODE>
392         * @return the <CODE>double</CODE> value with the specified name
393         * @throws JMSException if the JMS provider fails to read the message due to
394         *                 some internal error.
395         * @throws MessageFormatException if this type conversion is invalid.
396         */
397        public double getDouble(String name) throws JMSException {
398            initializeReading();
399            Object value = map.get(name);
400            if (value == null) {
401                return 0;
402            }
403            if (value instanceof Double) {
404                return ((Double)value).doubleValue();
405            }
406            if (value instanceof Float) {
407                return ((Float)value).floatValue();
408            }
409            if (value instanceof String) {
410                return Float.valueOf(value.toString()).floatValue();
411            } else {
412                throw new MessageFormatException(" cannot read a double from " + value.getClass().getName());
413            }
414        }
415    
416        /**
417         * Returns the <CODE>String</CODE> value with the specified name.
418         *
419         * @param name the name of the <CODE>String</CODE>
420         * @return the <CODE>String</CODE> value with the specified name; if there
421         *         is no item by this name, a null value is returned
422         * @throws JMSException if the JMS provider fails to read the message due to
423         *                 some internal error.
424         * @throws MessageFormatException if this type conversion is invalid.
425         */
426        public String getString(String name) throws JMSException {
427            initializeReading();
428            Object value = map.get(name);
429            if (value == null) {
430                return null;
431            }
432            if (value instanceof byte[]) {
433                throw new MessageFormatException("Use getBytes to read a byte array");
434            } else {
435                return value.toString();
436            }
437        }
438    
439        /**
440         * Returns the byte array value with the specified name.
441         *
442         * @param name the name of the byte array
443         * @return a copy of the byte array value with the specified name; if there
444         *         is no item by this name, a null value is returned.
445         * @throws JMSException if the JMS provider fails to read the message due to
446         *                 some internal error.
447         * @throws MessageFormatException if this type conversion is invalid.
448         */
449        public byte[] getBytes(String name) throws JMSException {
450            initializeReading();
451            Object value = map.get(name);
452            if (value instanceof byte[]) {
453                return (byte[])value;
454            } else {
455                throw new MessageFormatException(" cannot read a byte[] from " + value.getClass().getName());
456            }
457        }
458    
459        /**
460         * Returns the value of the object with the specified name.
461         * <P>
462         * This method can be used to return, in objectified format, an object in
463         * the Java programming language ("Java object") that had been stored in the
464         * Map with the equivalent <CODE>setObject</CODE> method call, or its
465         * equivalent primitive <CODE>set <I>type </I></CODE> method.
466         * <P>
467         * Note that byte values are returned as <CODE>byte[]</CODE>, not
468         * <CODE>Byte[]</CODE>.
469         *
470         * @param name the name of the Java object
471         * @return a copy of the Java object value with the specified name, in
472         *         objectified format (for example, if the object was set as an
473         *         <CODE>int</CODE>, an <CODE>Integer</CODE> is returned); if
474         *         there is no item by this name, a null value is returned
475         * @throws JMSException if the JMS provider fails to read the message due to
476         *                 some internal error.
477         */
478        public Object getObject(String name) throws JMSException {
479            initializeReading();
480            return map.get(name);
481        }
482    
483        /**
484         * Returns an <CODE>Enumeration</CODE> of all the names in the
485         * <CODE>MapMessage</CODE> object.
486         *
487         * @return an enumeration of all the names in this <CODE>MapMessage</CODE>
488         * @throws JMSException
489         */
490        public Enumeration<String> getMapNames() throws JMSException {
491            initializeReading();
492            return Collections.enumeration(map.keySet());
493        }
494    
495        protected void put(String name, Object value) throws JMSException {
496            if (name == null) {
497                throw new IllegalArgumentException("The name of the property cannot be null.");
498            }
499            if (name.length() == 0) {
500                throw new IllegalArgumentException("The name of the property cannot be an emprty string.");
501            }
502            map.put(name, value);
503        }
504    
505        /**
506         * Sets a <CODE>boolean</CODE> value with the specified name into the Map.
507         *
508         * @param name the name of the <CODE>boolean</CODE>
509         * @param value the <CODE>boolean</CODE> value to set in the Map
510         * @throws JMSException if the JMS provider fails to write the message due
511         *                 to some internal error.
512         * @throws IllegalArgumentException if the name is null or if the name is an
513         *                 empty string.
514         * @throws MessageNotWriteableException if the message is in read-only mode.
515         */
516        public void setBoolean(String name, boolean value) throws JMSException {
517            initializeWriting();
518            put(name, value ? Boolean.TRUE : Boolean.FALSE);
519        }
520    
521        /**
522         * Sets a <CODE>byte</CODE> value with the specified name into the Map.
523         *
524         * @param name the name of the <CODE>byte</CODE>
525         * @param value the <CODE>byte</CODE> value to set in the Map
526         * @throws JMSException if the JMS provider fails to write the message due
527         *                 to some internal error.
528         * @throws IllegalArgumentException if the name is null or if the name is an
529         *                 empty string.
530         * @throws MessageNotWriteableException if the message is in read-only mode.
531         */
532        public void setByte(String name, byte value) throws JMSException {
533            initializeWriting();
534            put(name, Byte.valueOf(value));
535        }
536    
537        /**
538         * Sets a <CODE>short</CODE> value with the specified name into the Map.
539         *
540         * @param name the name of the <CODE>short</CODE>
541         * @param value the <CODE>short</CODE> value to set in the Map
542         * @throws JMSException if the JMS provider fails to write the message due
543         *                 to some internal error.
544         * @throws IllegalArgumentException if the name is null or if the name is an
545         *                 empty string.
546         * @throws MessageNotWriteableException if the message is in read-only mode.
547         */
548        public void setShort(String name, short value) throws JMSException {
549            initializeWriting();
550            put(name, Short.valueOf(value));
551        }
552    
553        /**
554         * Sets a Unicode character value with the specified name into the Map.
555         *
556         * @param name the name of the Unicode character
557         * @param value the Unicode character value to set in the Map
558         * @throws JMSException if the JMS provider fails to write the message due
559         *                 to some internal error.
560         * @throws IllegalArgumentException if the name is null or if the name is an
561         *                 empty string.
562         * @throws MessageNotWriteableException if the message is in read-only mode.
563         */
564        public void setChar(String name, char value) throws JMSException {
565            initializeWriting();
566            put(name, Character.valueOf(value));
567        }
568    
569        /**
570         * Sets an <CODE>int</CODE> value with the specified name into the Map.
571         *
572         * @param name the name of the <CODE>int</CODE>
573         * @param value the <CODE>int</CODE> value to set in the Map
574         * @throws JMSException if the JMS provider fails to write the message due
575         *                 to some internal error.
576         * @throws IllegalArgumentException if the name is null or if the name is an
577         *                 empty string.
578         * @throws MessageNotWriteableException if the message is in read-only mode.
579         */
580        public void setInt(String name, int value) throws JMSException {
581            initializeWriting();
582            put(name, Integer.valueOf(value));
583        }
584    
585        /**
586         * Sets a <CODE>long</CODE> value with the specified name into the Map.
587         *
588         * @param name the name of the <CODE>long</CODE>
589         * @param value the <CODE>long</CODE> value to set in the Map
590         * @throws JMSException if the JMS provider fails to write the message due
591         *                 to some internal error.
592         * @throws IllegalArgumentException if the name is null or if the name is an
593         *                 empty string.
594         * @throws MessageNotWriteableException if the message is in read-only mode.
595         */
596        public void setLong(String name, long value) throws JMSException {
597            initializeWriting();
598            put(name, Long.valueOf(value));
599        }
600    
601        /**
602         * Sets a <CODE>float</CODE> value with the specified name into the Map.
603         *
604         * @param name the name of the <CODE>float</CODE>
605         * @param value the <CODE>float</CODE> value to set in the Map
606         * @throws JMSException if the JMS provider fails to write the message due
607         *                 to some internal error.
608         * @throws IllegalArgumentException if the name is null or if the name is an
609         *                 empty string.
610         * @throws MessageNotWriteableException if the message is in read-only mode.
611         */
612        public void setFloat(String name, float value) throws JMSException {
613            initializeWriting();
614            put(name, new Float(value));
615        }
616    
617        /**
618         * Sets a <CODE>double</CODE> value with the specified name into the Map.
619         *
620         * @param name the name of the <CODE>double</CODE>
621         * @param value the <CODE>double</CODE> value to set in the Map
622         * @throws JMSException if the JMS provider fails to write the message due
623         *                 to some internal error.
624         * @throws IllegalArgumentException if the name is null or if the name is an
625         *                 empty string.
626         * @throws MessageNotWriteableException if the message is in read-only mode.
627         */
628        public void setDouble(String name, double value) throws JMSException {
629            initializeWriting();
630            put(name, new Double(value));
631        }
632    
633        /**
634         * Sets a <CODE>String</CODE> value with the specified name into the Map.
635         *
636         * @param name the name of the <CODE>String</CODE>
637         * @param value the <CODE>String</CODE> value to set in the Map
638         * @throws JMSException if the JMS provider fails to write the message due
639         *                 to some internal error.
640         * @throws IllegalArgumentException if the name is null or if the name is an
641         *                 empty string.
642         * @throws MessageNotWriteableException if the message is in read-only mode.
643         */
644        public void setString(String name, String value) throws JMSException {
645            initializeWriting();
646            put(name, value);
647        }
648    
649        /**
650         * Sets a byte array value with the specified name into the Map.
651         *
652         * @param name the name of the byte array
653         * @param value the byte array value to set in the Map; the array is copied
654         *                so that the value for <CODE>name </CODE> will not be
655         *                altered by future modifications
656         * @throws JMSException if the JMS provider fails to write the message due
657         *                 to some internal error.
658         * @throws NullPointerException if the name is null, or if the name is an
659         *                 empty string.
660         * @throws MessageNotWriteableException if the message is in read-only mode.
661         */
662        public void setBytes(String name, byte[] value) throws JMSException {
663            initializeWriting();
664            if (value != null) {
665                put(name, value);
666            } else {
667                map.remove(name);
668            }
669        }
670    
671        /**
672         * Sets a portion of the byte array value with the specified name into the
673         * Map.
674         *
675         * @param name the name of the byte array
676         * @param value the byte array value to set in the Map
677         * @param offset the initial offset within the byte array
678         * @param length the number of bytes to use
679         * @throws JMSException if the JMS provider fails to write the message due
680         *                 to some internal error.
681         * @throws IllegalArgumentException if the name is null or if the name is an
682         *                 empty string.
683         * @throws MessageNotWriteableException if the message is in read-only mode.
684         */
685        public void setBytes(String name, byte[] value, int offset, int length) throws JMSException {
686            initializeWriting();
687            byte[] data = new byte[length];
688            System.arraycopy(value, offset, data, 0, length);
689            put(name, data);
690        }
691    
692        /**
693         * Sets an object value with the specified name into the Map.
694         * <P>
695         * This method works only for the objectified primitive object types (<code>Integer</code>,<code>Double</code>,
696         * <code>Long</code> &nbsp;...), <code>String</code> objects, and byte
697         * arrays.
698         *
699         * @param name the name of the Java object
700         * @param value the Java object value to set in the Map
701         * @throws JMSException if the JMS provider fails to write the message due
702         *                 to some internal error.
703         * @throws IllegalArgumentException if the name is null or if the name is an
704         *                 empty string.
705         * @throws MessageFormatException if the object is invalid.
706         * @throws MessageNotWriteableException if the message is in read-only mode.
707         */
708        public void setObject(String name, Object value) throws JMSException {
709            initializeWriting();
710            if (value != null) {
711                // byte[] not allowed on properties
712                if (!(value instanceof byte[])) {
713                    checkValidObject(value);
714                }
715                put(name, value);
716            } else {
717                put(name, null);
718            }
719        }
720    
721        /**
722         * Indicates whether an item exists in this <CODE>MapMessage</CODE>
723         * object.
724         *
725         * @param name the name of the item to test
726         * @return true if the item exists
727         * @throws JMSException if the JMS provider fails to determine if the item
728         *                 exists due to some internal error.
729         */
730        public boolean itemExists(String name) throws JMSException {
731            initializeReading();
732            return map.containsKey(name);
733        }
734    
735        private void initializeReading() throws JMSException {
736            loadContent();
737        }
738    
739        private void initializeWriting() throws MessageNotWriteableException {
740            checkReadOnlyBody();
741            setContent(null);
742        }
743    
744        public String toString() {
745            return super.toString() + " ActiveMQMapMessage{ " + "theTable = " + map + " }";
746        }
747    
748        public Map<String, Object> getContentMap() throws JMSException {
749            initializeReading();
750            return map;
751        }
752    }