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: StreamMessageImpl.java,v 1.13 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
60 import javax.jms.JMSException;
61 import javax.jms.MessageEOFException;
62 import javax.jms.MessageFormatException;
63 import javax.jms.MessageNotReadableException;
64 import javax.jms.MessageNotWriteableException;
65 import javax.jms.StreamMessage;
66
67
68 /***
69 * This class implements the {@link javax.jms.StreamMessage} interface.
70 * <p>
71 * A StreamMessage is used to send a stream of Java primitives.
72 * It is filled and read sequentially. It inherits from <code>Message</code>
73 * and adds a stream message body. It's methods are based largely on those
74 * found in <code>java.io.DataInputStream</code> and
75 * <code>java.io.DataOutputStream</code>.
76 * <p>
77 * The primitive types can be read or written explicitly using methods
78 * for each type. They may also be read or written generically as objects.
79 * For instance, a call to <code>StreamMessage.writeInt(6)</code> is
80 * equivalent to <code>StreamMessage.writeObject(new Integer(6))</code>.
81 * Both forms are provided because the explicit form is convenient for
82 * static programming and the object form is needed when types are not known
83 * at compile time.
84 * <p>
85 * When the message is first created, and when {@link #clearBody}
86 * is called, the body of the message is in write-only mode. After the
87 * first call to {@link #reset} has been made, the message body is in
88 * read-only mode. When a message has been sent, by definition, the
89 * provider calls <code>reset</code> in order to read it's content, and
90 * when a message has been received, the provider has called
91 * <code>reset</code> so that the message body is in read-only mode for the
92 * client.
93 * <p>
94 * If {@link #clearBody} is called on a message in read-only mode,
95 * the message body is cleared and the message body is in write-only mode.
96 * <p>
97 * If a client attempts to read a message in write-only mode, a
98 * MessageNotReadableException is thrown.
99 * <p>
100 * If a client attempts to write a message in read-only mode, a
101 * MessageNotWriteableException is thrown.
102 * <p>
103 * Stream messages support the following conversion table. The marked cases
104 * must be supported. The unmarked cases must throw a JMSException. The
105 * String to primitive conversions may throw a runtime exception if the
106 * primitives <code>valueOf()</code> method does not accept it as a valid
107 * String representation of the primitive.
108 * <p>
109 * A value written as the row type can be read as the column type.
110 *
111 * <pre>
112 * | | boolean byte short char int long float double String byte[]
113 * |----------------------------------------------------------------------
114 * |boolean | X X
115 * |byte | X X X X X
116 * |short | X X X X
117 * |char | X X
118 * |int | X X X
119 * |long | X X
120 * |float | X X X
121 * |double | X X
122 * |String | X X X X X X X X
123 * |byte[] | X
124 * |----------------------------------------------------------------------
125 * </pre>
126 * <p>
127 * Attempting to read a null value as a Java primitive type must be treated
128 * as calling the primitive's corresponding <code>valueOf(String)</code>
129 * conversion method with a null value. Since char does not support a String
130 * conversion, attempting to read a null value as a char must throw
131 * NullPointerException.
132 *
133 * @version $Revision: 1.13 $ $Date: 2004/01/29 12:00:37 $
134 * @author <a href="mailto:mourikis@intalio.com">Jim Mourikis</a>
135 * @author <a href="mailto:tima@intalio.com">Tim Anderson</a>
136 * @see javax.jms.StreamMessage
137 */
138 public final class StreamMessageImpl extends MessageImpl
139 implements StreamMessage {
140
141 /***
142 * Object version no. for serialization
143 */
144 static final long serialVersionUID = 2;
145
146 /***
147 * Type codes
148 */
149 private static final byte NULL = 0;
150 private static final byte BOOLEAN = 1;
151 private static final byte BYTE = 2;
152 private static final byte BYTE_ARRAY = 3;
153 private static final byte SHORT = 4;
154 private static final byte CHAR = 5;
155 private static final byte INT = 6;
156 private static final byte LONG = 7;
157 private static final byte FLOAT = 8;
158 private static final byte DOUBLE = 9;
159 private static final byte STRING = 10;
160
161 /***
162 * String values representing the above type codes, for error reporting
163 * purposes
164 */
165 private static final String[] TYPE_NAMES = {
166 "null", "boolean", "byte", "byte[]", "short", "char", "int", "long",
167 "float", "double", "String"};
168
169 /***
170 * Empty byte array for initialisation purposes
171 */
172 private static final byte[] EMPTY = new byte[]{};
173
174 /***
175 * The byte stream to store data
176 */
177 private byte[] _bytes = EMPTY;
178
179 /***
180 * The stream used for writes
181 */
182 private DataOutputStream _out = null;
183
184 /***
185 * The byte stream backing the output stream
186 */
187 private ByteArrayOutputStream _byteOut = null;
188
189 /***
190 * The stream used for reads
191 */
192 private DataInputStream _in = null;
193
194 /***
195 * The byte stream backing the input stream
196 */
197 private ByteArrayInputStream _byteIn = null;
198
199 /***
200 * Non-zero if incrementally reading a byte array using
201 * {@link #readBytes(byte[])}
202 */
203 int _readBytes = 0;
204
205 /***
206 * The length of the byte array being read using {@link #readBytes(byte[])}
207 */
208 int _byteArrayLength = 0;
209
210 /***
211 * The offset of the byte stream to start reading from. This defaults
212 * to 0, and only applies to messages that are cloned from a
213 * read-only instance where part of the stream had already been read.
214 */
215 private int _offset = 0;
216
217 /***
218 * Construct a new StreamMessage. When first created, the message is in
219 * write-only mode.
220 *
221 * @throws JMSException if the message type can't be set, or an I/O error
222 * occurs
223 */
224 public StreamMessageImpl() throws JMSException {
225 setJMSType("StreamMessage");
226 }
227
228 /***
229 * Clone an instance of this object
230 *
231 * @return a copy of this object
232 * @throws CloneNotSupportedException if object or attributes aren't
233 * cloneable
234 */
235 public final Object clone() throws CloneNotSupportedException {
236 StreamMessageImpl result = (StreamMessageImpl) super.clone();
237 if (_bodyReadOnly) {
238 result._bytes = new byte[_bytes.length];
239 System.arraycopy(_bytes, 0, result._bytes, 0, _bytes.length);
240 if (_byteIn != null) {
241 // if a client subsequently reads from the cloned object,
242 // start reading from offset of the original stream
243 _offset = _bytes.length - _byteIn.available();
244 }
245 result._byteIn = null;
246 result._in = null;
247 } else {
248 if (_out != null) {
249 try {
250 _out.flush();
251 } catch (IOException exception) {
252 throw new CloneNotSupportedException(
253 exception.getMessage());
254 }
255 result._bytes = _byteOut.toByteArray();
256 result._byteOut = null;
257 result._out = null;
258 } else {
259 result._bytes = new byte[_bytes.length];
260 System.arraycopy(_bytes, 0, result._bytes, 0, _bytes.length);
261 }
262 }
263
264 return result;
265 }
266
267 /***
268 * Serialize out this message's data
269 *
270 * @param out the stream to serialize out to
271 * @throws IOException if any I/O exceptions occurr
272 */
273 public final void writeExternal(ObjectOutput out) throws IOException {
274 // If it was in write mode, extract the byte array
275 if (!_bodyReadOnly && _out != null) {
276 _out.flush();
277 _bytes = _byteOut.toByteArray();
278 }
279
280 super.writeExternal(out);
281 out.writeLong(serialVersionUID);
282 out.writeInt(_bytes.length);
283 out.write(_bytes);
284 out.flush();
285 }
286
287 /***
288 * Serialize in this message's data
289 *
290 * @param in the stream to serialize in from
291 * @throws ClassNotFoundException if the class for an object being
292 * restored cannot be found.
293 * @throws IOException if any I/O exceptions occur
294 */
295 public final void readExternal(ObjectInput in)
296 throws ClassNotFoundException, IOException {
297 super.readExternal(in);
298 long version = in.readLong();
299 if (version == serialVersionUID) {
300 int length = in.readInt();
301 _bytes = new byte[length];
302 in.readFully(_bytes);
303 } else {
304 throw new IOException("Incorrect version enountered: " + version +
305 ". This version = " + serialVersionUID);
306 }
307 }
308
309 /***
310 * Read a <code>boolean</code> from the bytes message stream
311 *
312 * @return the <code>boolean</code> value read
313 * @throws JMSException if JMS fails to read message due to some internal
314 * JMS error
315 * @throws MessageEOFException if end of message stream
316 * @throws MessageFormatException if this type conversion is invalid
317 * @throws MessageNotReadableException if message is in write-only mode
318 */
319 public final boolean readBoolean() throws JMSException {
320 boolean result = false;
321 prepare();
322 try {
323 result = FormatConverter.getBoolean(readNext());
324 } catch (MessageFormatException exception) {
325 revert(exception);
326 }
327 return result;
328 }
329
330 /***
331 * Read a byte value from the stream message
332 *
333 * @return the next byte from the stream message as an 8-bit
334 * <code>byte</code>
335 * @throws JMSException if JMS fails to read message due to some internal
336 * JMS error
337 * @throws MessageEOFException if end of message stream
338 * @throws MessageFormatException if this type conversion is invalid
339 * @throws MessageNotReadableException if message is in write-only mode
340 * @throws NumberFormatException if numeric conversion is invalid
341 */
342 public final byte readByte() throws JMSException {
343 byte result = 0;
344 prepare();
345 try {
346 result = FormatConverter.getByte(readNext());
347 } catch (MessageFormatException exception) {
348 revert(exception);
349 } catch (NumberFormatException exception) {
350 revert(exception);
351 }
352 return result;
353 }
354
355 /***
356 * Read a 16-bit number from the stream message.
357 *
358 * @return a 16-bit number from the stream message
359 * @throws JMSException if JMS fails to read message due to some internal
360 * JMS error
361 * @throws MessageEOFException if end of message stream
362 * @throws MessageFormatException if this type conversion is invalid
363 * @throws MessageNotReadableException if message is in write-only mode
364 * @throws NumberFormatException if numeric conversion is invalid
365 */
366 public final short readShort() throws JMSException {
367 short result = 0;
368 prepare();
369 try {
370 result = FormatConverter.getShort(readNext());
371 } catch (MessageFormatException exception) {
372 revert(exception);
373 } catch (NumberFormatException exception) {
374 revert(exception);
375 }
376 return result;
377 }
378
379 /*
380 * Read a Unicode character value from the stream message
381 *
382 * @return a Unicode character from the stream message
383 * @throws JMSException if JMS fails to read message due to some internal
384 * JMS error
385 * @throws MessageEOFException if end of message stream
386 * @throws MessageFormatException if this type conversion is invalid
387 * @throws MessageNotReadableException if message is in write-only mode
388 */
389 public final char readChar() throws JMSException {
390 char result = 0;
391 prepare();
392 try {
393 result = FormatConverter.getChar(readNext());
394 } catch (MessageFormatException exception) {
395 revert(exception);
396 } catch (NullPointerException exception) {
397 revert(exception);
398 }
399 return result;
400 }
401
402 /*
403 * Read a 32-bit integer from the stream message
404 *
405 * @return a 32-bit integer value from the stream message, interpreted
406 * as an <code>int</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 MessageFormatException if this type conversion is invalid
411 * @throws MessageNotReadableException if message is in write-only mode
412 * @throws NumberFormatException if numeric conversion is invalid
413 */
414 public final int readInt() throws JMSException {
415 int result = 0;
416 prepare();
417 try {
418 result = FormatConverter.getInt(readNext());
419 } catch (MessageFormatException exception) {
420 revert(exception);
421 } catch (NumberFormatException exception) {
422 revert(exception);
423 }
424 return result;
425 }
426
427 /*
428 * Read a 64-bit integer from the stream message
429 *
430 * @return a 64-bit integer value from the stream message, interpreted as
431 * a <code>long</code>
432 * @throws JMSException if JMS fails to read message due to some internal
433 * JMS error
434 * @throws MessageEOFException if end of message stream
435 * @throws MessageFormatException if this type conversion is invalid
436 * @throws MessageNotReadableException if message is in write-only mode
437 * @throws NumberFormatException if numeric conversion is invalid
438 */
439 public final long readLong() throws JMSException {
440 long result = 0;
441 prepare();
442 try {
443 result = FormatConverter.getLong(readNext());
444 } catch (MessageFormatException exception) {
445 revert(exception);
446 } catch (NumberFormatException exception) {
447 revert(exception);
448 }
449 return result;
450 }
451
452 /***
453 * Read a <code>float</code> from the stream message
454 *
455 * @return a <code>float</code> value from the stream message
456 * @throws JMSException if JMS fails to read message due to some internal
457 * JMS error
458 * @throws MessageEOFException if end of message stream
459 * @throws MessageFormatException if this type conversion is invalid
460 * @throws MessageNotReadableException if message is in write-only mode
461 * @throws NullPointerException if the value is null
462 * @throws NumberFormatException if numeric conversion is invalid
463 */
464 public final float readFloat() throws JMSException {
465 float result = 0;
466 prepare();
467 try {
468 result = FormatConverter.getFloat(readNext());
469 } catch (MessageFormatException exception) {
470 revert(exception);
471 } catch (NullPointerException exception) {
472 revert(exception);
473 } catch (NumberFormatException exception) {
474 revert(exception);
475 }
476 return result;
477 }
478
479 /***
480 * Read a <code>double</code> from the stream message
481 *
482 * @return a <code>double</code> value from the stream message
483 * @throws JMSException if JMS fails to read message due to some internal
484 * JMS error
485 * @throws MessageEOFException if end of message stream
486 * @throws MessageFormatException if this type conversion is invalid
487 * @throws MessageNotReadableException if message is in write-only mode
488 * @throws NullPointerException if the value is null
489 * @throws NumberFormatException if numeric conversion is invalid
490 */
491 public final double readDouble() throws JMSException {
492 double result = 0;
493 prepare();
494 try {
495 result = FormatConverter.getDouble(readNext());
496 } catch (MessageFormatException exception) {
497 revert(exception);
498 } catch (NullPointerException exception) {
499 revert(exception);
500 } catch (NumberFormatException exception) {
501 revert(exception);
502 }
503 return result;
504 }
505
506 /***
507 * Read in a string from the stream message
508 *
509 * @return a Unicode string from the stream message
510 * @throws JMSException if JMS fails to read message due to some internal
511 * JMS error
512 * @throws MessageEOFException if end of message stream
513 * @throws MessageFormatException if this type conversion is invalid
514 * @throws MessageNotReadableException if message is in write-only mode
515 */
516 public final String readString() throws JMSException {
517 String result = null;
518 prepare();
519 try {
520 result = FormatConverter.getString(readNext());
521 } catch (MessageFormatException exception) {
522 revert(exception);
523 }
524 return result;
525 }
526
527 /***
528 * Read a byte array field from the stream message into the
529 * specified byte[] object (the read buffer).
530 * <p>
531 * To read the field value, readBytes should be successively called
532 * until it returns a value less than the length of the read buffer.
533 * The value of the bytes in the buffer following the last byte
534 * read are undefined.
535 * <p>
536 * If readBytes returns a value equal to the length of the buffer, a
537 * subsequent readBytes call must be made. If there are no more bytes
538 * to be read this call will return -1.
539 * <p>
540 * If the bytes array field value is null, readBytes returns -1.
541 * <p>
542 * If the bytes array field value is empty, readBytes returns 0.
543 * <p>
544 * Once the first readBytes call on a byte[] field value has been done,
545 * the full value of the field must be read before it is valid to read
546 * the next field. An attempt to read the next field before that has
547 * been done will throw a MessageFormatException.
548 * <p>
549 * To read the byte field value into a new byte[] object, use the
550 * {@link #readObject} method.
551 *
552 * @param value the buffer into which the data is read.
553 * @return the total number of bytes read into the buffer, or -1 if
554 * there is no more data because the end of the byte field has been
555 * reached.
556 * @throws JMSException if JMS fails to read message due to some internal
557 * JMS error
558 * @throws MessageEOFException if an end of message stream
559 * @throws MessageFormatException if this type conversion is invalid
560 * @throws MessageNotReadableException if message is in write-only mode
561 */
562 public final int readBytes(byte[] value) throws JMSException {
563 checkRead();
564 getInputStream();
565 int read = 0; // the number of bytes read
566 if (_readBytes == 0) {
567 try {
568 _in.mark(_bytes.length - _in.available());
569 byte type = (byte) (_in.readByte() & 0x0F);
570 if (type == NULL) {
571 return -1;
572 } else if (type != BYTE_ARRAY) {
573 _in.reset();
574 if (type < TYPE_NAMES.length) {
575 throw new MessageFormatException(
576 "Expected type=" + TYPE_NAMES[BYTE_ARRAY] +
577 ", but got type=" + TYPE_NAMES[type]);
578 } else {
579 throw new MessageFormatException(
580 "StreamMessage corrupted");
581 }
582 }
583 } catch (IOException exception) {
584 raise(exception);
585 }
586 try {
587 _byteArrayLength = _in.readInt();
588 } catch (IOException exception) {
589 raise(exception);
590 }
591 }
592 if (_byteArrayLength == 0) {
593 if (_readBytes != 0) { // not the first invocation
594 read = -1;
595 }
596 _readBytes = 0; // finished reading the byte array
597 } else {
598 ++_readBytes;
599 try {
600 if (value.length <= _byteArrayLength) {
601 read = value.length;
602 _in.readFully(value);
603 _byteArrayLength -= value.length;
604 } else {
605 read = _byteArrayLength;
606 _in.readFully(value, 0, _byteArrayLength);
607 _byteArrayLength = 0;
608 }
609 } catch (IOException exception) {
610 raise(exception);
611 }
612 }
613 return read;
614 }
615
616 /***
617 * Read a Java object from the stream message
618 * <p>
619 * Note that this method can be used to return in objectified format,
620 * an object that had been written to the stream with the equivalent
621 * <code>writeObject</code> method call, or it's equivalent primitive
622 * write<type> method.
623 * <p>
624 * Note that byte values are returned as byte[], not Byte[].
625 *
626 * @return a Java object from the stream message, in objectified
627 * format (eg. if it set as an int, then a Integer is returned).
628 * @throws JMSException if JMS fails to read message due to some internal
629 * JMS error
630 * @throws MessageEOFException if end of message stream
631 * @throws MessageNotReadableException if message is in write-only mode
632 */
633 public final Object readObject() throws JMSException {
634 Object result = null;
635 prepare();
636 try {
637 result = readNext();
638 } catch (MessageFormatException exception) {
639 revert(exception);
640 }
641 return result;
642 }
643
644 /***
645 * Write a <code>boolean</code> to the stream message.
646 * The value <code>true</code> is written out as the value
647 * <code>(byte)1</code>; the value <code>false</code> is written out as
648 * the value <code>(byte)0</code>.
649 *
650 * @param value the <code>boolean</code> value to be written.
651 * @throws JMSException if JMS fails to write message due to
652 * some internal JMS error
653 * @throws MessageNotWriteableException if message in read-only mode
654 */
655 public final void writeBoolean(boolean value) throws JMSException {
656 checkWrite();
657 try {
658 getOutputStream();
659 // encode the boolean value in the type byte
660 _out.writeByte(BOOLEAN | ((value) ? 1 << 4 : 0));
661 } catch (IOException exception) {
662 raise(exception);
663 }
664 }
665
666 /***
667 * Write out a <code>byte</code> to the stream message
668 *
669 * @param value the <code>byte</code> value to be written
670 * @throws JMSException if JMS fails to write message due to
671 * some internal JMS error
672 * @throws MessageNotWriteableException if message in read-only mode
673 */
674 public final void writeByte(byte value) throws JMSException {
675 checkWrite();
676 try {
677 getOutputStream();
678 _out.writeByte(BYTE);
679 _out.writeByte(value);
680 } catch (IOException exception) {
681 raise(exception);
682 }
683 }
684
685 /***
686 * Write a <code>short</code> to the stream message
687 *
688 * @param value the <code>short</code> to be written
689 * @throws JMSException if JMS fails to write message due to
690 * some internal JMS error
691 * @throws MessageNotWriteableException if message in read-only mode
692 */
693 public final void writeShort(short value) throws JMSException {
694 checkWrite();
695 try {
696 getOutputStream();
697 _out.writeByte(SHORT);
698 _out.writeShort(value);
699 } catch (IOException exception) {
700 raise(exception);
701 }
702 }
703
704 /***
705 * Write a <code>char</code> to the stream message
706 *
707 * @param value the <code>char</code> value to be written
708 * @throws JMSException if JMS fails to write message due to
709 * some internal JMS error
710 * @throws MessageNotWriteableException if message in read-only mode
711 */
712 public final void writeChar(char value) throws JMSException {
713 checkWrite();
714 try {
715 getOutputStream();
716 _out.writeByte(CHAR);
717 _out.writeChar(value);
718 } catch (IOException exception) {
719 raise(exception);
720 }
721 }
722
723 /***
724 * Write an <code>int</code> to the stream message
725 *
726 * @param value the <code>int</code> to be written
727 * @throws JMSException if JMS fails to write message due to
728 * some internal JMS error
729 * @throws MessageNotWriteableException if message in read-only mode
730 */
731 public final void writeInt(int value) throws JMSException {
732 checkWrite();
733 try {
734 getOutputStream();
735 _out.writeByte(INT);
736 _out.writeInt(value);
737 } catch (IOException exception) {
738 raise(exception);
739 }
740 }
741
742 /***
743 * Write a <code>long</code> to the stream message
744 *
745 * @param value the <code>long</code> to be written
746 * @throws JMSException if JMS fails to write message due to
747 * some internal JMS error
748 * @throws MessageNotWriteableException if message in read-only mode
749 */
750 public final void writeLong(long value) throws JMSException {
751 checkWrite();
752 try {
753 getOutputStream();
754 _out.writeByte(LONG);
755 _out.writeLong(value);
756 } catch (IOException exception) {
757 raise(exception);
758 }
759 }
760
761 /***
762 * Write a <code>float</code> to the stream message
763 *
764 * @param value the <code>float</code> value to be written
765 * @throws JMSException if JMS fails to write message due to
766 * some internal JMS error
767 * @throws MessageNotWriteableException if message in read-only mode
768 */
769 public final void writeFloat(float value) throws JMSException {
770 checkWrite();
771 try {
772 getOutputStream();
773 _out.writeByte(FLOAT);
774 _out.writeFloat(value);
775 } catch (IOException exception) {
776 raise(exception);
777 }
778 }
779
780 /***
781 * Write a <code>double</code> to the stream message
782 *
783 * @param value the <code>double</code> value to be written
784 * @throws JMSException if JMS fails to write message due to
785 * some internal JMS error
786 * @throws MessageNotWriteableException if message in read-only mode
787 */
788 public final void writeDouble(double value) throws JMSException {
789 checkWrite();
790 try {
791 getOutputStream();
792 _out.writeByte(DOUBLE);
793 _out.writeDouble(value);
794 } catch (IOException exception) {
795 raise(exception);
796 }
797 }
798
799 /***
800 * Write a string to the stream message
801 *
802 * @param value the <code>String</code> value to be written
803 * @throws JMSException if JMS fails to write message due to
804 * some internal JMS error
805 * @throws MessageNotWriteableException if message in read-only mode
806 * @throws NullPointerException if value is null
807 */
808 public final void writeString(String value) throws JMSException {
809 checkWrite();
810 if (value == null) {
811 // could throw IllegalArgumentException, but this is in keeping
812 // with that thrown by DataOutputStream
813 throw new NullPointerException("Argument value is null");
814 }
815 try {
816 getOutputStream();
817 _out.writeByte(STRING);
818 _out.writeUTF(value);
819 } catch (IOException exception) {
820 raise(exception);
821 }
822 }
823
824 /***
825 * Write a byte array field to the stream message
826 * <p>
827 * The byte array <code>value</code> is written as a byte array field
828 * into the StreamMessage. Consecutively written byte array fields are
829 * treated as two distinct fields when reading byte array fields.
830 *
831 * @param value the byte array to be written
832 * @throws JMSException if JMS fails to write message due to
833 * some internal JMS error
834 * @throws MessageNotWriteableException if message in read-only mode
835 * @throws NullPointerException if value is null
836 */
837 public final void writeBytes(byte[] value) throws JMSException {
838 checkWrite();
839 if (value == null) {
840 // could throw IllegalArgumentException, but this is in keeping
841 // with that thrown by DataOutputStream
842 throw new NullPointerException("Argument value is null");
843 }
844 try {
845 getOutputStream();
846 _out.writeByte(BYTE_ARRAY);
847 _out.writeInt(value.length);
848 _out.write(value);
849 } catch (IOException exception) {
850 raise(exception);
851 }
852 }
853
854 /***
855 * Write a portion of a byte array as a byte array field to the stream
856 * message
857 * <p>
858 * The a portion of the byte array <code>value</code> is written as a
859 * byte array field into the StreamMessage. Consecutively written byte
860 * array fields are treated as two distinct fields when reading byte
861 * array fields.
862 *
863 * @param value the byte array value to be written
864 * @param offset the initial offset within the byte array
865 * @param length the number of bytes to write
866 * @throws JMSException if JMS fails to write message due to
867 * some internal JMS error
868 * @throws MessageNotWriteableException if message in read-only mode
869 * @throws NullPointerException if value is null
870 */
871 public void writeBytes(byte[] value, int offset, int length)
872 throws JMSException {
873 checkWrite();
874 if (value == null) {
875 // could throw IllegalArgumentException, but this is in keeping
876 // with that thrown by DataOutputStream
877 throw new NullPointerException("Argument value is null");
878 }
879 try {
880 getOutputStream();
881 _out.writeByte(BYTE_ARRAY);
882 _out.writeInt(length);
883 _out.write(value, offset, length);
884 } catch (IOException exception) {
885 raise(exception);
886 }
887 }
888
889 /***
890 * Write a Java object to the stream message
891 * <p>
892 * Note that this method only works for the objectified primitive
893 * object types (Integer, Double, Long ...), String's and byte arrays.
894 *
895 * @param value the Java object to be written
896 * @throws JMSException if JMS fails to write message due to
897 * some internal JMS error
898 * @throws MessageFormatException if the object is invalid
899 * @throws MessageNotWriteableException if message in read-only mode
900 */
901 public void writeObject(Object value) throws JMSException {
902 if (value == null) {
903 try {
904 checkWrite();
905 getOutputStream();
906 _out.writeByte(NULL);
907 } catch (IOException exception) {
908 raise(exception);
909 }
910 } else if (value instanceof Boolean) {
911 writeBoolean(((Boolean) value).booleanValue());
912 } else if (value instanceof Byte) {
913 writeByte(((Byte) value).byteValue());
914 } else if (value instanceof byte[]) {
915 writeBytes((byte[]) value);
916 } else if (value instanceof Short) {
917 writeShort(((Short) value).shortValue());
918 } else if (value instanceof Character) {
919 writeChar(((Character) value).charValue());
920 } else if (value instanceof Integer) {
921 writeInt(((Integer) value).intValue());
922 } else if (value instanceof Long) {
923 writeLong(((Long) value).longValue());
924 } else if (value instanceof Float) {
925 writeFloat(((Float) value).floatValue());
926 } else if (value instanceof Double) {
927 writeDouble(((Double) value).doubleValue());
928 } else if (value instanceof String) {
929 writeString((String) value);
930 } else {
931 throw new MessageFormatException(
932 "Objects of type " + value.getClass().getName() +
933 " are not supported by StreamMessage");
934 }
935 }
936
937 /***
938 * Put the message body in read-only mode, and reposition the stream
939 * to the beginning
940 *
941 * @throws JMSException if JMS fails to reset the message due to
942 * some internal JMS error
943 */
944 public void reset() throws JMSException {
945 try {
946 if (!_bodyReadOnly) {
947 _bodyReadOnly = true;
948 if (_out != null) {
949 _out.flush();
950 _bytes = _byteOut.toByteArray();
951 _byteOut = null;
952 _out.close();
953 _out = null;
954 }
955 } else {
956 if (_in != null) {
957 _byteIn = null;
958 _in.close();
959 _in = null;
960 }
961 }
962 _readBytes = 0;
963 } catch (IOException exception) {
964 raise(exception);
965 }
966 }
967
968 /***
969 * Overide the super class method to reset the streams, and put the
970 * message body in write only mode
971 *
972 * @throws JMSException if JMS fails to reset the message due to
973 * some internal JMS error.
974 */
975 public void clearBody() throws JMSException {
976 try {
977 if (_bodyReadOnly) {
978 // in read-only mode
979 _bodyReadOnly = false;
980 if (_in != null) {
981 _byteIn = null;
982 _in.close();
983 _in = null;
984 _offset = 0;
985 }
986 } else if (_out != null) {
987 // already in write-only mode
988 _byteOut = null;
989 _out.close();
990 _out = null;
991 }
992 _bytes = EMPTY;
993 _readBytes = 0;
994 } catch (IOException exception) {
995 raise(exception);
996 }
997 }
998
999 /***
1000 * Set the read-only mode of the message. If read-only, resets the message
1001 * for reading
1002 *
1003 * @param readOnly if true, make the message body and properties
1004 * @throws JMSException if the read-only mode cannot be changed
1005 */
1006 public final void setReadOnly(boolean readOnly) throws JMSException {
1007 if (readOnly) {
1008 reset();
1009 }
1010 super.setReadOnly(readOnly);
1011 }
1012
1013 /***
1014 * Prepare to do a read
1015 *
1016 * @throws JMSException if the current position in the stream can't be
1017 * marked
1018 * @throws MessageNotReadableException if the message is in write-only mode
1019 */
1020 private final void prepare() throws JMSException {
1021 checkRead();
1022 getInputStream();
1023 try {
1024 _in.mark(_bytes.length - _in.available());
1025 } catch (IOException exception) {
1026 raise(exception);
1027 }
1028 }
1029
1030 /***
1031 * Reverts the stream to its prior position if a MessageFormatException is
1032 * thrown, and propagates the exception.
1033 *
1034 * @param exception the exception that caused the reset
1035 * @throws MessageFormatException
1036 */
1037 private void revert(MessageFormatException exception)
1038 throws MessageFormatException {
1039 try {
1040 _in.reset();
1041 } catch (IOException ignore) {
1042 // can't reset the stream, but need to propagate the original
1043 // exception
1044 }
1045 throw exception;
1046 }
1047
1048 /***
1049 * Reverts the stream to its prior position if a NumberFormatException or
1050 * NullPointerException is thrown, and propagates the exception.
1051 *
1052 * @param exception the exception that caused the reset
1053 * @throws NullPointerException
1054 * @throws NumberFormatException
1055 */
1056 private void revert(RuntimeException exception) {
1057 try {
1058 _in.reset();
1059 } catch (IOException ignore) {
1060 // can't reset the stream, but need to propagate the original
1061 // exception
1062 }
1063 throw exception;
1064 }
1065
1066 /***
1067 * Read the next object from the stream message
1068 *
1069 * @return a Java object from the stream message, in objectified
1070 * format (eg. if it set as an int, then a Integer is returned).
1071 * @throws JMSException if JMS fails to read message due to some internal
1072 * JMS error
1073 * @throws MessageEOFException if end of message stream
1074 * @throws MessageFormatException if a byte array has not been fully read
1075 * by {@link #readBytes(byte[])}
1076 * @throws MessageNotReadableException if the message is in write-only mode
1077 */
1078 private Object readNext() throws JMSException {
1079 if (_readBytes != 0) {
1080 throw new MessageFormatException(
1081 "Cannot read the next field until the byte array is read");
1082 }
1083
1084 byte type = 0;
1085 try {
1086 type = _in.readByte();
1087 } catch (IOException exception) {
1088 raise(exception);
1089 }
1090 if ((type & 0x0F) > TYPE_NAMES.length) {
1091 throw new JMSException("StreamMessage corrupted");
1092 }
1093 Object result = null;
1094
1095 try {
1096 switch (type & 0x0F) {
1097 case BOOLEAN:
1098 boolean value = ((type & 0xF0) != 0) ? true : false;
1099 result = new Boolean(value);
1100 break;
1101 case BYTE:
1102 result = new Byte(_in.readByte());
1103 break;
1104 case BYTE_ARRAY:
1105 int length = _in.readInt();
1106 byte[] bytes = new byte[length];
1107 _in.readFully(bytes);
1108 result = bytes;
1109 break;
1110 case SHORT:
1111 result = new Short(_in.readShort());
1112 break;
1113 case CHAR:
1114 result = new Character(_in.readChar());
1115 break;
1116 case INT:
1117 result = new Integer(_in.readInt());
1118 break;
1119 case LONG:
1120 result = new Long(_in.readLong());
1121 break;
1122 case FLOAT:
1123 result = new Float(_in.readFloat());
1124 break;
1125 case DOUBLE:
1126 result = new Double(_in.readDouble());
1127 break;
1128 case STRING:
1129 result = _in.readUTF();
1130 break;
1131 }
1132 } catch (IOException exception) {
1133 raise(exception);
1134 }
1135
1136 return result;
1137 }
1138
1139 /***
1140 * Initialise the input stream if it hasn't been intialised
1141 *
1142 * @return the input stream
1143 */
1144 private DataInputStream getInputStream() {
1145 if (_in == null) {
1146 _byteIn = new ByteArrayInputStream(_bytes, _offset,
1147 _bytes.length - _offset);
1148 _in = new DataInputStream(_byteIn);
1149 }
1150 return _in;
1151 }
1152
1153 /***
1154 * Initialise the output stream if it hasn't been intialised
1155 *
1156 * @return the output stream
1157 * @throws IOException if the output stream can't be created
1158 */
1159 private final DataOutputStream getOutputStream() throws IOException {
1160 if (_out == null) {
1161 _byteOut = new ByteArrayOutputStream();
1162 _out = new DataOutputStream(_byteOut);
1163 _out.write(_bytes);
1164 }
1165 return _out;
1166 }
1167
1168 /***
1169 * Helper to raise a JMSException when an I/O error occurs
1170 *
1171 * @param exception the exception that caused the failure
1172 * @throws JMSException
1173 */
1174 private final void raise(IOException exception) throws JMSException {
1175 JMSException error = null;
1176 if (exception instanceof EOFException) {
1177 error = new MessageEOFException(exception.getMessage());
1178 } else {
1179 error = new JMSException(exception.getMessage());
1180 }
1181 error.setLinkedException(exception);
1182 throw error;
1183 }
1184
1185 } //-- StreamMessageImpl
1186
1187
This page was automatically generated by Maven