View Javadoc

1   /***
2    * Redistribution and use of this software and associated documentation
3    * ("Software"), with or without modification, are permitted provided
4    * that the following conditions are met:
5    *
6    * 1. Redistributions of source code must retain copyright
7    *    statements and notices.  Redistributions must also contain a
8    *    copy of this document.
9    *
10   * 2. Redistributions in binary form must reproduce the
11   *    above copyright notice, this list of conditions and the
12   *    following disclaimer in the documentation and/or other
13   *    materials provided with the distribution.
14   *
15   * 3. The name "Exolab" must not be used to endorse or promote
16   *    products derived from this Software without prior written
17   *    permission of Exoffice Technologies.  For written permission,
18   *    please contact info@exolab.org.
19   *
20   * 4. Products derived from this Software may not be called "Exolab"
21   *    nor may "Exolab" appear in their names without prior written
22   *    permission of Exoffice Technologies. Exolab is a registered
23   *    trademark of Exoffice Technologies.
24   *
25   * 5. Due credit should be given to the Exolab Project
26   *    (http://www.exolab.org/).
27   *
28   * THIS SOFTWARE IS PROVIDED BY EXOFFICE TECHNOLOGIES AND CONTRIBUTORS
29   * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
30   * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
31   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
32   * EXOFFICE TECHNOLOGIES OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
33   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
34   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
35   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
37   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
38   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
39   * OF THE POSSIBILITY OF SUCH DAMAGE.
40   *
41   * Copyright 2000-2004 (C) Exoffice Technologies Inc. All Rights Reserved.
42   *
43   * $Id: BytesMessageImpl.java,v 1.12 2004/01/29 12:00:37 tanderson Exp $
44   *
45   * Date         Author  Changes
46   * 02/26/2000   jimm    Created
47   */
48  
49  package org.exolab.jms.message;
50  
51  import java.io.ByteArrayInputStream;
52  import java.io.ByteArrayOutputStream;
53  import java.io.DataInputStream;
54  import java.io.DataOutputStream;
55  import java.io.EOFException;
56  import java.io.IOException;
57  import java.io.ObjectInput;
58  import java.io.ObjectOutput;
59  import java.io.UTFDataFormatException;
60  
61  import javax.jms.BytesMessage;
62  import javax.jms.JMSException;
63  import javax.jms.MessageEOFException;
64  import javax.jms.MessageFormatException;
65  import javax.jms.MessageNotReadableException;
66  import javax.jms.MessageNotWriteableException;
67  
68  
69  /***
70   * This class implements the {@link BytesMessage} interface
71   *
72   * @version     $Revision: 1.12 $ $Date: 2004/01/29 12:00:37 $
73   * @author      <a href="mailto:mourikis@intalio.com">Jim Mourikis</a>
74   * @author      <a href="mailto:tima@intalio.com">Tim Anderson</a>
75   * @see         BytesMessage
76   */
77  public final class BytesMessageImpl extends MessageImpl
78      implements BytesMessage {
79  
80      /***
81       * Object version no. for serialization
82       */
83      static final long serialVersionUID = 1;
84  
85      /***
86       * Empty byte array for initialisation purposes
87       */
88      private static final byte[] EMPTY = new byte[]{};
89  
90      /***
91       * The byte stream to store data
92       */
93      private byte[] _bytes = EMPTY;
94  
95      /***
96       * The stream used for writes
97       */
98      private DataOutputStream _out = null;
99  
100     /***
101      * The byte stream backing the output stream
102      */
103     private ByteArrayOutputStream _byteOut = null;
104 
105     /***
106      * The stream used for reads
107      */
108     private DataInputStream _in = null;
109 
110     /***
111      * The byte stream backing the input stream
112      */
113     private ByteArrayInputStream _byteIn = null;
114 
115     /***
116      * The offset of the byte stream to start reading from. This defaults
117      * to 0, and only applies to messages that are cloned from a
118      * read-only instance where part of the stream had already been read.
119      */
120     private int _offset = 0;
121 
122     /***
123      * Construct a new BytesMessage. When first created, the message is in
124      * write-only mode.
125      *
126      * @throws JMSException if the message type can't be set
127      */
128     public BytesMessageImpl() throws JMSException {
129         setJMSType("BytesMessage");
130     }
131 
132     /***
133      * Clone an instance of this object
134      *
135      * @return a copy of this object
136      * @throws CloneNotSupportedException if object or attributes aren't
137      * cloneable
138      */
139     public final Object clone() throws CloneNotSupportedException {
140         BytesMessageImpl result = (BytesMessageImpl) super.clone();
141         if (_bodyReadOnly) {
142             result._bytes = new byte[_bytes.length];
143             System.arraycopy(_bytes, 0, result._bytes, 0, _bytes.length);
144             if (_byteIn != null) {
145                 // if a client subsequently reads from the cloned object,
146                 // start reading from offset of the original stream
147                 _offset = _bytes.length - _byteIn.available();
148             }
149             result._byteIn = null;
150             result._in = null;
151         } else {
152             if (_out != null) {
153                 try {
154                     _out.flush();
155                 } catch (IOException exception) {
156                     throw new CloneNotSupportedException(
157                         exception.getMessage());
158                 }
159                 result._bytes = _byteOut.toByteArray();
160                 result._byteOut = null;
161                 result._out = null;
162             } else {
163                 result._bytes = new byte[_bytes.length];
164                 System.arraycopy(_bytes, 0, result._bytes, 0, _bytes.length);
165             }
166         }
167 
168         return result;
169     }
170 
171     /***
172      * Serialize out this message's data
173      *
174      * @param out the stream to serialize out to
175      * @throws IOException if any I/O exceptions occurr
176      */
177     public final void writeExternal(ObjectOutput out) throws IOException {
178         // If it was in write mode, extract the byte array.
179         if (!_bodyReadOnly && _out != null) {
180             _out.flush();
181             _bytes = _byteOut.toByteArray();
182         }
183 
184         super.writeExternal(out);
185         out.writeLong(serialVersionUID);
186         out.writeInt(_bytes.length);
187         out.write(_bytes);
188         out.flush();
189     }
190 
191     /***
192      * Serialize in this message's data
193      *
194      * @param in the stream to serialize in from
195      * @throws ClassNotFoundException if the class for an object being
196      * restored cannot be found.
197      * @throws IOException if any I/O exceptions occur
198      */
199     public final void readExternal(ObjectInput in)
200         throws ClassNotFoundException, IOException {
201         super.readExternal(in);
202         long version = in.readLong();
203         if (version == serialVersionUID) {
204             int length = in.readInt();
205             _bytes = new byte[length];
206             in.readFully(_bytes);
207         } else {
208             throw new IOException("Incorrect version enountered: " + version +
209                 ". This version = " + serialVersionUID);
210         }
211     }
212 
213     /***
214      * Read a <code>boolean</code> from the bytes message stream
215      *
216      * @return the <code>boolean</code> value read
217      * @throws JMSException if JMS fails to read message due to some internal
218      * JMS error
219      * @throws MessageEOFException if end of bytes stream
220      * @throws MessageNotReadableException if message is in write-only mode
221      */
222     public final boolean readBoolean() throws JMSException {
223         boolean result = false;
224         prepare();
225         try {
226             result = _in.readBoolean();
227         } catch (IOException exception) {
228             revert(exception);
229         }
230         return result;
231     }
232 
233     /***
234      * Read a signed 8-bit value from the bytes message stream
235      *
236      * @return the next byte from the bytes message stream as a signed 8-bit
237      * <code>byte</code>
238      * @throws JMSException if JMS fails to read message due to some internal
239      * JMS error
240      * @throws MessageEOFException if end of message stream
241      * @throws MessageNotReadableException if message is in write-only mode
242      */
243     public final byte readByte() throws JMSException {
244         byte result = 0;
245         prepare();
246         try {
247             result = _in.readByte();
248         } catch (IOException exception) {
249             revert(exception);
250         }
251         return result;
252     }
253 
254     /***
255      * Read an unsigned 8-bit number from the bytes message stream
256      *
257      * @return the next byte from the bytes message stream, interpreted as an
258      * unsigned 8-bit number
259      * @throws JMSException if JMS fails to read message due to some internal
260      * JMS error
261      * @throws MessageNotReadableException if message is in write-only mode
262      * @throws MessageEOFException if end of message stream
263      */
264     public final int readUnsignedByte() throws JMSException {
265         int result = 0;
266         prepare();
267         try {
268             result = _in.readUnsignedByte();
269         } catch (IOException exception) {
270             revert(exception);
271         }
272         return result;
273     }
274 
275     /***
276      * Read a signed 16-bit number from the bytes message stream
277      *
278      * @return the next two bytes from the bytes message stream, interpreted
279      * as a signed 16-bit number
280      * @throws JMSException if JMS fails to read message due to some internal
281      * JMS error
282      * @throws MessageEOFException if end of message stream
283      * @throws MessageNotReadableException if message is in write-only mode
284      */
285     public final short readShort() throws JMSException {
286         short result = 0;
287         prepare();
288         try {
289             result = _in.readShort();
290         } catch (IOException exception) {
291             revert(exception);
292         }
293         return result;
294     }
295 
296     /***
297      * Read an unsigned 16-bit number from the bytes message stream
298      *
299      * @return the next two bytes from the bytes message stream, interpreted
300      * as an unsigned 16-bit integer
301      *
302      * @throws JMSException if JMS fails to read message due to some internal
303      * JMS error
304      * @throws MessageEOFException if end of message stream
305      * @throws MessageNotReadableException if message is in write-only mode
306      */
307     public final int readUnsignedShort() throws JMSException {
308         int result = 0;
309         prepare();
310         try {
311             result = _in.readUnsignedShort();
312         } catch (IOException exception) {
313             revert(exception);
314         }
315         return result;
316     }
317 
318     /***
319      * Read a Unicode character value from the bytes message stream
320      *
321      * @return the next two bytes from the bytes message stream as a Unicode
322      * character
323      * @throws JMSException if JMS fails to read message due to some internal
324      * JMS error
325      * @throws MessageEOFException if end of message stream
326      * @throws MessageNotReadableException if message is in write-only mode
327      */
328     public final char readChar() throws JMSException {
329         char result = 0;
330         prepare();
331         try {
332             result = _in.readChar();
333         } catch (IOException exception) {
334             revert(exception);
335         }
336         return result;
337     }
338 
339     /***
340      * Read a signed 32-bit integer from the bytes message stream
341      *
342      * @return the next four bytes from the bytes message stream, interpreted
343      * as an <code>int</code>
344      * @throws JMSException if JMS fails to read message due to some internal
345      * JMS error
346      * @throws MessageEOFException if end of message stream
347      * @throws MessageNotReadableException if message is in write-only mode
348      */
349     public final int readInt() throws JMSException {
350         int result = 0;
351         prepare();
352         try {
353             result = _in.readInt();
354         } catch (IOException exception) {
355             revert(exception);
356         }
357         return result;
358     }
359 
360     /***
361      * Read a signed 64-bit integer from the bytes message stream
362      *
363      * @return the next eight bytes from the bytes message stream, interpreted
364      * as a <code>long</code>.
365      * @throws JMSException if JMS fails to read message due to some internal
366      * JMS error
367      * @throws MessageEOFException if end of message stream
368      * @throws MessageNotReadableException if message is in write-only mode
369      */
370     public final long readLong() throws JMSException {
371         long result = 0;
372         prepare();
373         try {
374             result = _in.readLong();
375         } catch (IOException exception) {
376             revert(exception);
377         }
378         return result;
379     }
380 
381     /***
382      * Read a <code>float</code> from the bytes message stream
383      *
384      * @return the next four bytes from the bytes message stream, interpreted
385      * as a <code>float</code>
386      * @throws JMSException if JMS fails to read message due to some internal
387      * JMS error
388      * @throws MessageEOFException if end of message stream
389      * @throws MessageNotReadableException if message is in write-only mode
390      */
391     public final float readFloat() throws JMSException {
392         float result = 0;
393         prepare();
394         try {
395             result = _in.readFloat();
396         } catch (IOException exception) {
397             revert(exception);
398         }
399         return result;
400     }
401 
402     /***
403      * Read a <code>double</code> from the bytes message stream
404      *
405      * @return the next eight bytes from the bytes message stream,
406      *			interpreted as a <code>double</code>
407      * @throws JMSException if JMS fails to read message due to some internal
408      * JMS error
409      * @throws MessageEOFException if end of message stream
410      * @throws MessageNotReadableException if message is in write-only mode
411      */
412     public final double readDouble() throws JMSException {
413         double result = 0;
414         prepare();
415         try {
416             result = _in.readDouble();
417         } catch (IOException exception) {
418             revert(exception);
419         }
420         return result;
421     }
422 
423     /***
424      * Read in a string that has been encoded using a modified UTF-8 format
425      * from the bytes message stream
426      *
427      * <p>For more information on the UTF-8 format, see "File System Safe
428      * UCS Transformation Format (FSS_UFT)", X/Open Preliminary Specification,
429      * X/Open Company Ltd., Document Number: P316. This information also
430      * appears in ISO/IEC 10646, Annex P.
431      *
432      * @return a Unicode string from the bytes message stream
433      * @throws JMSException if JMS fails to read message due to some internal
434      * JMS error
435      * @throws MessageEOFException if end of message stream
436      * @throws MessageFormatException if string has an invalid format
437      * @throws MessageNotReadableException if message is in write-only mode
438      */
439     public final String readUTF() throws JMSException {
440         String result = null;
441         prepare();
442         try {
443             result = _in.readUTF();
444         } catch (IOException exception) {
445             revert(exception);
446         }
447         return result;
448     }
449 
450     /***
451      * Read a byte array from the bytes message stream
452      *
453      * <p>If the length of array <code>value</code> is less than
454      * the bytes remaining to be read from the stream, the array should
455      * be filled. A subsequent call reads the next increment, etc.
456      *
457      * <p>If the bytes remaining in the stream is less than the length of
458      * array <code>value</code>, the bytes should be read into the array.
459      * The return value of the total number of bytes read will be less than
460      * the length of the array, indicating that there are no more bytes left
461      * to be read from the stream. The next read of the stream returns -1.
462      *
463      * @param value the buffer into which the data is read
464      * @return the total number of bytes read into the buffer, or -1 if
465      * there is no more data because the end of the stream has been reached
466      * @throws JMSException if JMS fails to read message due to some internal
467      * JMS error
468      * @throws MessageNotReadableException if message is in write-only mode
469      */
470     public final int readBytes(byte[] value) throws JMSException {
471         return readBytes(value, value.length);
472     }
473 
474     /***
475      * Read a portion of the bytes message stream.
476      *
477      * <p>If the length of array <code>value</code> is less than
478      * the bytes remaining to be read from the stream, the array should
479      * be filled. A subsequent call reads the next increment, etc.
480      *
481      * <p>If the bytes remaining in the stream is less than the length of
482      * array <code>value</code>, the bytes should be read into the array.
483      * The return value of the total number of bytes read will be less than
484      * the length of the array, indicating that there are no more bytes left
485      * to be read from the stream. The next read of the stream returns -1.
486      *
487      * <p> If <code>length</code> is negative, or
488      * <code>length</code> is greater than the length of the array
489      * <code>value</code>, then an <code>IndexOutOfBoundsException</code> is
490      * thrown. No bytes will be read from the stream for this exception case.
491      *
492      * @param value the buffer into which the data is read.
493      * @param length the number of bytes to read. Must be less than or equal
494      * to value.length.
495      * @return the total number of bytes read into the buffer, or -1 if
496      * there is no more data because the end of the stream has been reached.
497      * @throws IndexOutOfBoundsException if <code>length</code> is invalid
498      * @throws JMSException if JMS fails to read message due to some internal
499      * JMS error
500      * @throws MessageNotReadableException if message is in write-only mode
501      */
502     public final int readBytes(byte[] value, int length) throws JMSException {
503         int read = -1;
504         prepare();
505 
506         if (length < 0 || length > value.length) {
507             throw new IndexOutOfBoundsException(
508                 "Length must be > 0 and less than array size");
509         }
510         try {
511             _in.mark(length);
512             int remain = _in.available();
513             if (remain == 0) {
514                 read = -1;
515             } else if (length <= remain) {
516                 read = length;
517                 _in.read(value, 0, length);
518             } else {
519                 _in.readFully(value, 0, remain);
520                 read = remain;
521             }
522         } catch (EOFException ignore) {
523         } catch (IOException exception) {
524             revert(exception);
525         }
526         return read;
527     }
528 
529     /***
530      * Write a <code>boolean</code> to the bytes message stream as a 1-byte
531      * value.
532      * The value <code>true</code> is written out as the value
533      * <code>(byte)1</code>; the value <code>false</code> is written out as
534      * the value <code>(byte)0</code>.
535      *
536      * @param value the <code>boolean</code> value to be written
537      * @throws JMSException if JMS fails to write message due to some internal
538      * JMS error
539      * @throws MessageNotWriteableException if message is in read-only mode
540      */
541     public final void writeBoolean(boolean value) throws JMSException {
542         checkWrite();
543         try {
544             getOutputStream().writeBoolean(value);
545         } catch (IOException exception) {
546             raise(exception);
547         }
548     }
549 
550     /***
551      * Write out a <code>byte</code> to the bytes message stream as a 1-byte
552      * value
553      *
554      * @param value the <code>byte</code> value to be written
555      * @throws JMSException if JMS fails to write message due to some internal
556      * JMS error
557      * @throws MessageNotWriteableException if message is in read-only mode
558      */
559     public final void writeByte(byte value) throws JMSException {
560         checkWrite();
561         try {
562             getOutputStream().writeByte(value);
563         } catch (IOException exception) {
564             raise(exception);
565         }
566     }
567 
568     /***
569      * Write a <code>short</code> to the bytes message stream as two bytes,
570      * high byte first
571      *
572      * @param value the <code>short</code> to be written
573      * @throws JMSException if JMS fails to write message due to some internal
574      * JMS error
575      * @throws MessageNotWriteableException if message is in read-only mode
576      */
577     public final void writeShort(short value) throws JMSException {
578         checkWrite();
579         try {
580             getOutputStream().writeShort(value);
581         } catch (IOException exception) {
582             raise(exception);
583         }
584     }
585 
586     /***
587      * Write a <code>char</code> to the bytes message stream as a 2-byte
588      * value, high byte first.
589      *
590      * @param value the <code>char</code> value to be written
591      * @throws MessageNotWriteableException if message is in read-only mode
592      * @throws JMSException if JMS fails to write message due to some internal
593      * JMS error
594      */
595     public final void writeChar(char value) throws JMSException {
596         checkWrite();
597         try {
598             getOutputStream().writeChar(value);
599         } catch (IOException exception) {
600             raise(exception);
601         }
602     }
603 
604     /***
605      * Write an <code>int</code> to the bytes message stream as four bytes,
606      * high byte first.
607      *
608      * @param value the <code>int</code> to be written
609      * @throws JMSException if JMS fails to write message due to some internal
610      * JMS error
611      * @throws MessageNotWriteableException if message is in read-only mode
612      */
613     public final void writeInt(int value) throws JMSException {
614         checkWrite();
615         try {
616             getOutputStream().writeInt(value);
617         } catch (IOException exception) {
618             raise(exception);
619         }
620     }
621 
622     /***
623      * Write a <code>long</code> to the bytes message stream as eight bytes,
624      * high byte first
625      *
626      * @param value the <code>long</code> to be written
627      * @throws JMSException if JMS fails to write message due to some internal
628      * JMS error
629      * @throws MessageNotWriteableException if message is in read-only mode
630      */
631     public final void writeLong(long value) throws JMSException {
632         checkWrite();
633         try {
634             getOutputStream().writeLong(value);
635         } catch (IOException exception) {
636             raise(exception);
637         }
638     }
639 
640     /***
641      * Convert the float argument to an <code>int</code> using the
642      * <code>floatToIntBits</code> method in class <code>Float</code>,
643      * and then writes that <code>int</code> value to the bytes message
644      * stream as a 4-byte quantity, high byte first.
645      *
646      * @param value the <code>float</code> value to be written.
647      * @throws JMSException if JMS fails to write message due to some internal
648      * JMS error
649      * @throws MessageNotWriteableException if message is in read-only mode
650      */
651     public final void writeFloat(float value) throws JMSException {
652         checkWrite();
653         try {
654             getOutputStream().writeFloat(value);
655         } catch (IOException exception) {
656             raise(exception);
657         }
658     }
659 
660 
661     /***
662      * Convert the double argument to a <code>long</code> using the
663      * <code>doubleToLongBits</code> method in class <code>Double</code>,
664      * and then writes that <code>long</code> value to the bytes message
665      * stream as an 8-byte quantity, high byte first.
666      *
667      * @param value the <code>double</code> value to be written.
668      * @throws JMSException if JMS fails to write message due to some internal
669      * JMS error
670      * @throws MessageNotWriteableException if message is in read-only mode
671      */
672     public final void writeDouble(double value) throws JMSException {
673         checkWrite();
674         try {
675             getOutputStream().writeDouble(value);
676         } catch (IOException exception) {
677             raise(exception);
678         }
679     }
680 
681     /***
682      * Write a string to the bytes message stream using UTF-8 encoding in a
683      * machine-independent manner.
684      *
685      * <p>For more information on the UTF-8 format, see "File System Safe
686      * UCS Transformation Format (FSS_UFT)", X/Open Preliminary Specification,
687      * X/Open Company Ltd., Document Number: P316. This information also
688      * appears in ISO/IEC 10646, Annex P.
689      *
690      * @param value the <code>String</code> value to be written
691      * @throws MessageNotWriteableException if message is in read-only mode
692      * @throws JMSException if JMS fails to write message due to some internal
693      * JMS error
694      */
695     public final void writeUTF(String value) throws JMSException {
696         checkWrite();
697         try {
698             getOutputStream().writeUTF(value);
699         } catch (IOException exception) {
700             raise(exception);
701         }
702     }
703 
704     /***
705      * Write a byte array to the bytes message stream
706      *
707      * @param value the byte array to be written.
708      * @throws JMSException if JMS fails to write message due to some internal
709      * JMS error
710      * @throws MessageNotWriteableException if message is in read-only mode
711      */
712     public final void writeBytes(byte[] value) throws JMSException {
713         checkWrite();
714         try {
715             getOutputStream().write(value);
716         } catch (IOException exception) {
717             raise(exception);
718         }
719     }
720 
721     /***
722      * Write a portion of a byte array to the bytes message stream
723      *
724      * @param value the byte array value to be written.
725      * @param offset the initial offset within the byte array.
726      * @param length the number of bytes to use.
727      * @throws JMSException if JMS fails to write message due to some internal
728      * JMS error
729      * @throws MessageNotWriteableException if message is in read-only mode
730      */
731     public final void writeBytes(byte[] value, int offset, int length)
732         throws JMSException {
733         checkWrite();
734         try {
735             getOutputStream().write(value, offset, length);
736         } catch (IOException exception) {
737             raise(exception);
738         }
739     }
740 
741     /***
742      * Write a Java object to the bytes message stream.
743      *
744      * <p>Note that this method only works for the objectified primitive
745      * object types (Integer, Double, Long ...), String's and byte arrays.
746      *
747      * @param value the Java object to be written. Must not be null.
748      *
749      * @throws JMSException if JMS fails to write message due to some internal
750      * JMS error
751      * @throws MessageFormatException if object is invalid type
752      * @throws MessageNotWriteableException if message in read-only mode
753      * @throws NullPointerException if parameter <code>value</code> is null
754      */
755     public final void writeObject(Object value) throws JMSException {
756         if (value instanceof Boolean) {
757             writeBoolean(((Boolean) value).booleanValue());
758         } else if (value instanceof Byte) {
759             writeByte(((Byte) value).byteValue());
760         } else if (value instanceof Short) {
761             writeShort(((Short) value).shortValue());
762         } else if (value instanceof Character) {
763             writeChar(((Character) value).charValue());
764         } else if (value instanceof Integer) {
765             writeInt(((Integer) value).intValue());
766         } else if (value instanceof Long) {
767             writeLong(((Long) value).longValue());
768         } else if (value instanceof Float) {
769             writeFloat(((Float) value).floatValue());
770         } else if (value instanceof Double) {
771             writeDouble(((Double) value).doubleValue());
772         } else if (value instanceof String) {
773             writeUTF((String) value);
774         } else if (value instanceof byte[]) {
775             writeBytes((byte[]) value);
776         } else if (value == null) {
777             throw new NullPointerException(
778                 "BytesMessage does not support null");
779         } else {
780             throw new MessageFormatException("Cannot write objects of type=" +
781                 value.getClass().getName());
782         }
783     }
784 
785     /***
786      * Put the message body in read-only mode, and reposition the stream of
787      * bytes to the beginning
788      *
789      * @throws JMSException if JMS fails to reset the message due to some
790      * internal JMS error
791      */
792     public final void reset() throws JMSException {
793         try {
794             if (!_bodyReadOnly) {
795                 _bodyReadOnly = true;
796                 if (_out != null) {
797                     _out.flush();
798                     _bytes = _byteOut.toByteArray();
799                     _byteOut = null;
800                     _out.close();
801                     _out = null;
802                 }
803             } else {
804                 if (_in != null) {
805                     _byteIn = null;
806                     _in.close();
807                     _in = null;
808                 }
809             }
810         } catch (IOException exception) {
811             raise(exception);
812         }
813     }
814 
815     /***
816      * Overide the super class method to reset the streams, and put the
817      * message body in write only mode.
818      *
819      * <p>If <code>clearBody</code> is called on a message in read-only mode,
820      * the message body is cleared and the message is in write-only mode.
821      * bytes to the beginning.
822      *
823      *	<p>If <code>clearBody</code> is called on a message already in
824      * 	write-only mode, the spec does not define the outcome, so do nothing.
825      * 	Client must then call <code>reset</code>, followed by
826      *	<code>clearBody</code> to reset the stream at the beginning for a
827      *	new write.
828      * @throws JMSException if JMS fails to reset the message due to some
829      * internal JMS error
830      */
831     public final void clearBody() throws JMSException {
832         try {
833             if (_bodyReadOnly) {
834                 // in read-only mode
835                 _bodyReadOnly = false;
836                 if (_in != null) {
837                     _byteIn = null;
838                     _in.close();
839                     _in = null;
840                     _offset = 0;
841                 }
842             } else if (_out != null) {
843                 // already in write-only mode
844                 _byteOut = null;
845                 _out.close();
846                 _out = null;
847             }
848             _bytes = EMPTY;
849             _byteOut = null;
850             _out = null;
851         } catch (IOException exception) {
852             raise(exception);
853         }
854     }
855 
856     /***
857      * Set the read-only mode of the message.
858      *
859      * @param readOnly if true, make the message body and properties read-only,
860      * and invoke {@link #reset}
861      * @throws JMSException if the read-only mode cannot be changed
862      */
863     public final void setReadOnly(boolean readOnly) throws JMSException {
864         if (readOnly) {
865             reset();
866         }
867         super.setReadOnly(readOnly);
868     }
869 
870     /***
871      * Prepare to do a read
872      *
873      * @throws JMSException if the current position in the stream can't be
874      * marked
875      * @throws MessageNotReadableException if the message is in write-only mode
876      */
877     private final void prepare() throws JMSException {
878         checkRead();
879         getInputStream();
880         try {
881             _in.mark(_bytes.length - _in.available());
882         } catch (IOException exception) {
883             raise(exception);
884         }
885     }
886 
887     /***
888      * Reverts the stream to its prior position if an I/O exception is
889      * thrown, and propagates the exception as a JMSException
890      *
891      * @param exception the exception that caused the reset
892      * @throws JMSException for general IOException errors
893      * @throws MessageEOFException if end-of-file was reached
894      */
895     private final void revert(IOException exception) throws JMSException {
896         try {
897             _in.reset();
898         } catch (IOException ignore) {
899         }
900         JMSException error = null;
901         if (exception instanceof EOFException) {
902             error = new MessageEOFException(exception.getMessage());
903         } else if (exception instanceof UTFDataFormatException) {
904             error = new MessageFormatException(exception.getMessage());
905         } else {
906             error = new JMSException(exception.getMessage());
907         }
908         error.setLinkedException(exception);
909         throw error;
910     }
911 
912     /***
913      * Initialise the input stream if it hasn't been intialised
914      *
915      * @return the input stream
916      */
917     private DataInputStream getInputStream() {
918         if (_in == null) {
919             _byteIn = new ByteArrayInputStream(_bytes, _offset,
920                 _bytes.length - _offset);
921             _in = new DataInputStream(_byteIn);
922         }
923         return _in;
924     }
925 
926     /***
927      * Initialise the output stream if it hasn't been intialised
928      *
929      * @return the output stream
930      * @throws IOException if the output stream can't be created
931      */
932     private final DataOutputStream getOutputStream() throws IOException {
933         if (_out == null) {
934             _byteOut = new ByteArrayOutputStream();
935             _out = new DataOutputStream(_byteOut);
936             _out.write(_bytes);
937         }
938         return _out;
939     }
940 
941     /***
942      * Helper to raise a JMSException when an I/O error occurs
943      *
944      * @param exception the exception that caused the failure
945      * @throws JMSException
946      */
947     private final void raise(IOException exception) throws JMSException {
948         JMSException error = new JMSException(exception.getMessage());
949         error.setLinkedException(exception);
950         throw error;
951     }
952 
953 } //-- BytesMessageImpl