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