View Javadoc
1 /*** 2 * Redistribution and use of this software and associated documentation 3 * ("Software"), with or without modification, are permitted provided 4 * that the following conditions are met: 5 * 6 * 1. Redistributions of source code must retain copyright 7 * statements and notices. Redistributions must also contain a 8 * copy of this document. 9 * 10 * 2. Redistributions in binary form must reproduce the 11 * above copyright notice, this list of conditions and the 12 * following disclaimer in the documentation and/or other 13 * materials provided with the distribution. 14 * 15 * 3. The name "Exolab" must not be used to endorse or promote 16 * products derived from this Software without prior written 17 * permission of Exoffice Technologies. For written permission, 18 * please contact info@exolab.org. 19 * 20 * 4. Products derived from this Software may not be called "Exolab" 21 * nor may "Exolab" appear in their names without prior written 22 * permission of Exoffice Technologies. Exolab is a registered 23 * trademark of Exoffice Technologies. 24 * 25 * 5. Due credit should be given to the Exolab Project 26 * (http://www.exolab.org/). 27 * 28 * THIS SOFTWARE IS PROVIDED BY EXOFFICE TECHNOLOGIES AND CONTRIBUTORS 29 * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT 30 * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 31 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 32 * EXOFFICE TECHNOLOGIES OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 33 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 34 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 35 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 37 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 38 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 39 * OF THE POSSIBILITY OF SUCH DAMAGE. 40 * 41 * Copyright 2000-2004 (C) Exoffice Technologies Inc. All Rights Reserved. 42 * 43 * $Id: 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