1 /***
2 *
3 * Copyright 2004 Protique Ltd
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 **/
18
19 package org.codehaus.activemq.message;
20
21 import javax.jms.JMSException;
22 import javax.jms.MessageEOFException;
23 import javax.jms.MessageFormatException;
24 import javax.jms.MessageNotReadableException;
25 import javax.jms.MessageNotWriteableException;
26 import javax.jms.StreamMessage;
27 import java.io.ByteArrayInputStream;
28 import java.io.ByteArrayOutputStream;
29 import java.io.DataInputStream;
30 import java.io.DataOutputStream;
31 import java.io.EOFException;
32 import java.io.IOException;
33
34 /***
35 * A <CODE>StreamMessage</CODE> object is used to send a stream of primitive
36 * types in the Java programming language. It is filled and read sequentially.
37 * It inherits from the <CODE>Message</CODE> interface
38 * and adds a stream message body. Its methods are based largely on those
39 * found in <CODE>java.io.DataInputStream</CODE> and
40 * <CODE>java.io.DataOutputStream</CODE>.
41 * <p/>
42 * <P>The primitive types can be read or written explicitly using methods
43 * for each type. They may also be read or written generically as objects.
44 * For instance, a call to <CODE>StreamMessage.writeInt(6)</CODE> is
45 * equivalent to <CODE>StreamMessage.writeObject(new Integer(6))</CODE>.
46 * Both forms are provided, because the explicit form is convenient for
47 * static programming, and the object form is needed when types are not known
48 * at compile time.
49 * <p/>
50 * <P>When the message is first created, and when <CODE>clearBody</CODE>
51 * is called, the body of the message is in write-only mode. After the
52 * first call to <CODE>reset</CODE> has been made, the message body is in
53 * read-only mode.
54 * After a message has been sent, the client that sent it can retain and
55 * modify it without affecting the message that has been sent. The same message
56 * object can be sent multiple times.
57 * When a message has been received, the provider has called
58 * <CODE>reset</CODE> so that the message body is in read-only mode for the client.
59 * <p/>
60 * <P>If <CODE>clearBody</CODE> is called on a message in read-only mode,
61 * the message body is cleared and the message body is in write-only mode.
62 * <p/>
63 * <P>If a client attempts to read a message in write-only mode, a
64 * <CODE>MessageNotReadableException</CODE> is thrown.
65 * <p/>
66 * <P>If a client attempts to write a message in read-only mode, a
67 * <CODE>MessageNotWriteableException</CODE> is thrown.
68 * <p/>
69 * <P><CODE>StreamMessage</CODE> objects support the following conversion
70 * table. The marked cases must be supported. The unmarked cases must throw a
71 * <CODE>JMSException</CODE>. The <CODE>String</CODE>-to-primitive conversions
72 * may throw a runtime exception if the primitive's <CODE>valueOf()</CODE>
73 * method does not accept it as a valid <CODE>String</CODE> representation of
74 * the primitive.
75 * <p/>
76 * <P>A value written as the row type can be read as the column type.
77 * <p/>
78 * <PRE>
79 * | | boolean byte short char int long float double String byte[]
80 * |----------------------------------------------------------------------
81 * |boolean | X X
82 * |byte | X X X X X
83 * |short | X X X X
84 * |char | X X
85 * |int | X X X
86 * |long | X X
87 * |float | X X X
88 * |double | X X
89 * |String | X X X X X X X X
90 * |byte[] | X
91 * |----------------------------------------------------------------------
92 * </PRE>
93 * <p/>
94 * <P>Attempting to read a null value as a primitive type must be treated
95 * as calling the primitive's corresponding <code>valueOf(String)</code>
96 * conversion method with a null value. Since <code>char</code> does not
97 * support a <code>String</code> conversion, attempting to read a null value
98 * as a <code>char</code> must throw a <code>NullPointerException</code>.
99 *
100 * @see javax.jms.Session#createStreamMessage()
101 * @see javax.jms.BytesMessage
102 * @see javax.jms.MapMessage
103 * @see javax.jms.Message
104 * @see javax.jms.ObjectMessage
105 * @see javax.jms.TextMessage
106 */
107
108 public class ActiveMQStreamMessage extends ActiveMQMessage implements StreamMessage {
109
110 private DataOutputStream dataOut;
111 private ByteArrayOutputStream bytesOut;
112 private DataInputStream dataIn;
113 private int bytesToRead = -1;
114
115
116 /***
117 * Return the type of Packet
118 *
119 * @return integer representation of the type of Packet
120 */
121
122 public int getPacketType() {
123 return ACTIVEMQ_STREAM_MESSAGE;
124 }
125
126 /***
127 * @return Returns a shallow copy of the message instance
128 * @throws JMSException
129 */
130
131 public ActiveMQMessage shallowCopy() throws JMSException {
132 ActiveMQStreamMessage other = new ActiveMQStreamMessage();
133 this.initializeOther(other);
134 try {
135 other.setBodyAsBytes(this.getBodyAsBytes());
136 }
137 catch (IOException e) {
138 JMSException jmsEx = new JMSException("setBodyAsBytes() failed");
139 jmsEx.setLinkedException(e);
140 throw jmsEx;
141 }
142 return other;
143 }
144
145 /***
146 * @return Returns a deep copy of the message - note the header fields are only shallow copied
147 * @throws JMSException
148 */
149
150 public ActiveMQMessage deepCopy() throws JMSException {
151 ActiveMQStreamMessage other = new ActiveMQStreamMessage();
152 this.initializeOther(other);
153 try {
154 if (this.getBodyAsBytes() != null) {
155 byte[] data = new byte[this.getBodyAsBytes().length];
156 System.arraycopy(this.getBodyAsBytes(), 0, data, 0, data.length);
157 other.setBodyAsBytes(data);
158 }
159 }
160 catch (IOException e) {
161 JMSException jmsEx = new JMSException("setBodyAsBytes() failed");
162 jmsEx.setLinkedException(e);
163 throw jmsEx;
164 }
165 return other;
166 }
167
168
169 /***
170 * Clears out the message body. Clearing a message's body does not clear
171 * its header values or property entries.
172 * <p/>
173 * <P>If this message body was read-only, calling this method leaves
174 * the message body in the same state as an empty body in a newly
175 * created message.
176 *
177 * @throws JMSException if the JMS provider fails to clear the message
178 * body due to some internal error.
179 */
180
181 public void clearBody() throws JMSException {
182 super.clearBody();
183 this.dataOut = null;
184 this.dataIn = null;
185 this.bytesOut = null;
186 }
187
188 /***
189 * Reads a <code>boolean</code> from the stream message.
190 *
191 * @return the <code>boolean</code> value read
192 * @throws JMSException if the JMS provider fails to read the message
193 * due to some internal error.
194 * @throws MessageEOFException if unexpected end of message stream has
195 * been reached.
196 * @throws MessageFormatException if this type conversion is invalid.
197 * @throws MessageNotReadableException if the message is in write-only
198 * mode.
199 */
200
201 public boolean readBoolean() throws JMSException {
202 initializeReading();
203 try {
204 if (this.dataIn.available() == 0) {
205 throw new MessageEOFException("reached end of data");
206 }
207 if (this.dataIn.available() < 2) {
208 throw new MessageFormatException("Not enough data left to read value");
209 }
210
211 this.dataIn.mark(10);
212 int type = this.dataIn.read();
213 if (type == BOOLEAN) {
214 return this.dataIn.readBoolean();
215 }
216 if (type == STRING) {
217 return Boolean.valueOf(this.dataIn.readUTF()).booleanValue();
218 }
219 else {
220 this.dataIn.reset();
221 throw new MessageFormatException(" not a boolean type");
222 }
223 }
224 catch (EOFException e) {
225 JMSException jmsEx = new MessageEOFException(e.getMessage());
226 jmsEx.setLinkedException(e);
227 throw jmsEx;
228 }
229 catch (IOException e) {
230 JMSException jmsEx = new MessageFormatException(e.getMessage());
231 jmsEx.setLinkedException(e);
232 throw jmsEx;
233 }
234 }
235
236
237 /***
238 * Reads a <code>byte</code> value from the stream message.
239 *
240 * @return the next byte from the stream message as a 8-bit
241 * <code>byte</code>
242 * @throws JMSException if the JMS provider fails to read the message
243 * due to some internal error.
244 * @throws MessageEOFException if unexpected end of message stream has
245 * been reached.
246 * @throws MessageFormatException if this type conversion is invalid.
247 * @throws MessageNotReadableException if the message is in write-only
248 * mode.
249 */
250
251 public byte readByte() throws JMSException {
252 initializeReading();
253 try {
254 if (this.dataIn.available() == 0) {
255 throw new MessageEOFException("reached end of data");
256 }
257 if (this.dataIn.available() < 2) {
258 throw new MessageFormatException("Not enough data left to read value");
259 }
260
261 this.dataIn.mark(10);
262 int type = this.dataIn.read();
263 if (type == BYTE) {
264 return this.dataIn.readByte();
265 }
266 if (type == STRING) {
267 return Byte.valueOf(this.dataIn.readUTF()).byteValue();
268 }
269 else {
270 this.dataIn.reset();
271 throw new MessageFormatException(" not a byte type");
272 }
273 }
274 catch (NumberFormatException mfe) {
275 try {
276 this.dataIn.reset();
277 }
278 catch (IOException ioe) {
279 JMSException jmsEx = new JMSException("reset failed");
280 jmsEx.setLinkedException(ioe);
281 }
282 throw mfe;
283
284 }
285 catch (EOFException e) {
286 JMSException jmsEx = new MessageEOFException(e.getMessage());
287 jmsEx.setLinkedException(e);
288 throw jmsEx;
289 }
290 catch (IOException e) {
291 JMSException jmsEx = new MessageFormatException(e.getMessage());
292 jmsEx.setLinkedException(e);
293 throw jmsEx;
294 }
295 }
296
297
298 /***
299 * Reads a 16-bit integer from the stream message.
300 *
301 * @return a 16-bit integer from the stream message
302 * @throws JMSException if the JMS provider fails to read the message
303 * due to some internal error.
304 * @throws MessageEOFException if unexpected end of message stream has
305 * been reached.
306 * @throws MessageFormatException if this type conversion is invalid.
307 * @throws MessageNotReadableException if the message is in write-only
308 * mode.
309 */
310
311 public short readShort() throws JMSException {
312 initializeReading();
313 try {
314 if (this.dataIn.available() == 0) {
315 throw new MessageEOFException("reached end of data");
316 }
317 if (this.dataIn.available() < 2) {
318 throw new MessageFormatException("Not enough data left to read value");
319 }
320
321 this.dataIn.mark(17);
322 int type = this.dataIn.read();
323 if (type == SHORT) {
324 return this.dataIn.readShort();
325 }
326 if (type == BYTE) {
327 return this.dataIn.readByte();
328 }
329 if (type == STRING) {
330 return Short.valueOf(this.dataIn.readUTF()).shortValue();
331 }
332 else {
333 this.dataIn.reset();
334 throw new MessageFormatException(" not a short type");
335 }
336 }
337 catch (NumberFormatException mfe) {
338 try {
339 this.dataIn.reset();
340 }
341 catch (IOException ioe) {
342 JMSException jmsEx = new JMSException("reset failed");
343 jmsEx.setLinkedException(ioe);
344 }
345 throw mfe;
346
347 }
348 catch (EOFException e) {
349 JMSException jmsEx = new MessageEOFException(e.getMessage());
350 jmsEx.setLinkedException(e);
351 throw jmsEx;
352 }
353 catch (IOException e) {
354 JMSException jmsEx = new MessageFormatException(e.getMessage());
355 jmsEx.setLinkedException(e);
356 throw jmsEx;
357 }
358
359 }
360
361
362 /***
363 * Reads a Unicode character value from the stream message.
364 *
365 * @return a Unicode character from the stream message
366 * @throws JMSException if the JMS provider fails to read the message
367 * due to some internal error.
368 * @throws MessageEOFException if unexpected end of message stream has
369 * been reached.
370 * @throws MessageFormatException if this type conversion is invalid
371 * @throws MessageNotReadableException if the message is in write-only
372 * mode.
373 */
374
375 public char readChar() throws JMSException {
376 initializeReading();
377 try {
378 if (this.dataIn.available() == 0) {
379 throw new MessageEOFException("reached end of data");
380 }
381 if (this.dataIn.available() < 2) {
382 throw new MessageFormatException("Not enough data left to read value");
383 }
384
385 this.dataIn.mark(17);
386 int type = this.dataIn.read();
387 if (type == CHAR) {
388 return this.dataIn.readChar();
389 }
390 else {
391 this.dataIn.reset();
392 throw new MessageFormatException(" not a char type");
393 }
394 }
395 catch (NumberFormatException mfe) {
396 try {
397 this.dataIn.reset();
398 }
399 catch (IOException ioe) {
400 JMSException jmsEx = new JMSException("reset failed");
401 jmsEx.setLinkedException(ioe);
402 }
403 throw mfe;
404
405 }
406 catch (EOFException e) {
407 JMSException jmsEx = new MessageEOFException(e.getMessage());
408 jmsEx.setLinkedException(e);
409 throw jmsEx;
410 }
411 catch (IOException e) {
412 JMSException jmsEx = new MessageFormatException(e.getMessage());
413 jmsEx.setLinkedException(e);
414 throw jmsEx;
415 }
416 }
417
418
419 /***
420 * Reads a 32-bit integer from the stream message.
421 *
422 * @return a 32-bit integer value from the stream message, interpreted
423 * as an <code>int</code>
424 * @throws JMSException if the JMS provider fails to read the message
425 * due to some internal error.
426 * @throws MessageEOFException if unexpected end of message stream has
427 * been reached.
428 * @throws MessageFormatException if this type conversion is invalid.
429 * @throws MessageNotReadableException if the message is in write-only
430 * mode.
431 */
432
433 public int readInt() throws JMSException {
434 initializeReading();
435 try {
436 if (this.dataIn.available() == 0) {
437 throw new MessageEOFException("reached end of data");
438 }
439 if (this.dataIn.available() < 2) {
440 throw new MessageFormatException("Not enough data left to read value");
441 }
442
443 this.dataIn.mark(33);
444 int type = this.dataIn.read();
445 if (type == INT) {
446 return this.dataIn.readInt();
447 }
448 if (type == SHORT) {
449 return this.dataIn.readShort();
450 }
451 if (type == BYTE) {
452 return this.dataIn.readByte();
453 }
454 if (type == STRING) {
455 return Integer.valueOf(this.dataIn.readUTF()).intValue();
456 }
457 else {
458 this.dataIn.reset();
459 throw new MessageFormatException(" not an int type");
460 }
461 }
462 catch (NumberFormatException mfe) {
463 try {
464 this.dataIn.reset();
465 }
466 catch (IOException ioe) {
467 JMSException jmsEx = new JMSException("reset failed");
468 jmsEx.setLinkedException(ioe);
469 }
470 throw mfe;
471
472 }
473 catch (EOFException e) {
474 JMSException jmsEx = new MessageEOFException(e.getMessage());
475 jmsEx.setLinkedException(e);
476 throw jmsEx;
477 }
478 catch (IOException e) {
479 JMSException jmsEx = new MessageFormatException(e.getMessage());
480 jmsEx.setLinkedException(e);
481 throw jmsEx;
482 }
483 }
484
485
486 /***
487 * Reads a 64-bit integer from the stream message.
488 *
489 * @return a 64-bit integer value from the stream message, interpreted as
490 * a <code>long</code>
491 * @throws JMSException if the JMS provider fails to read the message
492 * due to some internal error.
493 * @throws MessageEOFException if unexpected end of message stream has
494 * been reached.
495 * @throws MessageFormatException if this type conversion is invalid.
496 * @throws MessageNotReadableException if the message is in write-only
497 * mode.
498 */
499
500 public long readLong() throws JMSException {
501 initializeReading();
502 try {
503 if (this.dataIn.available() == 0) {
504 throw new MessageEOFException("reached end of data");
505 }
506 if (this.dataIn.available() < 2) {
507 throw new MessageFormatException("Not enough data left to read value");
508 }
509
510 this.dataIn.mark(65);
511 int type = this.dataIn.read();
512 if (type == LONG) {
513 return this.dataIn.readLong();
514 }
515 if (type == INT) {
516 return this.dataIn.readInt();
517 }
518 if (type == SHORT) {
519 return this.dataIn.readShort();
520 }
521 if (type == BYTE) {
522 return this.dataIn.readByte();
523 }
524 if (type == STRING) {
525 return Long.valueOf(this.dataIn.readUTF()).longValue();
526 }
527 else {
528 this.dataIn.reset();
529 throw new MessageFormatException(" not a long type");
530 }
531 }
532 catch (NumberFormatException mfe) {
533 try {
534 this.dataIn.reset();
535 }
536 catch (IOException ioe) {
537 JMSException jmsEx = new JMSException("reset failed");
538 jmsEx.setLinkedException(ioe);
539 }
540 throw mfe;
541
542 }
543 catch (EOFException e) {
544 JMSException jmsEx = new MessageEOFException(e.getMessage());
545 jmsEx.setLinkedException(e);
546 throw jmsEx;
547 }
548 catch (IOException e) {
549 JMSException jmsEx = new MessageFormatException(e.getMessage());
550 jmsEx.setLinkedException(e);
551 throw jmsEx;
552 }
553 }
554
555
556 /***
557 * Reads a <code>float</code> from the stream message.
558 *
559 * @return a <code>float</code> value from the stream message
560 * @throws JMSException if the JMS provider fails to read the message
561 * due to some internal error.
562 * @throws MessageEOFException if unexpected end of message stream has
563 * been reached.
564 * @throws MessageFormatException if this type conversion is invalid.
565 * @throws MessageNotReadableException if the message is in write-only
566 * mode.
567 */
568
569 public float readFloat() throws JMSException {
570 initializeReading();
571 try {
572 if (this.dataIn.available() == 0) {
573 throw new MessageEOFException("reached end of data");
574 }
575 if (this.dataIn.available() < 2) {
576 throw new MessageFormatException("Not enough data left to read value");
577 }
578
579 this.dataIn.mark(33);
580 int type = this.dataIn.read();
581 if (type == FLOAT) {
582 return this.dataIn.readFloat();
583 }
584 if (type == STRING) {
585 return Float.valueOf(this.dataIn.readUTF()).floatValue();
586 }
587 else {
588 this.dataIn.reset();
589 throw new MessageFormatException(" not a float type");
590 }
591 }
592 catch (NumberFormatException mfe) {
593 try {
594 this.dataIn.reset();
595 }
596 catch (IOException ioe) {
597 JMSException jmsEx = new JMSException("reset failed");
598 jmsEx.setLinkedException(ioe);
599 }
600 throw mfe;
601
602 }
603 catch (EOFException e) {
604 JMSException jmsEx = new MessageEOFException(e.getMessage());
605 jmsEx.setLinkedException(e);
606 throw jmsEx;
607 }
608 catch (IOException e) {
609 JMSException jmsEx = new MessageFormatException(e.getMessage());
610 jmsEx.setLinkedException(e);
611 throw jmsEx;
612 }
613 }
614
615
616 /***
617 * Reads a <code>double</code> from the stream message.
618 *
619 * @return a <code>double</code> value from the stream message
620 * @throws JMSException if the JMS provider fails to read the message
621 * due to some internal error.
622 * @throws MessageEOFException if unexpected end of message stream has
623 * been reached.
624 * @throws MessageFormatException if this type conversion is invalid.
625 * @throws MessageNotReadableException if the message is in write-only
626 * mode.
627 */
628
629 public double readDouble() throws JMSException {
630 initializeReading();
631 try {
632 if (this.dataIn.available() == 0) {
633 throw new MessageEOFException("reached end of data");
634 }
635 if (this.dataIn.available() < 2) {
636 throw new MessageFormatException("Not enough data left to read value");
637 }
638
639 this.dataIn.mark(65);
640 int type = this.dataIn.read();
641 if (type == DOUBLE) {
642 return this.dataIn.readDouble();
643 }
644 if (type == FLOAT) {
645 return this.dataIn.readFloat();
646 }
647 if (type == STRING) {
648 return Double.valueOf(this.dataIn.readUTF()).doubleValue();
649 }
650 else {
651 this.dataIn.reset();
652 throw new MessageFormatException(" not a double type");
653 }
654 }
655 catch (NumberFormatException mfe) {
656 try {
657 this.dataIn.reset();
658 }
659 catch (IOException ioe) {
660 JMSException jmsEx = new JMSException("reset failed");
661 jmsEx.setLinkedException(ioe);
662 }
663 throw mfe;
664
665 }
666 catch (EOFException e) {
667 JMSException jmsEx = new MessageEOFException(e.getMessage());
668 jmsEx.setLinkedException(e);
669 throw jmsEx;
670 }
671 catch (IOException e) {
672 JMSException jmsEx = new MessageFormatException(e.getMessage());
673 jmsEx.setLinkedException(e);
674 throw jmsEx;
675 }
676 }
677
678
679 /***
680 * Reads a <CODE>String</CODE> from the stream message.
681 *
682 * @return a Unicode string from the stream message
683 * @throws JMSException if the JMS provider fails to read the message
684 * due to some internal error.
685 * @throws MessageEOFException if unexpected end of message stream has
686 * been reached.
687 * @throws MessageFormatException if this type conversion is invalid.
688 * @throws MessageNotReadableException if the message is in write-only
689 * mode.
690 */
691
692 public String readString() throws JMSException {
693 initializeReading();
694 try {
695 if (this.dataIn.available() == 0) {
696 throw new MessageEOFException("reached end of data");
697 }
698 if (this.dataIn.available() < 2) {
699 throw new MessageFormatException("Not enough data left to read value");
700 }
701
702 this.dataIn.mark(65);
703 int type = this.dataIn.read();
704 if (type == NULL) {
705 return null;
706 }
707 if (type == STRING) {
708 return this.dataIn.readUTF();
709 }
710 if (type == LONG) {
711 return new Long(this.dataIn.readLong()).toString();
712 }
713 if (type == INT) {
714 return new Integer(this.dataIn.readInt()).toString();
715 }
716 if (type == SHORT) {
717 return new Short(this.dataIn.readShort()).toString();
718 }
719 if (type == BYTE) {
720 return new Byte(this.dataIn.readByte()).toString();
721 }
722 if (type == FLOAT) {
723 return new Float(this.dataIn.readFloat()).toString();
724 }
725 if (type == DOUBLE) {
726 return new Double(this.dataIn.readDouble()).toString();
727 }
728 if (type == BOOLEAN) {
729 return (this.dataIn.readBoolean() ? Boolean.TRUE : Boolean.FALSE).toString();
730 }
731 if (type == CHAR) {
732 return new Character(this.dataIn.readChar()).toString();
733 }
734 else {
735 this.dataIn.reset();
736 throw new MessageFormatException(" not a String type");
737 }
738 }
739 catch (NumberFormatException mfe) {
740 try {
741 this.dataIn.reset();
742 }
743 catch (IOException ioe) {
744 JMSException jmsEx = new JMSException("reset failed");
745 jmsEx.setLinkedException(ioe);
746 }
747 throw mfe;
748
749 }
750 catch (EOFException e) {
751 JMSException jmsEx = new MessageEOFException(e.getMessage());
752 jmsEx.setLinkedException(e);
753 throw jmsEx;
754 }
755 catch (IOException e) {
756 JMSException jmsEx = new MessageFormatException(e.getMessage());
757 jmsEx.setLinkedException(e);
758 throw jmsEx;
759 }
760 }
761
762
763 /***
764 * Reads a byte array field from the stream message into the
765 * specified <CODE>byte[]</CODE> object (the read buffer).
766 * <p/>
767 * <P>To read the field value, <CODE>readBytes</CODE> should be
768 * successively called
769 * until it returns a value less than the length of the read buffer.
770 * The value of the bytes in the buffer following the last byte
771 * read is undefined.
772 * <p/>
773 * <P>If <CODE>readBytes</CODE> returns a value equal to the length of the
774 * buffer, a subsequent <CODE>readBytes</CODE> call must be made. If there
775 * are no more bytes to be read, this call returns -1.
776 * <p/>
777 * <P>If the byte array field value is null, <CODE>readBytes</CODE>
778 * returns -1.
779 * <p/>
780 * <P>If the byte array field value is empty, <CODE>readBytes</CODE>
781 * returns 0.
782 * <p/>
783 * <P>Once the first <CODE>readBytes</CODE> call on a <CODE>byte[]</CODE>
784 * field value has been made,
785 * the full value of the field must be read before it is valid to read
786 * the next field. An attempt to read the next field before that has
787 * been done will throw a <CODE>MessageFormatException</CODE>.
788 * <p/>
789 * <P>To read the byte field value into a new <CODE>byte[]</CODE> object,
790 * use the <CODE>readObject</CODE> method.
791 *
792 * @param value the buffer into which the data is read
793 * @return the total number of bytes read into the buffer, or -1 if
794 * there is no more data because the end of the byte field has been
795 * reached
796 * @throws JMSException if the JMS provider fails to read the message
797 * due to some internal error.
798 * @throws MessageEOFException if unexpected end of message stream has
799 * been reached.
800 * @throws MessageFormatException if this type conversion is invalid.
801 * @throws MessageNotReadableException if the message is in write-only
802 * mode.
803 * @see #readObject()
804 */
805
806 public int readBytes(byte[] value) throws JMSException {
807 initializeReading();
808 try {
809 if (value == null) {
810 throw new NullPointerException();
811 }
812 if (bytesToRead == 0) {
813 bytesToRead = -1;
814 return -1;
815 }
816 else if (bytesToRead > 0) {
817 if (value.length >= bytesToRead) {
818 bytesToRead = 0;
819 return dataIn.read(value, 0, bytesToRead);
820 }
821 else {
822 bytesToRead -= value.length;
823 return dataIn.read(value);
824 }
825 }
826 else {
827 if (this.dataIn.available() == 0) {
828 throw new MessageEOFException("reached end of data");
829 }
830 if (this.dataIn.available() < 1) {
831 throw new MessageFormatException("Not enough data left to read value");
832 }
833 this.dataIn.mark(value.length + 1);
834 int type = this.dataIn.read();
835 if (this.dataIn.available() < 1) {
836 return -1;
837 }
838 if (type != BYTES) {
839 throw new MessageFormatException("Not a byte array");
840 }
841 int len = this.dataIn.readInt();
842
843 if (len >= value.length) {
844 bytesToRead = len - value.length;
845 return this.dataIn.read(value);
846 }
847 else {
848 bytesToRead = 0;
849 return this.dataIn.read(value, 0, len);
850 }
851 }
852 }
853 catch (EOFException e) {
854 JMSException jmsEx = new MessageEOFException(e.getMessage());
855 jmsEx.setLinkedException(e);
856 throw jmsEx;
857 }
858 catch (IOException e) {
859 JMSException jmsEx = new MessageFormatException(e.getMessage());
860 jmsEx.setLinkedException(e);
861 throw jmsEx;
862 }
863 }
864
865
866 /***
867 * Reads an object from the stream message.
868 * <p/>
869 * <P>This method can be used to return, in objectified format,
870 * an object in the Java programming language ("Java object") that has
871 * been written to the stream with the equivalent
872 * <CODE>writeObject</CODE> method call, or its equivalent primitive
873 * <CODE>write<I>type</I></CODE> method.
874 * <p/>
875 * <P>Note that byte values are returned as <CODE>byte[]</CODE>, not
876 * <CODE>Byte[]</CODE>.
877 * <p/>
878 * <P>An attempt to call <CODE>readObject</CODE> to read a byte field
879 * value into a new <CODE>byte[]</CODE> object before the full value of the
880 * byte field has been read will throw a
881 * <CODE>MessageFormatException</CODE>.
882 *
883 * @return a Java object from the stream message, in objectified
884 * format (for example, if the object was written as an <CODE>int</CODE>,
885 * an <CODE>Integer</CODE> is returned)
886 * @throws JMSException if the JMS provider fails to read the message
887 * due to some internal error.
888 * @throws MessageEOFException if unexpected end of message stream has
889 * been reached.
890 * @throws MessageFormatException if this type conversion is invalid.
891 * @throws MessageNotReadableException if the message is in write-only
892 * mode.
893 * @see #readBytes(byte[] value)
894 */
895
896 public Object readObject() throws JMSException {
897 initializeReading();
898 try {
899 if (this.dataIn.available() == 0) {
900 throw new MessageEOFException("reached end of data");
901 }
902 if (this.dataIn.available() < 1) {
903 throw new MessageFormatException("Not enough data left to read value - avaialable = " + dataIn.available());
904 }
905
906 this.dataIn.mark(65);
907 int type = this.dataIn.read();
908 if (type == NULL) {
909 return null;
910 }
911 if (type == STRING) {
912 return this.dataIn.readUTF();
913 }
914 if (type == LONG) {
915 return new Long(this.dataIn.readLong());
916 }
917 if (type == INT) {
918 return new Integer(this.dataIn.readInt());
919 }
920 if (type == SHORT) {
921 return new Short(this.dataIn.readShort());
922 }
923 if (type == BYTE) {
924 return new Byte(this.dataIn.readByte());
925 }
926 if (type == FLOAT) {
927 return new Float(this.dataIn.readFloat());
928 }
929 if (type == DOUBLE) {
930 return new Double(this.dataIn.readDouble());
931 }
932 if (type == BOOLEAN) {
933 return this.dataIn.readBoolean() ? Boolean.TRUE : Boolean.FALSE;
934 }
935 if (type == CHAR) {
936 return new Character(this.dataIn.readChar());
937 }
938 if (type == BYTES) {
939 int len = this.dataIn.readInt();
940 byte[] value = new byte[len];
941 this.dataIn.read(value);
942 return value;
943 }
944 else {
945 this.dataIn.reset();
946 throw new MessageFormatException("unknown type");
947 }
948 }
949 catch (NumberFormatException mfe) {
950 try {
951 this.dataIn.reset();
952 }
953 catch (IOException ioe) {
954 JMSException jmsEx = new JMSException("reset failed");
955 jmsEx.setLinkedException(ioe);
956 }
957 throw mfe;
958
959 }
960 catch (EOFException e) {
961 JMSException jmsEx = new MessageEOFException(e.getMessage());
962 jmsEx.setLinkedException(e);
963 throw jmsEx;
964 }
965 catch (IOException e) {
966 JMSException jmsEx = new MessageFormatException(e.getMessage());
967 jmsEx.setLinkedException(e);
968 throw jmsEx;
969 }
970 }
971
972
973 /***
974 * Writes a <code>boolean</code> to the stream message.
975 * The value <code>true</code> is written as the value
976 * <code>(byte)1</code>; the value <code>false</code> is written as
977 * the value <code>(byte)0</code>.
978 *
979 * @param value the <code>boolean</code> value to be written
980 * @throws JMSException if the JMS provider fails to write the message
981 * due to some internal error.
982 * @throws MessageNotWriteableException if the message is in read-only
983 * mode.
984 */
985
986 public void writeBoolean(boolean value) throws JMSException {
987 initializeWriting();
988 try {
989 this.dataOut.write(BOOLEAN);
990 this.dataOut.writeBoolean(value);
991 }
992 catch (IOException ioe) {
993 JMSException jmsEx = new JMSException(ioe.getMessage());
994 jmsEx.setLinkedException(ioe);
995 throw jmsEx;
996 }
997 }
998
999
1000 /***
1001 * Writes a <code>byte</code> to the stream message.
1002 *
1003 * @param value the <code>byte</code> value to be written
1004 * @throws JMSException if the JMS provider fails to write the message
1005 * due to some internal error.
1006 * @throws MessageNotWriteableException if the message is in read-only
1007 * mode.
1008 */
1009
1010 public void writeByte(byte value) throws JMSException {
1011 initializeWriting();
1012 try {
1013 this.dataOut.write(BYTE);
1014 this.dataOut.writeByte(value);
1015 }
1016 catch (IOException ioe) {
1017 JMSException jmsEx = new JMSException(ioe.getMessage());
1018 jmsEx.setLinkedException(ioe);
1019 throw jmsEx;
1020 }
1021 }
1022
1023
1024 /***
1025 * Writes a <code>short</code> to the stream message.
1026 *
1027 * @param value the <code>short</code> value to be written
1028 * @throws JMSException if the JMS provider fails to write the message
1029 * due to some internal error.
1030 * @throws MessageNotWriteableException if the message is in read-only
1031 * mode.
1032 */
1033
1034 public void writeShort(short value) throws JMSException {
1035 initializeWriting();
1036 try {
1037 this.dataOut.write(SHORT);
1038 this.dataOut.writeShort(value);
1039 }
1040 catch (IOException ioe) {
1041 JMSException jmsEx = new JMSException(ioe.getMessage());
1042 jmsEx.setLinkedException(ioe);
1043 throw jmsEx;
1044 }
1045 }
1046
1047
1048 /***
1049 * Writes a <code>char</code> to the stream message.
1050 *
1051 * @param value the <code>char</code> value to be written
1052 * @throws JMSException if the JMS provider fails to write the message
1053 * due to some internal error.
1054 * @throws MessageNotWriteableException if the message is in read-only
1055 * mode.
1056 */
1057
1058 public void writeChar(char value) throws JMSException {
1059 initializeWriting();
1060 try {
1061 this.dataOut.write(CHAR);
1062 this.dataOut.writeChar(value);
1063 }
1064 catch (IOException ioe) {
1065 JMSException jmsEx = new JMSException(ioe.getMessage());
1066 jmsEx.setLinkedException(ioe);
1067 throw jmsEx;
1068 }
1069 }
1070
1071
1072 /***
1073 * Writes an <code>int</code> to the stream message.
1074 *
1075 * @param value the <code>int</code> value to be written
1076 * @throws JMSException if the JMS provider fails to write the message
1077 * due to some internal error.
1078 * @throws MessageNotWriteableException if the message is in read-only
1079 * mode.
1080 */
1081
1082 public void writeInt(int value) throws JMSException {
1083 initializeWriting();
1084 try {
1085 this.dataOut.write(INT);
1086 this.dataOut.writeInt(value);
1087 }
1088 catch (IOException ioe) {
1089 JMSException jmsEx = new JMSException(ioe.getMessage());
1090 jmsEx.setLinkedException(ioe);
1091 throw jmsEx;
1092 }
1093 }
1094
1095
1096 /***
1097 * Writes a <code>long</code> to the stream message.
1098 *
1099 * @param value the <code>long</code> value to be written
1100 * @throws JMSException if the JMS provider fails to write the message
1101 * due to some internal error.
1102 * @throws MessageNotWriteableException if the message is in read-only
1103 * mode.
1104 */
1105
1106 public void writeLong(long value) throws JMSException {
1107 initializeWriting();
1108 try {
1109 this.dataOut.write(LONG);
1110 this.dataOut.writeLong(value);
1111 }
1112 catch (IOException ioe) {
1113 JMSException jmsEx = new JMSException(ioe.getMessage());
1114 jmsEx.setLinkedException(ioe);
1115 throw jmsEx;
1116 }
1117 }
1118
1119
1120 /***
1121 * Writes a <code>float</code> to the stream message.
1122 *
1123 * @param value the <code>float</code> value to be written
1124 * @throws JMSException if the JMS provider fails to write the message
1125 * due to some internal error.
1126 * @throws MessageNotWriteableException if the message is in read-only
1127 * mode.
1128 */
1129
1130 public void writeFloat(float value) throws JMSException {
1131 initializeWriting();
1132 try {
1133 this.dataOut.write(FLOAT);
1134 this.dataOut.writeFloat(value);
1135 }
1136 catch (IOException ioe) {
1137 JMSException jmsEx = new JMSException(ioe.getMessage());
1138 jmsEx.setLinkedException(ioe);
1139 throw jmsEx;
1140 }
1141 }
1142
1143
1144 /***
1145 * Writes a <code>double</code> to the stream message.
1146 *
1147 * @param value the <code>double</code> value to be written
1148 * @throws JMSException if the JMS provider fails to write the message
1149 * due to some internal error.
1150 * @throws MessageNotWriteableException if the message is in read-only
1151 * mode.
1152 */
1153
1154 public void writeDouble(double value) throws JMSException {
1155 initializeWriting();
1156 try {
1157 this.dataOut.write(DOUBLE);
1158 this.dataOut.writeDouble(value);
1159 }
1160 catch (IOException ioe) {
1161 JMSException jmsEx = new JMSException(ioe.getMessage());
1162 jmsEx.setLinkedException(ioe);
1163 throw jmsEx;
1164 }
1165 }
1166
1167
1168 /***
1169 * Writes a <code>String</code> to the stream message.
1170 *
1171 * @param value the <code>String</code> value to be written
1172 * @throws JMSException if the JMS provider fails to write the message
1173 * due to some internal error.
1174 * @throws MessageNotWriteableException if the message is in read-only
1175 * mode.
1176 */
1177
1178 public void writeString(String value) throws JMSException {
1179 initializeWriting();
1180 try {
1181 if (value == null) {
1182 this.dataOut.write(NULL);
1183 }
1184 else {
1185 this.dataOut.write(STRING);
1186 this.dataOut.writeUTF(value);
1187 }
1188 }
1189 catch (IOException ioe) {
1190 JMSException jmsEx = new JMSException(ioe.getMessage());
1191 jmsEx.setLinkedException(ioe);
1192 throw jmsEx;
1193 }
1194 }
1195
1196
1197 /***
1198 * Writes a byte array field to the stream message.
1199 * <p/>
1200 * <P>The byte array <code>value</code> is written to the message
1201 * as a byte array field. Consecutively written byte array fields are
1202 * treated as two distinct fields when the fields are read.
1203 *
1204 * @param value the byte array value to be written
1205 * @throws JMSException if the JMS provider fails to write the message
1206 * due to some internal error.
1207 * @throws MessageNotWriteableException if the message is in read-only
1208 * mode.
1209 */
1210
1211 public void writeBytes(byte[] value) throws JMSException {
1212 writeBytes(value, 0, value.length);
1213 }
1214
1215
1216 /***
1217 * Writes a portion of a byte array as a byte array field to the stream
1218 * message.
1219 * <p/>
1220 * <P>The a portion of the byte array <code>value</code> is written to the
1221 * message as a byte array field. Consecutively written byte
1222 * array fields are treated as two distinct fields when the fields are
1223 * read.
1224 *
1225 * @param value the byte array value to be written
1226 * @param offset the initial offset within the byte array
1227 * @param length the number of bytes to use
1228 * @throws JMSException if the JMS provider fails to write the message
1229 * due to some internal error.
1230 * @throws MessageNotWriteableException if the message is in read-only
1231 * mode.
1232 */
1233
1234 public void writeBytes(byte[] value, int offset, int length) throws JMSException {
1235 initializeWriting();
1236 try {
1237 this.dataOut.write(BYTES);
1238 this.dataOut.writeInt(length);
1239 this.dataOut.write(value, offset, length);
1240 }
1241 catch (IOException ioe) {
1242 JMSException jmsEx = new JMSException(ioe.getMessage());
1243 jmsEx.setLinkedException(ioe);
1244 throw jmsEx;
1245 }
1246 }
1247
1248
1249 /***
1250 * Writes an object to the stream message.
1251 * <p/>
1252 * <P>This method works only for the objectified primitive
1253 * object types (<code>Integer</code>, <code>Double</code>,
1254 * <code>Long</code> ...), <code>String</code> objects, and byte
1255 * arrays.
1256 *
1257 * @param value the Java object to be written
1258 * @throws JMSException if the JMS provider fails to write the message
1259 * due to some internal error.
1260 * @throws MessageFormatException if the object is invalid.
1261 * @throws MessageNotWriteableException if the message is in read-only
1262 * mode.
1263 */
1264
1265 public void writeObject(Object value) throws JMSException {
1266 initializeWriting();
1267 if (value == null) {
1268 try {
1269 this.dataOut.write(NULL);
1270 }
1271 catch (IOException ioe) {
1272 JMSException jmsEx = new JMSException(ioe.getMessage());
1273 jmsEx.setLinkedException(ioe);
1274 throw jmsEx;
1275 }
1276 }
1277 else if (value instanceof String) {
1278 writeString(value.toString());
1279 }
1280 else if (value instanceof Character) {
1281 writeChar(((Character) value).charValue());
1282 }
1283 else if (value instanceof Boolean) {
1284 writeBoolean(((Boolean) value).booleanValue());
1285 }
1286 else if (value instanceof Byte) {
1287 writeByte(((Byte) value).byteValue());
1288 }
1289 else if (value instanceof Short) {
1290 writeShort(((Short) value).shortValue());
1291 }
1292 else if (value instanceof Integer) {
1293 writeInt(((Integer) value).intValue());
1294 }
1295 else if (value instanceof Float) {
1296 writeFloat(((Float) value).floatValue());
1297 }
1298 else if (value instanceof Double) {
1299 writeDouble(((Double) value).doubleValue());
1300 }
1301 else if (value instanceof byte[]) {
1302 writeBytes((byte[]) value);
1303 }
1304 }
1305
1306
1307 /***
1308 * Puts the message body in read-only mode and repositions the stream of
1309 * bytes to the beginning.
1310 *
1311 * @throws JMSException if an internal error occurs
1312 */
1313
1314 public void reset() throws JMSException {
1315 super.readOnlyMessage = true;
1316 if (this.dataOut != null) {
1317 try {
1318 this.dataOut.flush();
1319 super.setBodyAsBytes(this.bytesOut.toByteArray());
1320 dataOut.close();
1321 }
1322 catch (IOException ioe) {
1323 JMSException jmsEx = new JMSException("reset failed: " + ioe.getMessage());
1324 jmsEx.setLinkedException(ioe);
1325 throw jmsEx;
1326 }
1327 }
1328 this.bytesOut = null;
1329 this.dataIn = null;
1330 this.dataOut = null;
1331 }
1332
1333 /***
1334 * @param bodyAsBytes The bodyAsBytes to set.
1335 */
1336 public void setBodyAsBytes(byte[] bodyAsBytes) {
1337 super.setBodyAsBytes(bodyAsBytes);
1338 dataOut = null;
1339 dataIn = null;
1340 }
1341
1342 /***
1343 * @return Returns the data body
1344 * @throws IOException if an exception occurs retreiving the data
1345 */
1346 public byte[] getBodyAsBytes() throws IOException {
1347 if (this.dataOut != null) {
1348 this.dataOut.flush();
1349 super.setBodyAsBytes(this.bytesOut.toByteArray());
1350 dataOut.close();
1351 dataOut = null;
1352 }
1353 return super.getBodyAsBytes();
1354 }
1355
1356
1357 private void initializeWriting() throws MessageNotWriteableException {
1358 if (super.readOnlyMessage) {
1359 throw new MessageNotWriteableException("This message is in read-only mode");
1360 }
1361 if (this.dataOut == null) {
1362 this.bytesOut = new ByteArrayOutputStream();
1363 this.dataOut = new DataOutputStream(this.bytesOut);
1364 }
1365 }
1366
1367
1368 private void initializeReading() throws MessageNotReadableException {
1369 if (!super.readOnlyMessage) {
1370 throw new MessageNotReadableException("This message is in write-only mode");
1371 }
1372 try {
1373 byte[] data = super.getBodyAsBytes();
1374 if (this.dataIn == null && data != null) {
1375 ByteArrayInputStream bytesIn = new ByteArrayInputStream(data);
1376 this.dataIn = new DataInputStream(bytesIn);
1377 }
1378 }
1379 catch (IOException e) {
1380 MessageNotReadableException mnr = new MessageNotReadableException("getBodyAsBytes failed");
1381 mnr.setLinkedException(e);
1382 throw mnr;
1383 }
1384 }
1385 }