001    /** 
002     * 
003     * Copyright 2004 Protique Ltd
004     * 
005     * Licensed under the Apache License, Version 2.0 (the "License"); 
006     * you may not use this file except in compliance with the License. 
007     * 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     **/
018    package org.activemq.message;
019    
020    import javax.jms.BytesMessage;
021    import javax.jms.JMSException;
022    import javax.jms.MessageEOFException;
023    import javax.jms.MessageFormatException;
024    import javax.jms.MessageNotReadableException;
025    import javax.jms.MessageNotWriteableException;
026    
027    import org.activemq.io.util.ByteArray;
028    import org.activemq.io.util.ByteArrayCompression;
029    
030    import java.io.ByteArrayInputStream;
031    import java.io.ByteArrayOutputStream;
032    import java.io.DataInputStream;
033    import java.io.DataOutputStream;
034    import java.io.EOFException;
035    import java.io.IOException;
036    
037    /**
038     * A <CODE>BytesMessage</CODE> object is used to send a message containing a stream of uninterpreted bytes. It
039     * inherits from the <CODE>Message</CODE> interface and adds a bytes message body. The receiver of the message
040     * supplies the interpretation of the bytes.
041     * <P>
042     * The <CODE>BytesMessage</CODE> methods are based largely on those found in <CODE>java.io.DataInputStream</CODE>
043     * and <CODE>java.io.DataOutputStream</CODE>.
044     * <P>
045     * This message type is for client encoding of existing message formats. If possible, one of the other self-defining
046     * message types should be used instead.
047     * <P>
048     * Although the JMS API allows the use of message properties with byte messages, they are typically not used, since the
049     * inclusion of properties may affect the format.
050     * <P>
051     * The primitive types can be written explicitly using methods for each type. They may also be written generically as
052     * objects. For instance, a call to <CODE>BytesMessage.writeInt(6)</CODE> is equivalent to <CODE>
053     * BytesMessage.writeObject(new Integer(6))</CODE>. Both forms are provided, because the explicit form is convenient
054     * for static programming, and the object form is needed when types are not known at compile time.
055     * <P>
056     * When the message is first created, and when <CODE>clearBody</CODE> is called, the body of the message is in
057     * write-only mode. After the first call to <CODE>reset</CODE> has been made, the message body is in read-only mode.
058     * After a message has been sent, the client that sent it can retain and modify it without affecting the message that
059     * has been sent. The same message object can be sent multiple times. When a message has been received, the provider
060     * has called <CODE>reset</CODE> so that the message body is in read-only mode for the client.
061     * <P>
062     * If <CODE>clearBody</CODE> is called on a message in read-only mode, the message body is cleared and the message is
063     * in write-only mode.
064     * <P>
065     * If a client attempts to read a message in write-only mode, a <CODE>MessageNotReadableException</CODE> is thrown.
066     * <P>
067     * If a client attempts to write a message in read-only mode, a <CODE>MessageNotWriteableException</CODE> is thrown.
068     * 
069     * @see javax.jms.Session#createBytesMessage()
070     * @see javax.jms.MapMessage
071     * @see javax.jms.Message
072     * @see javax.jms.ObjectMessage
073     * @see javax.jms.StreamMessage
074     * @see javax.jms.TextMessage
075     */
076    public class ActiveMQBytesMessage extends ActiveMQMessage implements BytesMessage {
077        private DataOutputStream dataOut;
078        private ByteArrayOutputStream bytesOut;
079        private DataInputStream dataIn;
080        private transient long bodyLength = 0;
081    
082        /**
083         * Return the type of Packet
084         * 
085         * @return integer representation of the type of Packet
086         */
087        public int getPacketType() {
088            return ACTIVEMQ_BYTES_MESSAGE;
089        }
090    
091        /**
092         * @return Returns a shallow copy of the message instance
093         * @throws JMSException
094         */
095        public ActiveMQMessage shallowCopy() throws JMSException {
096            ActiveMQBytesMessage other = new ActiveMQBytesMessage();
097            this.initializeOther(other);
098            try {
099                other.setBodyAsBytes(this.getBodyAsBytes());
100            }
101            catch (IOException e) {
102                JMSException jmsEx = new JMSException("setBodyAsBytes() failed");
103                jmsEx.setLinkedException(e);
104                throw jmsEx;
105            }
106            return other;
107        }
108    
109        /**
110         * @return Returns a deep copy of the message - note the header fields are only shallow copied
111         * @throws JMSException
112         */
113        public ActiveMQMessage deepCopy() throws JMSException {
114            ActiveMQBytesMessage other = new ActiveMQBytesMessage();
115            this.initializeOther(other);
116            try {
117                if (this.getBodyAsBytes() != null) {
118                    ByteArray data = this.getBodyAsBytes().copy();
119                    other.setBodyAsBytes(data);
120                }
121            }
122            catch (IOException e) {
123                JMSException jmsEx = new JMSException("setBodyAsBytes() failed");
124                jmsEx.setLinkedException(e);
125                throw jmsEx;
126            }
127            return other;
128        }
129    
130        /**
131         * @param bodyAsBytes The bodyAsBytes to set.
132         * @param offset
133         * @param length
134         */
135        public void setBodyAsBytes(byte[] bodyAsBytes,int offset, int length) {
136            super.setBodyAsBytes(bodyAsBytes,offset,length);
137            dataOut = null;
138            dataIn = null;
139        }
140    
141        /**
142         * @return Returns the data body
143         * @throws IOException if an exception occurs retreiving the data
144         */
145        public ByteArray getBodyAsBytes() throws IOException {
146            if (this.dataOut != null) {
147                this.dataOut.flush();
148                byte[] data = this.bytesOut.toByteArray();
149                super.setBodyAsBytes(data,0,data.length);
150                dataOut.close();
151                dataOut = null;
152            }
153            return super.getBodyAsBytes();
154        }
155        
156        
157    
158        /**
159         * Clears out the message body. Clearing a message's body does not clear its header values or property entries.
160         * <P>
161         * If this message body was read-only, calling this method leaves the message body in the same state as an empty
162         * body in a newly created message.
163         *
164         * @throws JMSException if the JMS provider fails to clear the message body due to some internal error.
165         */
166        public void clearBody() throws JMSException {
167            super.clearBody();
168            this.dataOut = null;
169            this.dataIn = null;
170            this.bytesOut = null;
171        }
172    
173        /**
174         * Gets the number of bytes of the message body when the message is in read-only mode. The value returned can be
175         * used to allocate a byte array. The value returned is the entire length of the message body, regardless of where
176         * the pointer for reading the message is currently located.
177         *
178         * @return number of bytes in the message
179         * @throws JMSException                if the JMS provider fails to read the message due to some internal error.
180         * @throws MessageNotReadableException if the message is in write-only mode.
181         * @since 1.1
182         */
183        public long getBodyLength() throws JMSException {
184            initializeReading();
185            return bodyLength;
186        }
187    
188        /**
189         * Reads a <code>boolean</code> from the bytes message stream.
190         *
191         * @return the <code>boolean</code> value read
192         * @throws JMSException                if the JMS provider fails to read the message due to some internal error.
193         * @throws MessageEOFException         if unexpected end of bytes stream has been reached.
194         * @throws MessageNotReadableException if the message is in write-only mode.
195         */
196        public boolean readBoolean() throws JMSException {
197            initializeReading();
198            try {
199                return this.dataIn.readBoolean();
200            }
201            catch (EOFException eof) {
202                JMSException jmsEx = new MessageEOFException(eof.getMessage());
203                jmsEx.setLinkedException(eof);
204                throw jmsEx;
205            }
206            catch (IOException ioe) {
207                JMSException jmsEx = new JMSException("Format error occured" + ioe.getMessage());
208                jmsEx.setLinkedException(ioe);
209                throw jmsEx;
210            }
211        }
212    
213        /**
214         * Reads a signed 8-bit value from the bytes message stream.
215         *
216         * @return the next byte from the bytes message stream as a signed 8-bit <code>byte</code>
217         * @throws JMSException                if the JMS provider fails to read the message due to some internal error.
218         * @throws MessageEOFException         if unexpected end of bytes stream has been reached.
219         * @throws MessageNotReadableException if the message is in write-only mode.
220         */
221        public byte readByte() throws JMSException {
222            initializeReading();
223            try {
224                return this.dataIn.readByte();
225            }
226            catch (EOFException eof) {
227                JMSException jmsEx = new MessageEOFException(eof.getMessage());
228                jmsEx.setLinkedException(eof);
229                throw jmsEx;
230            }
231            catch (IOException ioe) {
232                JMSException jmsEx = new JMSException("Format error occured" + ioe.getMessage());
233                jmsEx.setLinkedException(ioe);
234                throw jmsEx;
235            }
236        }
237    
238        /**
239         * Reads an unsigned 8-bit number from the bytes message stream.
240         *
241         * @return the next byte from the bytes message stream, interpreted as an unsigned 8-bit number
242         * @throws JMSException                if the JMS provider fails to read the message due to some internal error.
243         * @throws MessageEOFException         if unexpected end of bytes stream has been reached.
244         * @throws MessageNotReadableException if the message is in write-only mode.
245         */
246        public int readUnsignedByte() throws JMSException {
247            initializeReading();
248            try {
249                return this.dataIn.readUnsignedByte();
250            }
251            catch (EOFException eof) {
252                JMSException jmsEx = new MessageEOFException(eof.getMessage());
253                jmsEx.setLinkedException(eof);
254                throw jmsEx;
255            }
256            catch (IOException ioe) {
257                JMSException jmsEx = new JMSException("Format error occured" + ioe.getMessage());
258                jmsEx.setLinkedException(ioe);
259                throw jmsEx;
260            }
261        }
262    
263        /**
264         * Reads a signed 16-bit number from the bytes message stream.
265         *
266         * @return the next two bytes from the bytes message stream, interpreted as a signed 16-bit number
267         * @throws JMSException                if the JMS provider fails to read the message due to some internal error.
268         * @throws MessageEOFException         if unexpected end of bytes stream has been reached.
269         * @throws MessageNotReadableException if the message is in write-only mode.
270         */
271        public short readShort() throws JMSException {
272            initializeReading();
273            try {
274                return this.dataIn.readShort();
275            }
276            catch (EOFException eof) {
277                JMSException jmsEx = new MessageEOFException(eof.getMessage());
278                jmsEx.setLinkedException(eof);
279                throw jmsEx;
280            }
281            catch (IOException ioe) {
282                JMSException jmsEx = new JMSException("Format error occured" + ioe.getMessage());
283                jmsEx.setLinkedException(ioe);
284                throw jmsEx;
285            }
286        }
287    
288        /**
289         * Reads an unsigned 16-bit number from the bytes message stream.
290         *
291         * @return the next two bytes from the bytes message stream, interpreted as an unsigned 16-bit integer
292         * @throws JMSException                if the JMS provider fails to read the message due to some internal error.
293         * @throws MessageEOFException         if unexpected end of bytes stream has been reached.
294         * @throws MessageNotReadableException if the message is in write-only mode.
295         */
296        public int readUnsignedShort() throws JMSException {
297            initializeReading();
298            try {
299                return this.dataIn.readUnsignedShort();
300            }
301            catch (EOFException eof) {
302                JMSException jmsEx = new MessageEOFException(eof.getMessage());
303                jmsEx.setLinkedException(eof);
304                throw jmsEx;
305            }
306            catch (IOException ioe) {
307                JMSException jmsEx = new JMSException("Format error occured" + ioe.getMessage());
308                jmsEx.setLinkedException(ioe);
309                throw jmsEx;
310            }
311        }
312    
313        /**
314         * Reads a Unicode character value from the bytes message stream.
315         *
316         * @return the next two bytes from the bytes message stream as a Unicode character
317         * @throws JMSException                if the JMS provider fails to read the message due to some internal error.
318         * @throws MessageEOFException         if unexpected end of bytes stream has been reached.
319         * @throws MessageNotReadableException if the message is in write-only mode.
320         */
321        public char readChar() throws JMSException {
322            initializeReading();
323            try {
324                return this.dataIn.readChar();
325            }
326            catch (EOFException eof) {
327                JMSException jmsEx = new MessageEOFException(eof.getMessage());
328                jmsEx.setLinkedException(eof);
329                throw jmsEx;
330            }
331            catch (IOException ioe) {
332                JMSException jmsEx = new JMSException("Format error occured" + ioe.getMessage());
333                jmsEx.setLinkedException(ioe);
334                throw jmsEx;
335            }
336        }
337    
338        /**
339         * Reads a signed 32-bit integer from the bytes message stream.
340         *
341         * @return the next four bytes from the bytes message stream, interpreted as an <code>int</code>
342         * @throws JMSException                if the JMS provider fails to read the message due to some internal error.
343         * @throws MessageEOFException         if unexpected end of bytes stream has been reached.
344         * @throws MessageNotReadableException if the message is in write-only mode.
345         */
346        public int readInt() throws JMSException {
347            initializeReading();
348            try {
349                return this.dataIn.readInt();
350            }
351            catch (EOFException eof) {
352                JMSException jmsEx = new MessageEOFException(eof.getMessage());
353                jmsEx.setLinkedException(eof);
354                throw jmsEx;
355            }
356            catch (IOException ioe) {
357                JMSException jmsEx = new JMSException("Format error occured" + ioe.getMessage());
358                jmsEx.setLinkedException(ioe);
359                throw jmsEx;
360            }
361        }
362    
363        /**
364         * Reads a signed 64-bit integer from the bytes message stream.
365         *
366         * @return the next eight bytes from the bytes message stream, interpreted as a <code>long</code>
367         * @throws JMSException                if the JMS provider fails to read the message due to some internal error.
368         * @throws MessageEOFException         if unexpected end of bytes stream has been reached.
369         * @throws MessageNotReadableException if the message is in write-only mode.
370         */
371        public long readLong() throws JMSException {
372            initializeReading();
373            try {
374                return this.dataIn.readLong();
375            }
376            catch (EOFException eof) {
377                JMSException jmsEx = new MessageEOFException(eof.getMessage());
378                jmsEx.setLinkedException(eof);
379                throw jmsEx;
380            }
381            catch (IOException ioe) {
382                JMSException jmsEx = new JMSException("Format error occured" + ioe.getMessage());
383                jmsEx.setLinkedException(ioe);
384                throw jmsEx;
385            }
386        }
387    
388        /**
389         * Reads a <code>float</code> from the bytes message stream.
390         *
391         * @return the next four bytes from the bytes message stream, interpreted as a <code>float</code>
392         * @throws JMSException                if the JMS provider fails to read the message due to some internal error.
393         * @throws MessageEOFException         if unexpected end of bytes stream has been reached.
394         * @throws MessageNotReadableException if the message is in write-only mode.
395         */
396        public float readFloat() throws JMSException {
397            initializeReading();
398            try {
399                return this.dataIn.readFloat();
400            }
401            catch (EOFException eof) {
402                JMSException jmsEx = new MessageEOFException(eof.getMessage());
403                jmsEx.setLinkedException(eof);
404                throw jmsEx;
405            }
406            catch (IOException ioe) {
407                JMSException jmsEx = new JMSException("Format error occured" + ioe.getMessage());
408                jmsEx.setLinkedException(ioe);
409                throw jmsEx;
410            }
411        }
412    
413        /**
414         * Reads a <code>double</code> from the bytes message stream.
415         *
416         * @return the next eight bytes from the bytes message stream, interpreted as a <code>double</code>
417         * @throws JMSException                if the JMS provider fails to read the message due to some internal error.
418         * @throws MessageEOFException         if unexpected end of bytes stream has been reached.
419         * @throws MessageNotReadableException if the message is in write-only mode.
420         */
421        public double readDouble() throws JMSException {
422            initializeReading();
423            try {
424                return this.dataIn.readDouble();
425            }
426            catch (EOFException eof) {
427                JMSException jmsEx = new MessageEOFException(eof.getMessage());
428                jmsEx.setLinkedException(eof);
429                throw jmsEx;
430            }
431            catch (IOException ioe) {
432                JMSException jmsEx = new JMSException("Format error occured" + ioe.getMessage());
433                jmsEx.setLinkedException(ioe);
434                throw jmsEx;
435            }
436        }
437    
438        /**
439         * Reads a string that has been encoded using a modified UTF-8 format from the bytes message stream.
440         * <P>
441         * For more information on the UTF-8 format, see "File System Safe UCS Transformation Format (FSS_UTF)", X/Open
442         * Preliminary Specification, X/Open Company Ltd., Document Number: P316. This information also appears in ISO/IEC
443         * 10646, Annex P.
444         *
445         * @return a Unicode string from the bytes message stream
446         * @throws JMSException                if the JMS provider fails to read the message due to some internal error.
447         * @throws MessageEOFException         if unexpected end of bytes stream has been reached.
448         * @throws MessageNotReadableException if the message is in write-only mode.
449         */
450        public String readUTF() throws JMSException {
451            initializeReading();
452            try {
453                return this.dataIn.readUTF();
454            }
455            catch (EOFException eof) {
456                JMSException jmsEx = new MessageEOFException(eof.getMessage());
457                jmsEx.setLinkedException(eof);
458                throw jmsEx;
459            }
460            catch (IOException ioe) {
461                JMSException jmsEx = new JMSException("Format error occured" + ioe.getMessage());
462                jmsEx.setLinkedException(ioe);
463                throw jmsEx;
464            }
465        }
466    
467        /**
468         * Reads a byte array from the bytes message stream.
469         * <P>
470         * If the length of array <code>value</code> is less than the number of bytes remaining to be read from the
471         * stream, the array should be filled. A subsequent call reads the next increment, and so on.
472         * <P>
473         * If the number of bytes remaining in the stream is less than the length of array <code>value</code>, the bytes
474         * should be read into the array. The return value of the total number of bytes read will be less than the length
475         * of the array, indicating that there are no more bytes left to be read from the stream. The next read of the
476         * stream returns -1.
477         *
478         * @param value the buffer into which the data is read
479         * @return the total number of bytes read into the buffer, or -1 if there is no more data because the end of the
480         *         stream has been reached
481         * @throws JMSException                if the JMS provider fails to read the message due to some internal error.
482         * @throws MessageNotReadableException if the message is in write-only mode.
483         */
484        public int readBytes(byte[] value) throws JMSException {
485            return readBytes(value, value.length);
486        }
487    
488        /**
489         * Reads a portion of the bytes message stream.
490         * <P>
491         * If the length of array <code>value</code> is less than the number of bytes remaining to be read from the
492         * stream, the array should be filled. A subsequent call reads the next increment, and so on.
493         * <P>
494         * If the number of bytes remaining in the stream is less than the length of array <code>value</code>, the bytes
495         * should be read into the array. The return value of the total number of bytes read will be less than the length
496         * of the array, indicating that there are no more bytes left to be read from the stream. The next read of the
497         * stream returns -1.
498         * <p/>
499         * If <code>length</code> is negative, or <code>length</code> is greater than the length of the array <code>value</code>,
500         * then an <code>IndexOutOfBoundsException</code> is thrown. No bytes will be read from the stream for this
501         * exception case.
502         *
503         * @param value  the buffer into which the data is read
504         * @param length the number of bytes to read; must be less than or equal to <code>value.length</code>
505         * @return the total number of bytes read into the buffer, or -1 if there is no more data because the end of the
506         *         stream has been reached
507         * @throws JMSException                if the JMS provider fails to read the message due to some internal error.
508         * @throws MessageNotReadableException if the message is in write-only mode.
509         */
510        public int readBytes(byte[] value, int length) throws JMSException {
511            initializeReading();
512            try {
513                int n = 0;
514                while (n < length) {
515                    int count = this.dataIn.read(value, n, length - n);
516                    if (count < 0) {
517                        break;
518                    }
519                    n += count;
520                }
521                if (n == 0 && length > 0) {
522                    n = -1;
523                }
524                return n;
525            }
526            catch (EOFException eof) {
527                JMSException jmsEx = new MessageEOFException(eof.getMessage());
528                jmsEx.setLinkedException(eof);
529                throw jmsEx;
530            }
531            catch (IOException ioe) {
532                JMSException jmsEx = new JMSException("Format error occured" + ioe.getMessage());
533                jmsEx.setLinkedException(ioe);
534                throw jmsEx;
535            }
536        }
537    
538        /**
539         * Writes a <code>boolean</code> to the bytes message stream as a 1-byte value. The value <code>true</code> is
540         * written as the value <code>(byte)1</code>; the value <code>false</code> is written as the value <code>(byte)0</code>.
541         *
542         * @param value the <code>boolean</code> value to be written
543         * @throws JMSException                 if the JMS provider fails to write the message due to some internal error.
544         * @throws MessageNotWriteableException if the message is in read-only mode.
545         */
546        public void writeBoolean(boolean value) throws JMSException {
547            initializeWriting();
548            try {
549                this.dataOut.writeBoolean(value);
550            }
551            catch (IOException ioe) {
552                JMSException jmsEx = new JMSException("Could not write data:" + ioe.getMessage());
553                jmsEx.setLinkedException(ioe);
554                throw jmsEx;
555            }
556        }
557    
558        /**
559         * Writes a <code>byte</code> to the bytes message stream as a 1-byte value.
560         *
561         * @param value the <code>byte</code> value to be written
562         * @throws JMSException                 if the JMS provider fails to write the message due to some internal error.
563         * @throws MessageNotWriteableException if the message is in read-only mode.
564         */
565        public void writeByte(byte value) throws JMSException {
566            initializeWriting();
567            try {
568                this.dataOut.writeByte(value);
569            }
570            catch (IOException ioe) {
571                JMSException jmsEx = new JMSException("Could not write data:" + ioe.getMessage());
572                jmsEx.setLinkedException(ioe);
573                throw jmsEx;
574            }
575        }
576    
577        /**
578         * Writes a <code>short</code> to the bytes message stream as two bytes, high byte first.
579         *
580         * @param value the <code>short</code> to be written
581         * @throws JMSException                 if the JMS provider fails to write the message due to some internal error.
582         * @throws MessageNotWriteableException if the message is in read-only mode.
583         */
584        public void writeShort(short value) throws JMSException {
585            initializeWriting();
586            try {
587                this.dataOut.writeShort(value);
588            }
589            catch (IOException ioe) {
590                JMSException jmsEx = new JMSException("Could not write data:" + ioe.getMessage());
591                jmsEx.setLinkedException(ioe);
592                throw jmsEx;
593            }
594        }
595    
596        /**
597         * Writes a <code>char</code> to the bytes message stream as a 2-byte value, high byte first.
598         *
599         * @param value the <code>char</code> value to be written
600         * @throws JMSException                 if the JMS provider fails to write the message due to some internal error.
601         * @throws MessageNotWriteableException if the message is in read-only mode.
602         */
603        public void writeChar(char value) throws JMSException {
604            initializeWriting();
605            try {
606                this.dataOut.writeChar(value);
607            }
608            catch (IOException ioe) {
609                JMSException jmsEx = new JMSException("Could not write data:" + ioe.getMessage());
610                jmsEx.setLinkedException(ioe);
611                throw jmsEx;
612            }
613        }
614    
615        /**
616         * Writes an <code>int</code> to the bytes message stream as four bytes, high byte first.
617         *
618         * @param value the <code>int</code> to be written
619         * @throws JMSException                 if the JMS provider fails to write the message due to some internal error.
620         * @throws MessageNotWriteableException if the message is in read-only mode.
621         */
622        public void writeInt(int value) throws JMSException {
623            initializeWriting();
624            try {
625                this.dataOut.writeInt(value);
626            }
627            catch (IOException ioe) {
628                JMSException jmsEx = new JMSException("Could not write data:" + ioe.getMessage());
629                jmsEx.setLinkedException(ioe);
630                throw jmsEx;
631            }
632        }
633    
634        /**
635         * Writes a <code>long</code> to the bytes message stream as eight bytes, high byte first.
636         *
637         * @param value the <code>long</code> to be written
638         * @throws JMSException                 if the JMS provider fails to write the message due to some internal error.
639         * @throws MessageNotWriteableException if the message is in read-only mode.
640         */
641        public void writeLong(long value) throws JMSException {
642            initializeWriting();
643            try {
644                this.dataOut.writeLong(value);
645            }
646            catch (IOException ioe) {
647                JMSException jmsEx = new JMSException("Could not write data:" + ioe.getMessage());
648                jmsEx.setLinkedException(ioe);
649                throw jmsEx;
650            }
651        }
652    
653        /**
654         * Converts the <code>float</code> argument to an <code>int</code> using the <code>floatToIntBits</code>
655         * method in class <code>Float</code>, and then writes that <code>int</code> value to the bytes message stream
656         * as a 4-byte quantity, high byte first.
657         *
658         * @param value the <code>float</code> value to be written
659         * @throws JMSException                 if the JMS provider fails to write the message due to some internal error.
660         * @throws MessageNotWriteableException if the message is in read-only mode.
661         */
662        public void writeFloat(float value) throws JMSException {
663            initializeWriting();
664            try {
665                this.dataOut.writeFloat(value);
666            }
667            catch (IOException ioe) {
668                JMSException jmsEx = new JMSException("Could not write data:" + ioe.getMessage());
669                jmsEx.setLinkedException(ioe);
670                throw jmsEx;
671            }
672        }
673    
674        /**
675         * Converts the <code>double</code> argument to a <code>long</code> using the <code>doubleToLongBits</code>
676         * method in class <code>Double</code>, and then writes that <code>long</code> value to the bytes message
677         * stream as an 8-byte quantity, high byte first.
678         *
679         * @param value the <code>double</code> value to be written
680         * @throws JMSException                 if the JMS provider fails to write the message due to some internal error.
681         * @throws MessageNotWriteableException if the message is in read-only mode.
682         */
683        public void writeDouble(double value) throws JMSException {
684            initializeWriting();
685            try {
686                this.dataOut.writeDouble(value);
687            }
688            catch (IOException ioe) {
689                JMSException jmsEx = new JMSException("Could not write data:" + ioe.getMessage());
690                jmsEx.setLinkedException(ioe);
691                throw jmsEx;
692            }
693        }
694    
695        /**
696         * Writes a string to the bytes message stream using UTF-8 encoding in a machine-independent manner.
697         * <P>
698         * For more information on the UTF-8 format, see "File System Safe UCS Transformation Format (FSS_UTF)", X/Open
699         * Preliminary Specification, X/Open Company Ltd., Document Number: P316. This information also appears in ISO/IEC
700         * 10646, Annex P.
701         *
702         * @param value the <code>String</code> value to be written
703         * @throws JMSException                 if the JMS provider fails to write the message due to some internal error.
704         * @throws MessageNotWriteableException if the message is in read-only mode.
705         */
706        public void writeUTF(String value) throws JMSException {
707            initializeWriting();
708            try {
709                this.dataOut.writeUTF(value);
710            }
711            catch (IOException ioe) {
712                JMSException jmsEx = new JMSException("Could not write data:" + ioe.getMessage());
713                jmsEx.setLinkedException(ioe);
714                throw jmsEx;
715            }
716        }
717    
718        /**
719         * Writes a byte array to the bytes message stream.
720         *
721         * @param value the byte array to be written
722         * @throws JMSException                 if the JMS provider fails to write the message due to some internal error.
723         * @throws MessageNotWriteableException if the message is in read-only mode.
724         */
725        public void writeBytes(byte[] value) throws JMSException {
726            initializeWriting();
727            try {
728                this.dataOut.write(value);
729            }
730            catch (IOException ioe) {
731                JMSException jmsEx = new JMSException("Could not write data:" + ioe.getMessage());
732                jmsEx.setLinkedException(ioe);
733                throw jmsEx;
734            }
735        }
736    
737        /**
738         * Writes a portion of a byte array to the bytes message stream.
739         *
740         * @param value  the byte array value to be written
741         * @param offset the initial offset within the byte array
742         * @param length the number of bytes to use
743         * @throws JMSException                 if the JMS provider fails to write the message due to some internal error.
744         * @throws MessageNotWriteableException if the message is in read-only mode.
745         */
746        public void writeBytes(byte[] value, int offset, int length) throws JMSException {
747            initializeWriting();
748            try {
749                this.dataOut.write(value, offset, length);
750            }
751            catch (IOException ioe) {
752                JMSException jmsEx = new JMSException("Could not write data:" + ioe.getMessage());
753                jmsEx.setLinkedException(ioe);
754                throw jmsEx;
755            }
756        }
757    
758        /**
759         * Writes an object to the bytes message stream.
760         * <P>
761         * This method works only for the objectified primitive object types (<code>Integer</code>,<code>Double</code>,
762         * <code>Long</code> &nbsp;...), <code>String</code> objects, and byte arrays.
763         *
764         * @param value the object in the Java programming language ("Java object") to be written; it must not be null
765         * @throws JMSException                   if the JMS provider fails to write the message due to some internal error.
766         * @throws MessageFormatException         if the object is of an invalid type.
767         * @throws MessageNotWriteableException   if the message is in read-only mode.
768         * @throws java.lang.NullPointerException if the parameter <code>value</code> is null.
769         */
770        public void writeObject(Object value) throws JMSException {
771            if (value == null) {
772                throw new NullPointerException();
773            }
774            initializeWriting();
775            if (value instanceof Boolean) {
776                writeBoolean(((Boolean) value).booleanValue());
777            }
778            else if (value instanceof Character) {
779                writeChar(((Character) value).charValue());
780            }
781            else if (value instanceof Byte) {
782                writeByte(((Byte) value).byteValue());
783            }
784            else if (value instanceof Short) {
785                writeShort(((Short) value).shortValue());
786            }
787            else if (value instanceof Integer) {
788                writeInt(((Integer) value).intValue());
789            }
790            else if (value instanceof Double) {
791                writeDouble(((Double) value).doubleValue());
792            }
793            else if (value instanceof Long) {
794                writeLong(((Long) value).longValue());
795            }
796            else if (value instanceof Float) {
797                writeFloat(((Float) value).floatValue());
798            }
799            else if (value instanceof Double) {
800                writeDouble(((Double) value).doubleValue());
801            }
802            else if (value instanceof String) {
803                writeUTF(value.toString());
804            }
805            else if (value instanceof byte[]) {
806                writeBytes((byte[]) value);
807            }
808            else {
809                throw new MessageFormatException("Cannot write non-primitive type:" + value.getClass());
810            }
811        }
812    
813        /**
814         * Puts the message body in read-only mode and repositions the stream of bytes to the beginning.
815         * 
816         * @throws JMSException if an internal error occurs
817         */
818        public void reset() throws JMSException {
819            super.readOnlyMessage = true;
820            if (this.dataOut != null) {
821                try {
822                    this.dataOut.flush();
823                    byte[] data = this.bytesOut.toByteArray();
824                    super.setBodyAsBytes(data,0,data.length);
825                    dataOut.close();
826                }
827                catch (IOException ioe) {
828                    JMSException jmsEx = new JMSException("reset failed: " + ioe.getMessage());
829                    jmsEx.setLinkedException(ioe);
830                    throw jmsEx;
831                }
832            }
833            this.bytesOut = null;
834            this.dataIn = null;
835            this.dataOut = null;
836        }
837    
838        private void initializeWriting() throws MessageNotWriteableException {
839            if (super.readOnlyMessage) {
840                throw new MessageNotWriteableException("This message is in read-only mode");
841            }
842            if (this.dataOut == null) {
843                this.bytesOut = new ByteArrayOutputStream();
844                this.dataOut = new DataOutputStream(this.bytesOut);
845            }
846        }
847    
848        private void initializeReading() throws MessageNotReadableException {
849            if (!super.readOnlyMessage) {
850                throw new MessageNotReadableException("This message is in write-only mode");
851            }
852            try {
853                ByteArray data = super.getBodyAsBytes();
854                if (this.dataIn == null && data != null) {
855                    if (ByteArrayCompression.isCompressed(data)){
856                        ByteArrayCompression compression = new ByteArrayCompression();
857                        data = compression.inflate(data);
858                    }
859                    this.bodyLength = data.getLength();
860                    ByteArrayInputStream bytesIn = new ByteArrayInputStream(data.getBuf(),data.getOffset(),data.getLength());
861                    this.dataIn = new DataInputStream(bytesIn);
862                }
863            }
864            catch (IOException e) {
865                MessageNotReadableException mnr = new MessageNotReadableException("getBodyAsBytes failed");
866                mnr.setLinkedException(e);
867                throw mnr;
868            }
869        }
870    
871        public String toString() {
872            return super.toString() + " ActiveMQBytesMessage{ " +
873                    "bytesOut = " + bytesOut +
874                    ", dataOut = " + dataOut +
875                    ", dataIn = " + dataIn +
876                    " }";
877        }
878    }