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