1 /* 2 * Copyright (c) 1998-2002 The Jgroup Team. 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU Lesser General Public License version 2 as 6 * published by the Free Software Foundation. 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU Lesser General Public License for more details. 12 * 13 * You should have received a copy of the GNU Lesser General Public License 14 * along with this program; if not, write to the Free Software 15 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 16 * 17 */ 18 19 package jgroup.util; 20 21 import java.io.Externalizable; 22 import java.io.IOException; 23 import java.io.ObjectInput; 24 import java.io.ObjectOutput; 25 import java.io.ObjectOutputStream; 26 import java.io.OutputStream; 27 import java.io.UTFDataFormatException; 28 29 import jgroup.core.ConfigManager; 30 31 /** 32 * The <code>OutMessage</code> class is an output stream specially 33 * designed to be used with a transport layer which fragments messages. 34 * Messages are stored in fragments, each fragment being composed by an 35 * header, a payload and a trailer. The length of these fields may be 36 * configured when creating a new <code>OutMessage</code>. Data written 37 * in the message are stored in the payload field of the fragments. 38 * 39 * @author Alberto Montresor 40 * @author Hein Meling 41 * @since Jgroup 0.8 42 */ 43 public final class OutMessage 44 extends OutputStream 45 implements ObjectOutput, Externalizable 46 { 47 48 //////////////////////////////////////////////////////////////////////////////////////////// 49 // Constants 50 //////////////////////////////////////////////////////////////////////////////////////////// 51 52 private static final long serialVersionUID = -6820954817177822595L; 53 54 /** Initial number of fragments composing a message */ 55 private static final int NUM_BUFFER = 16; 56 57 58 //////////////////////////////////////////////////////////////////////////////////////////// 59 // Static section 60 //////////////////////////////////////////////////////////////////////////////////////////// 61 62 /** Temporary buffer used in writeTemp */ 63 private static byte[] temp = new byte[8]; 64 65 66 //////////////////////////////////////////////////////////////////////////////////////////// 67 // Fields 68 //////////////////////////////////////////////////////////////////////////////////////////// 69 70 private int payload; // Payload size in bytes 71 private int header; // Header size in bytes 72 private int trailer; // Trailer size in bytes 73 private int totlen; // Total length of a fragment 74 private int endpayload; // Last byte used for payload 75 76 private int position; // Current position in the stream [0..byteCount] 77 private int fragment; // Current fragment number 78 private int offset; // Current offset in the fragment 79 80 private int fragmentCount; // Total number of fragments used 81 private int byteCount; // Total number of bytes written 82 private int maxsize; // Max size in bytes 83 84 private byte[] buffer; // Current fragment 85 private byte[][] fragments; // Byte array fragments 86 87 /** Number of bytes written so far */ 88 private int written; 89 90 91 //////////////////////////////////////////////////////////////////////////////////////////// 92 // Constructors 93 //////////////////////////////////////////////////////////////////////////////////////////// 94 95 /** 96 * Construct a message with header and trailer of length zero. In 97 * this way, OutMessage may be used as a more efficient version of 98 * ByteArrayOutputStream. 99 */ 100 public OutMessage(int payload) 101 { 102 this(payload, 0, 0); 103 } 104 105 106 /** 107 * Construct a OutMessage starting from an InMessage. In this way, it 108 * possible to forward messages received from other servers without 109 * copying them. 110 */ 111 public OutMessage(InMessage msg) 112 { 113 // Store and compute fixed limits 114 this.payload = msg.getPayload(); 115 this.header = msg.getHeader(); 116 this.trailer = msg.getTrailer(); 117 endpayload = payload + header; 118 totlen = endpayload + trailer; 119 120 // Initialize variables regarding message size 121 byteCount = msg.getByteCount(); 122 fragmentCount = msg.getFragmentCount(); 123 /* 124 * Patch to avoid problems when forwarding/resending a message 125 * to a different set of receivers (or different cluster) than 126 * the original InMessage had in its trailer. This means that 127 * the MsgJG will want to add flow control data and a different 128 * receiver set to the trailer of the newly created OutMessage. 129 * The size of these fields may differ (require more capacity) 130 * than the original InMessage. Note that the actual byte count, 131 * and the size of the allocated fragments double array will not 132 * change until more capacity is required. 133 * 134 * The original code looked like this: 135 * maxsize = byteCount; 136 */ 137 maxsize = Integer.MAX_VALUE; 138 139 // Initialize variables regarding message position in the stream 140 position = 0; 141 fragment = 0; 142 offset = header; 143 144 // Create fragments 145 fragments = msg.getFragments(); 146 buffer = fragments[0]; 147 } 148 149 150 /** 151 * This constructor builds a message output stream subdivided in 152 * fragments of length <code>header + payload + trailer</code>. 153 * Used for messages whose size is initially unknown. 154 * 155 * @exception IndexOutOfBoundsException 156 * Raised if <code>payload</code> is negative or equal to zero; 157 * <code>header</code> is negative; <code>trailer</code> is negative. 158 */ 159 public OutMessage(int payload, int header, int trailer) 160 { 161 if (payload <= 0 || header < 0 || trailer < 0) 162 throw new IndexOutOfBoundsException(); 163 164 // Store and compute fixed limits 165 this.payload = payload; 166 this.header = header; 167 this.trailer = trailer; 168 endpayload = payload + header; 169 totlen = endpayload + trailer; 170 171 // Initialize variables regarding message size 172 byteCount = 0; 173 fragmentCount = 1; 174 maxsize = Integer.MAX_VALUE; 175 176 // Initialize variables regarding message position in the stream 177 position = 0; 178 fragment = 0; 179 offset = header; 180 181 // Create fragments 182 fragments = new byte[NUM_BUFFER][]; 183 fragments[0] = new byte[totlen]; 184 buffer = fragments[0]; 185 } 186 187 188 /** 189 * This constructor builds a message output stream subdivided in 190 * fragments. Used for messages whose size is known a priori. If 191 * <code>maxsize</code> is less than <code>payload</code>, the 192 * payload of the message is set to <code>maxsize</code>. The total 193 * length of each fragments is <code>header + payload + 194 * trailer</code>. 195 * 196 * @exception IndexOutOfBoundsException 197 * Raised if <code>payload</code> is negative or equal to zero; 198 * <code>header</code> is negative; <code>trailer</code> is 199 * negative; <code>maxsize</code> is negative or equal to zero. 200 */ 201 public OutMessage(int payload, int header, int trailer, int maxsize) 202 { 203 if (payload <= 0 || header < 0 || trailer < 0 || maxsize <= 0) 204 throw new IndexOutOfBoundsException(); 205 206 // Store and compute fixed limits 207 this.payload = (maxsize < payload ? maxsize : payload); 208 this.header = header; 209 this.trailer = trailer; 210 endpayload = this.payload + header; 211 totlen = endpayload + trailer; 212 213 /* 214 * Initialize variables regarding message size; the total number of 215 * bytes is already set to maxsize; in this way, in the following 216 * we'll skip to check whether the size exceed maxsize or not. 217 */ 218 byteCount = maxsize; 219 fragmentCount = ((maxsize-1) / this.payload) + 1; 220 this.maxsize = maxsize; 221 222 // Initialize variables regarding message position in the stream 223 position = 0; 224 fragment = 0; 225 offset = header; 226 227 // Create fragments 228 fragments = new byte[fragmentCount][]; 229 for (int i=0; i< fragmentCount; i++) 230 fragments[i] = new byte[totlen]; 231 buffer = fragments[0]; 232 } 233 234 235 /** 236 * Clear and reset the <code>OutMessage</code> to the given new 237 * maxsize. This occurs by reusing the already allocated memory for 238 * the fragments of this <code>OutMessage</code>. 239 */ 240 public void clear(int newmaxsize) 241 { 242 if (newmaxsize > maxsize) 243 throw new IllegalStateException("The newmaxsize cannot be greater than the creation time maxsize"); 244 245 /* 246 * Initialize variables regarding message size; the total number of 247 * byte is already set to maxsize; in this way, in the following 248 * we'll skip to check whether the size exceed maxsize or not. 249 */ 250 byteCount = newmaxsize; 251 fragmentCount = ((newmaxsize-1) / this.payload) + 1; 252 253 // Initialize variables regarding message position in the stream 254 position = 0; 255 fragment = 0; 256 offset = header; 257 258 buffer = fragments[0]; 259 } 260 261 262 //////////////////////////////////////////////////////////////////////////////////////////// 263 // Methods from Externalizable 264 //////////////////////////////////////////////////////////////////////////////////////////// 265 266 /** 267 * Default constructor for externalization. 268 */ 269 public OutMessage() { } 270 271 272 /** 273 * Restores the content of this object from the marshalled data contained 274 * in the specified input stream. 275 * 276 * @param in the stream to be read 277 */ 278 public void readExternal(ObjectInput in) 279 throws IOException 280 { 281 payload = in.readInt(); 282 header = in.readInt(); 283 trailer = in.readInt(); 284 endpayload = payload + header; 285 totlen = endpayload + trailer; 286 fragmentCount = in.readInt(); 287 byteCount = in.readInt(); 288 maxsize = in.readInt(); 289 written = in.readInt(); 290 291 // Initialize variables for message position in the stream 292 position = 0; 293 fragment = 0; 294 offset = header; 295 296 // Reconstruct fragments from stream 297 fragments = new byte[fragmentCount][]; 298 int fcount = fragmentCount - 1; 299 for (int i = 0; i < fcount; i++) { 300 fragments[i] = new byte[totlen]; 301 in.readFully(fragments[i]); 302 } 303 fragments[fcount] = new byte[totlen]; 304 int lastFragLen = getLastPayload() + header + trailer; 305 in.readFully(fragments[fcount], 0, lastFragLen); 306 buffer = fragments[0]; 307 // System.out.println("outmsg READ: " + this.toString(true)); 308 } 309 310 311 /** 312 * Marshals the content of this object to the specified output stream. 313 * 314 * @param out the stream to be written 315 */ 316 public void writeExternal(ObjectOutput out) 317 throws IOException 318 { 319 out.writeInt(payload); 320 out.writeInt(header); 321 out.writeInt(trailer); 322 out.writeInt(fragmentCount); 323 out.writeInt(byteCount); 324 out.writeInt(maxsize); 325 out.writeInt(written); 326 327 int fcount = fragmentCount - 1; 328 for (int i = 0; i < fcount; i++) { 329 out.write(fragments[i]); 330 } 331 int lastFragLen = getLastPayload() + header + trailer; 332 out.write(fragments[fcount], 0, lastFragLen); 333 // System.out.println("outmsg WRITE: " + this.toString(true)); 334 } 335 336 337 //////////////////////////////////////////////////////////////////////////////////////////// 338 // Methods inherited from OutputStream 339 //////////////////////////////////////////////////////////////////////////////////////////// 340 341 /** 342 * Writes the specified byte to this byte array output stream. 343 * <p> 344 * If the total number of bytes written in the message exceeds the maximum 345 * length, a <code>ArrayIndexOutOfBoundsException</code> is thrown. 346 * 347 * @param b the byte to be written. 348 */ 349 public void write(int b) 350 { 351 /* Increase the number of stored bytes */ 352 position++; 353 if (position > byteCount) { 354 byteCount = position; 355 if (position > maxsize) 356 throw new ArrayIndexOutOfBoundsException(); 357 if (offset == endpayload) { 358 /* 359 * We have reached the end of the last fragment; we add a 360 * another fragment to store the new byte; fragment, offset and 361 * buffer are updated to point to the fist byte of the new 362 * fragment. 363 */ 364 fragment++; 365 offset = header; 366 enlargeSpace(byteCount); 367 buffer = fragments[fragment]; 368 } 369 } 370 /* We write the byte and increment offset */ 371 buffer[offset++] = (byte) b; 372 written += 1; 373 } 374 375 376 /** 377 * Writes <code>len</code> bytes from the specified byte array 378 * starting at offset <code>off</code> to this msg output stream. 379 * <p> 380 * If <code>b</code> is <code>null</code>, a 381 * <code>NullPointerException</code> is thrown. 382 * <p> 383 * If <code>off</code> is negative, or <code>len</code> is negative, or 384 * <code>off+len</code> is greater than the length of the array 385 * <code>b</code>, then an <tt>IndexOutOfBoundsException</tt> is thrown. 386 * <p> 387 * If the total number of bytes written in the message exceeds the maximum 388 * length, a <tt>ArrayIndexOutOfBoundsException</tt> is thrown. 389 * 390 * @param b the data. 391 * @param off the start offset in the data. 392 * @param len the number of bytes to write. 393 */ 394 public void write(byte[] b, int off, int len) 395 { 396 if ((off<0) || (off>b.length) || (len<0) || ((off+len) > b.length)) { 397 throw new IndexOutOfBoundsException(); 398 } else if (len == 0) { 399 return; 400 } else { 401 position += len; 402 if (position > byteCount) { 403 byteCount = position; 404 if (position > maxsize) { 405 throw new ArrayIndexOutOfBoundsException(); 406 } 407 } 408 if (offset + len <= endpayload) { 409 System.arraycopy(b, off, buffer, offset, len); 410 offset += len; 411 } else { 412 enlargeSpace(byteCount); 413 int todo = endpayload - offset; 414 System.arraycopy(b, off, buffer, offset, todo); 415 int remaining = len-todo; 416 off += todo; 417 fragment++; 418 while (remaining > payload) { 419 System.arraycopy(b, off, fragments[fragment], header, payload); 420 remaining -= payload; 421 off += payload; 422 fragment++; 423 } 424 System.arraycopy(b, off, fragments[fragment], header, remaining); 425 offset = remaining + header; 426 buffer = fragments[fragment]; 427 428 } 429 } 430 written += len; 431 } 432 433 /** 434 * Writes <code>b.length</code> bytes from the specified byte array 435 * to this output stream. The general contract for <code>write(b)</code> 436 * is that it should have exactly the same effect as the call 437 * <code>write(b, 0, b.length)</code>. 438 * <p> 439 * If the total number of bytes written in the message exceeds the maximum 440 * length, a <tt>ArrayIndexOutOfBoundsException</tt> is thrown. 441 * 442 * @param b the data. 443 * 444 */ 445 public void write(byte[] b) 446 { 447 write(b, 0, b.length); 448 } 449 450 //////////////////////////////////////////////////////////////////////////////////////////// 451 // Methods from DataOutput 452 //////////////////////////////////////////////////////////////////////////////////////////// 453 454 /** 455 * Writes a <code>boolean</code> value to this output stream. 456 * If the argument <code>v</code> 457 * is <code>true</code>, the value <code>(byte)1</code> 458 * is written; if <code>v</code> is <code>false</code>, 459 * the value <code>(byte)0</code> is written. 460 * The byte written by this method may 461 * be read by the <code>readBoolean</code> 462 * method of interface <code>DataInput</code>, 463 * which will then return a <code>boolean</code> 464 * equal to <code>v</code>. 465 * 466 * @param v the boolean to be written. 467 * @exception IOException if an I/O error occurs. 468 */ 469 public void writeBoolean(boolean v) throws IOException 470 { 471 write(v ? 1 : 0); 472 } 473 474 /** 475 * Writes to the output stream the eight low- 476 * order bits of the argument <code>v</code>. 477 * The 24 high-order bits of <code>v</code> 478 * are ignored. (This means that <code>writeByte</code> 479 * does exactly the same thing as <code>write</code> 480 * for an integer argument.) The byte written 481 * by this method may be read by the <code>readByte</code> 482 * method of interface <code>DataInput</code>, 483 * which will then return a <code>byte</code> 484 * equal to <code>(byte)v</code>. 485 * 486 * @param v the byte value to be written. 487 * @exception IOException if an I/O error occurs. 488 */ 489 public void writeByte(int v) throws IOException 490 { 491 write(v); 492 } 493 494 /** 495 * Writes two bytes to the output 496 * stream to represent the value of the argument. 497 * The byte values to be written, in the order 498 * shown, are: <p> 499 * <pre><code> 500 * (byte)(0xff & (v >> 8)) 501 * (byte)(0xff & v) 502 * </code> </pre> <p> 503 * The bytes written by this method may be 504 * read by the <code>readShort</code> method 505 * of interface <code>DataInput</code> , which 506 * will then return a <code>short</code> equal 507 * to <code>(short)v</code>. 508 * 509 * @param v the <code>short</code> value to be written. 510 * @exception IOException if an I/O error occurs. 511 */ 512 public void writeShort(int v) throws IOException 513 { 514 write2(v); 515 } 516 517 /** 518 * Writes a <code>char</code> value, wich 519 * is comprised of two bytes, to the 520 * output stream. 521 * The byte values to be written, in the order 522 * shown, are: 523 * <p><pre><code> 524 * (byte)(0xff & (v >> 8)) 525 * (byte)(0xff & v) 526 * </code></pre><p> 527 * The bytes written by this method may be 528 * read by the <code>readChar</code> method 529 * of interface <code>DataInput</code> , which 530 * will then return a <code>char</code> equal 531 * to <code>(char)v</code>. 532 * 533 * @param v the <code>char</code> value to be written. 534 * @exception IOException if an I/O error occurs. 535 */ 536 public void writeChar(int v) throws IOException 537 { 538 write2(v); 539 } 540 541 /** 542 * Writes an <code>int</code> value, which is 543 * comprised of four bytes, to the output stream. 544 * The byte values to be written, in the order 545 * shown, are: 546 * <p><pre><code> 547 * (byte)(0xff & (v >> 24)) 548 * (byte)(0xff & (v >> 16)) 549 * (byte)(0xff & (v >>    8)) 550 * (byte)(0xff & v) 551 * </code></pre><p> 552 * The bytes written by this method may be read 553 * by the <code>readInt</code> method of interface 554 * <code>DataInput</code> , which will then 555 * return an <code>int</code> equal to <code>v</code>. 556 * 557 * @param v the <code>int</code> value to be written. 558 * @exception IOException if an I/O error occurs. 559 */ 560 public void writeInt(int v) throws IOException 561 { 562 write4(v); 563 } 564 565 /** 566 * Writes an <code>long</code> value, which is 567 * comprised of four bytes, to the output stream. 568 * The byte values to be written, in the order 569 * shown, are: 570 * <p><pre><code> 571 * (byte)(0xff & (v >> 48)) 572 * (byte)(0xff & (v >> 40)) 573 * (byte)(0xff & (v >> 32)) 574 * (byte)(0xff & (v >> 24)) 575 * (byte)(0xff & (v >> 16)) 576 * (byte)(0xff & (v >> 8)) 577 * (byte)(0xff & v) 578 * </code></pre><p> 579 * The bytes written by this method may be 580 * read by the <code>readLong</code> method 581 * of interface <code>DataInput</code> , which 582 * will then return a <code>long</code> equal 583 * to <code>v</code>. 584 * 585 * @param v the <code>long</code> value to be written. 586 * @exception IOException if an I/O error occurs. 587 */ 588 public void writeLong(long v) throws IOException 589 { 590 write8(v); 591 } 592 593 /** 594 * Writes a <code>float</code> value, 595 * which is comprised of four bytes, to the output stream. 596 * It does this as if it first converts this 597 * <code>float</code> value to an <code>int</code> 598 * in exactly the manner of the <code>Float.floatToIntBits</code> 599 * method and then writes the <code>int</code> 600 * value in exactly the manner of the <code>writeInt</code> 601 * method. The bytes written by this method 602 * may be read by the <code>readFloat</code> 603 * method of interface <code>DataInput</code>, 604 * which will then return a <code>float</code> 605 * equal to <code>v</code>. 606 * 607 * @param v the <code>float</code> value to be written. 608 * @exception IOException if an I/O error occurs. 609 */ 610 public void writeFloat(float v) throws IOException 611 { 612 write4(Float.floatToIntBits(v)); 613 } 614 615 /** 616 * Writes a <code>double</code> value, 617 * which is comprised of eight bytes, to the output stream. 618 * It does this as if it first converts this 619 * <code>double</code> value to a <code>long</code> 620 * in exactly the manner of the <code>Double.doubleToLongBits</code> 621 * method and then writes the <code>long</code> 622 * value in exactly the manner of the <code>writeLong</code> 623 * method. The bytes written by this method 624 * may be read by the <code>readDouble</code> 625 * method of interface <code>DataInput</code>, 626 * which will then return a <code>double</code> 627 * equal to <code>v</code>. 628 * 629 * @param v the <code>double</code> value to be written. 630 * @exception IOException if an I/O error occurs. 631 */ 632 public void writeDouble(double v) throws IOException 633 { 634 write8(Double.doubleToLongBits(v)); 635 } 636 637 /** 638 * Writes a string to the output stream. 639 * For every character in the string 640 * <code>s</code>, taken in order, one byte 641 * is written to the output stream. If 642 * <code>s</code> is <code>null</code>, a <code>NullPointerException</code> 643 * is thrown.<p> If <code>s.length</code> 644 * is zero, then no bytes are written. Otherwise, 645 * the character <code>s[0]</code> is written 646 * first, then <code>s[1]</code>, and so on; 647 * the last character written is <code>s[s.length-1]</code>. 648 * For each character, one byte is written, 649 * the low-order byte, in exactly the manner 650 * of the <code>writeByte</code> method . The 651 * high-order eight bits of each character 652 * in the string are ignored. 653 * 654 * @param s the string of bytes to be written. 655 * @exception IOException if an I/O error occurs. 656 */ 657 public void writeBytes(String s) throws IOException 658 { 659 int len = s.length(); 660 for (int i = 0 ; i < len ; i++) { 661 write((byte)s.charAt(i)); 662 } 663 } 664 665 /** 666 * Writes every character in the string <code>s</code>, 667 * to the output stream, in order, 668 * two bytes per character. If <code>s</code> 669 * is <code>null</code>, a <code>NullPointerException</code> 670 * is thrown. If <code>s.length</code> 671 * is zero, then no characters are written. 672 * Otherwise, the character <code>s[0]</code> 673 * is written first, then <code>s[1]</code>, 674 * and so on; the last character written is 675 * <code>s[s.length-1]</code>. For each character, 676 * two bytes are actually written, high-order 677 * byte first, in exactly the manner of the 678 * <code>writeChar</code> method. 679 * 680 * @param s the string value to be written. 681 * @exception IOException if an I/O error occurs. 682 */ 683 public void writeChars(String s) throws IOException 684 { 685 int len = s.length(); 686 for (int i = 0 ; i < len ; i++) { 687 int v = s.charAt(i); 688 write((v >>> 8) & 0xFF); 689 write((v >>> 0) & 0xFF); 690 } 691 } 692 693 /** 694 * Writes two bytes of length information 695 * to the output stream, followed 696 * by the Java modified UTF representation 697 * of every character in the string <code>s</code>. 698 * If <code>s</code> is <code>null</code>, 699 * a <code>NullPointerException</code> is thrown. 700 * Each character in the string <code>s</code> 701 * is converted to a group of one, two, or 702 * three bytes, depending on the value of the 703 * character.<p> 704 * If a character <code>c</code> 705 * is in the range <code>\u0001</code> through 706 * <code>\u007f</code>, it is represented 707 * by one byte:<p> 708 * <pre>(byte)c </pre> <p> 709 * If a character <code>c</code> is <code>\u0000</code> 710 * or is in the range <code>\u0080</code> 711 * through <code>\u07ff</code>, then it is 712 * represented by two bytes, to be written 713 * in the order shown:<p> <pre><code> 714 * (byte)(0xc0 | (0x1f & (c >> 6))) 715 * (byte)(0x80 | (0x3f & c)) 716 * </code></pre> <p> If a character 717 * <code>c</code> is in the range <code>\u0800</code> 718 * through <code>uffff</code>, then it is 719 * represented by three bytes, to be written 720 * in the order shown:<p> <pre><code> 721 * (byte)(0xc0 | (0x0f & (c >> 12))) 722 * (byte)(0x80 | (0x3f & (c >> 6))) 723 * (byte)(0x80 | (0x3f & c)) 724 * </code></pre> <p> First, 725 * the total number of bytes needed to represent 726 * all the characters of <code>s</code> is 727 * calculated. If this number is larger than 728 * <code>65535</code>, then a <code>UTFDataFormatError</code> 729 * is thrown. Otherwise, this length is written 730 * to the output stream in exactly the manner 731 * of the <code>writeShort</code> method; 732 * after this, the one-, two-, or three-byte 733 * representation of each character in the 734 * string <code>s</code> is written.<p> The 735 * bytes written by this method may be read 736 * by the <code>readUTF</code> method of interface 737 * <code>DataInput</code> , which will then 738 * return a <code>String</code> equal to <code>s</code>. 739 * 740 * @param str the string value to be written. 741 * @exception IOException if an I/O error occurs. 742 */ 743 public void writeUTF(String str) throws IOException 744 { 745 int strlen = str.length(); 746 int utflen = 0; 747 char[] charr = new char[strlen]; 748 int c, count = 0; 749 750 str.getChars(0, strlen, charr, 0); 751 752 for (int i = 0; i < strlen; i++) { 753 c = charr[i]; 754 if ((c >= 0x0001) && (c <= 0x007F)) { 755 utflen++; 756 } else if (c > 0x07FF) { 757 utflen += 3; 758 } else { 759 utflen += 2; 760 } 761 } 762 763 if (utflen > 65535) 764 throw new UTFDataFormatException(); 765 766 byte[] bytearr = new byte[utflen+2]; 767 bytearr[count++] = (byte) ((utflen >>> 8) & 0xFF); 768 bytearr[count++] = (byte) ((utflen >>> 0) & 0xFF); 769 for (int i = 0; i < strlen; i++) { 770 c = charr[i]; 771 if ((c >= 0x0001) && (c <= 0x007F)) { 772 bytearr[count++] = (byte) c; 773 } else if (c > 0x07FF) { 774 bytearr[count++] = (byte) (0xE0 | ((c >> 12) & 0x0F)); 775 bytearr[count++] = (byte) (0x80 | ((c >> 6) & 0x3F)); 776 bytearr[count++] = (byte) (0x80 | ((c >> 0) & 0x3F)); 777 } else { 778 bytearr[count++] = (byte) (0xC0 | ((c >> 6) & 0x1F)); 779 bytearr[count++] = (byte) (0x80 | ((c >> 0) & 0x3F)); 780 } 781 } 782 write(bytearr); 783 } 784 785 /** 786 * Returns the current value of the counter <code>written</code>, 787 * the number of bytes written to this data output stream so far. 788 * Note: in this version, the counter may overflow. 789 * 790 * @return the value of the <code>written</code> field. 791 * @see java.io.DataOutputStream#written 792 */ 793 public final int size() { 794 return written; 795 } 796 797 798 //////////////////////////////////////////////////////////////////////////////////////////// 799 // Methods from ObjectOutput 800 //////////////////////////////////////////////////////////////////////////////////////////// 801 802 /** 803 * Write an object to the underlying storage or stream. The 804 * class that implements this interface defines how the object is 805 * written. 806 * 807 * @exception IOException Any of the usual Input/Output related exceptions. 808 */ 809 public void writeObject(Object obj) 810 throws IOException 811 { 812 // workaround since OutMessage don't provide native support for writeObject() 813 ObjectOutputStream oos = new ObjectOutputStream(this); 814 oos.writeObject(obj); 815 } 816 817 818 //////////////////////////////////////////////////////////////////////////////////////////// 819 // Public methods 820 //////////////////////////////////////////////////////////////////////////////////////////// 821 822 /** 823 * Move the current position in the stream to the specified 824 * <code>position</code>. <p> 825 * 826 * @exception IndexOutOfBoundsException 827 * Raised if the new position in the message exceeds the maximum 828 * length. 829 */ 830 public void seek(int position) 831 { 832 fragment = position / payload; 833 offset = header + position % payload; 834 this.position = position; 835 /* 836 * FIXED HEIN 29/7-02: this didn't enlarge the space, when the 837 * position was at the first position in the next fragment. Thus, 838 * it was fixed using >= instead of >. See also related comment in 839 * the enlargeSpace() method below. 840 */ 841 if (position >= byteCount) { 842 byteCount = position; 843 if (position > maxsize) { 844 throw new IndexOutOfBoundsException("New position (" + position 845 + ") exceeds maximum length:" + maxsize); 846 } 847 enlargeSpace(position); 848 } 849 buffer = fragments[fragment]; 850 } 851 852 853 /** 854 * Write a short integer value <code>v</code> into the stream at the 855 * current position. <p> 856 * 857 * @exception ArrayIndexOutOfBoundsException 858 * Raised if the total number of bytes written in the message 859 * exceeds the maximum length. 860 */ 861 public void write2(int v) 862 { 863 position += 2; 864 if (position > byteCount) { 865 byteCount = position; 866 if (position > maxsize) 867 throw new ArrayIndexOutOfBoundsException(); 868 } 869 if (offset + 2 > endpayload) { 870 temp[0] = (byte) (v >>> 8); 871 temp[1] = (byte) (v >>> 0); 872 writeTemp(temp, 2); 873 } else { 874 buffer[offset++] = (byte) (v >>> 8); 875 buffer[offset++] = (byte) (v >>> 0); 876 } 877 written += 2; 878 } 879 880 881 /** 882 * Write an integer value <code>v</code> into the stream at the 883 * current position. <p> 884 * 885 * @exception ArrayIndexOutOfBoundsException 886 * Raised if the total number of bytes written in the message 887 * exceeds the maximum length. 888 */ 889 public void write4(int v) 890 { 891 position += 4; 892 if (position > byteCount) { 893 byteCount = position; 894 if (position > maxsize) 895 throw new ArrayIndexOutOfBoundsException(); 896 } 897 if (offset + 4 > endpayload) { 898 temp[0] = (byte) (v >>> 24); 899 temp[1] = (byte) (v >>> 16); 900 temp[2] = (byte) (v >>> 8); 901 temp[3] = (byte) (v >>> 0); 902 writeTemp(temp, 4); 903 } else { 904 buffer[offset++] = (byte) (v >>> 24); 905 buffer[offset++] = (byte) (v >>> 16); 906 buffer[offset++] = (byte) (v >>> 8); 907 buffer[offset++] = (byte) (v >>> 0); 908 } 909 written += 4; 910 } 911 912 913 /** 914 * Write a long value <code>v</code> into the stream at the current 915 * position. <p> 916 * 917 * @exception ArrayIndexOutOfBoundsException 918 * Raised if the total number of bytes written in the message 919 * exceeds the maximum length. 920 */ 921 public void write8(long v) 922 { 923 position += 8; 924 if (position > byteCount) { 925 byteCount = position; 926 if (position > maxsize) 927 throw new ArrayIndexOutOfBoundsException(); 928 } 929 int v1 = (int) (v >>> 32); 930 int v2 = (int) (v & 0xFFFFFFFFL); 931 if (offset + 8 > endpayload) { 932 temp[0] = (byte) (v1 >>> 24); 933 temp[1] = (byte) (v1 >>> 16); 934 temp[2] = (byte) (v1 >>> 8); 935 temp[3] = (byte) (v1 >>> 0); 936 temp[4] = (byte) (v2 >>> 24); 937 temp[5] = (byte) (v2 >>> 16); 938 temp[6] = (byte) (v2 >>> 8); 939 temp[7] = (byte) (v2 >>> 0); 940 writeTemp(temp, 8); 941 } else { 942 buffer[offset++] = (byte) (v1 >>> 24); 943 buffer[offset++] = (byte) (v1 >>> 16); 944 buffer[offset++] = (byte) (v1 >>> 8); 945 buffer[offset++] = (byte) (v1 >>> 0); 946 buffer[offset++] = (byte) (v2 >>> 24); 947 buffer[offset++] = (byte) (v2 >>> 16); 948 buffer[offset++] = (byte) (v2 >>> 8); 949 buffer[offset++] = (byte) (v2 >>> 0); 950 } 951 written += 8; 952 } 953 954 /** 955 * Write the content of another <code>OutMessage</code> into the 956 * current message. <p> 957 * 958 * @exception ArrayIndexOutOfBoundsException 959 * Raised if the total number of bytes written in the message 960 * exceeds the maximum length. 961 */ 962 public void write(OutMessage msg) 963 { 964 byte[][] fragments = msg.getFragments(); 965 int length = msg.getFragmentCount(); 966 for (int i=0; i < length-1; i++) 967 write(fragments[i], msg.getHeader(), msg.getPayload()); 968 write(fragments[length-1], msg.getHeader(), msg.getLastPayload()); 969 } 970 971 /** 972 * Reset the byte array 973 */ 974 public void reset() 975 { 976 // Reinitialize fragments 977 buffer = fragments[0]; 978 979 // Initialize variables regarding message size 980 byteCount = 0; 981 fragmentCount = 1; 982 maxsize = Integer.MAX_VALUE; 983 984 // Initialize variables regarding message position in the stream 985 position = 0; 986 fragment = 0; 987 offset = header; 988 } 989 990 /** 991 * Compute the checksum of the bytes stored in the message 992 * 993 * @return the computed checksum. 994 */ 995 public int computeChecksum() 996 { 997 byte check = 0; 998 999 for (int j = 0; j < fragmentCount-1; j++) 1000 for (int i = header; i < endpayload; i++) 1001 check ^= fragments[j][i]; 1002 byte[] buffer = fragments[fragmentCount-1]; 1003 int end = getLastPayload() + header; 1004 for (int i = header; i < end; i++) 1005 check ^= buffer[i]; 1006 1007 return check; 1008 } 1009 1010 1011 //////////////////////////////////////////////////////////////////////////////////////////// 1012 // Package methods 1013 //////////////////////////////////////////////////////////////////////////////////////////// 1014 1015 /** 1016 * Return the current position in this message. 1017 */ 1018 public int getPosition() 1019 { 1020 return position; 1021 } 1022 1023 /** 1024 * Return the length of the payload for this message. 1025 */ 1026 public int getPayload() 1027 { 1028 return payload; 1029 } 1030 1031 /** 1032 * Return the length of the header for this message. 1033 */ 1034 public int getHeader() 1035 { 1036 return header; 1037 } 1038 1039 /** 1040 * Return the length of the trailer for this message. 1041 */ 1042 public int getTrailer() 1043 { 1044 return trailer; 1045 } 1046 1047 /** 1048 * Returns the total number of bytes stored in this message. 1049 */ 1050 public int getByteCount() 1051 { 1052 return byteCount; 1053 } 1054 1055 /** 1056 * Returns the number of fragments composing the message. 1057 */ 1058 public int getFragmentCount() 1059 { 1060 return fragmentCount; 1061 } 1062 1063 /** 1064 * Returns the length of the payload of the last fragment composing 1065 * the message. 1066 */ 1067 public int getLastPayload() 1068 { 1069 if (byteCount % payload == 0) 1070 return payload; 1071 else 1072 return (byteCount % payload); 1073 } 1074 1075 /** 1076 * Returns an array of byte arrays which contains the fragments 1077 * composing the message. The length of fragment array may be 1078 * greater than the number of fragments actually allocated; the 1079 * actual number of fragments is obtained by invoking method 1080 * <code>getFragmentCount</code>. 1081 */ 1082 public byte[][] getFragments() 1083 { 1084 return fragments; 1085 } 1086 1087 /** 1088 * Converts the whole <code>OutMessage</code> into a byte array. 1089 */ 1090 public byte[] toByteArray() 1091 { 1092 byte[] ret = new byte[byteCount]; 1093 if (byteCount > 0) { 1094 for (int i=0; i < fragmentCount-1; i++) { 1095 System.arraycopy(fragments[i], 0, ret, i*payload, payload); 1096 } 1097 System.arraycopy(fragments[fragmentCount-1], 0, ret, 1098 (fragmentCount-1)*payload, getLastPayload()); 1099 } 1100 return ret; 1101 } 1102 1103 1104 //////////////////////////////////////////////////////////////////////////////////////////// 1105 // Private methods 1106 //////////////////////////////////////////////////////////////////////////////////////////// 1107 1108 /** 1109 * Copy <code>length</code> bytes from array <code>temp</code>; this 1110 * method is invoked when methods <code>write2</code>, 1111 * <code>write4</code>, or <code>write8</code> should write part of 1112 * their data in the current fragment and the rest in the following 1113 * fragment. 1114 */ 1115 private void writeTemp(byte[] temp, int length) 1116 { 1117 enlargeSpace(byteCount); 1118 for (int i = 0; i < length; i++) { 1119 if (offset == endpayload) { 1120 fragment++; 1121 buffer = fragments[fragment]; 1122 offset = header; 1123 } 1124 buffer[offset++] = temp[i]; 1125 } 1126 } 1127 1128 1129 /** 1130 * Allocate new byte fragments to guarantee that there is enough 1131 * space to store <code>newsize</code> bytes. 1132 */ 1133 private void enlargeSpace(int newsize) 1134 { 1135 /* 1136 * FIXED HEIN 29/7-02: computing the number of fragments was 1137 * incorrect when the newsize was equal to the payload. This caused 1138 * a problem when doing seek(payload). The seek() would compute 1139 * that it was in the first position of the next fragment, but 1140 * computing nfragment to one less fragment than seek() expected, 1141 * and so it didn't enlarge the space after all. So when doing a 1142 * write() from the new current position, we would get a 1143 * NullPointerException since it was trying to access a buffer[] 1144 * that was not initialized. 1145 * 1146 * The old code was this: 1147 * int nfragment = ((newsize-1) / payload) + 1; 1148 * 1149 * In anycase, this does not lead to many extra memory allocations, 1150 * since the case when newsize equals to payload is very rare. 1151 */ 1152 1153 /* Total number of fragments needed to store <code>newsize</code> bytes. */ 1154 int nfragment = (newsize / payload) + 1; 1155 if (nfragment > fragments.length) { 1156 /* Double the fragments array */ 1157 byte[][] tempbuffer = new byte[nfragment*2][]; 1158 System.arraycopy(fragments, 0, tempbuffer, 0, fragments.length); 1159 fragments = tempbuffer; 1160 } 1161 for (int i = fragmentCount; i < nfragment; i++) 1162 fragments[i] = new byte[totlen]; 1163 if (fragmentCount < nfragment) 1164 fragmentCount = nfragment; 1165 } 1166 1167 1168 //////////////////////////////////////////////////////////////////////////////////////////// 1169 // Object methods 1170 //////////////////////////////////////////////////////////////////////////////////////////// 1171 1172 /** 1173 * Returns a string representation of this object (used only for 1174 * debugging) 1175 */ 1176 public String toString(boolean all) 1177 { 1178 StringBuilder buf = new StringBuilder(); 1179 buf.append("[OutMessage: byteCnt="); 1180 buf.append(byteCount); 1181 buf.append(", payload="); 1182 buf.append(payload); 1183 buf.append(", header="); 1184 buf.append(header); 1185 buf.append(", trailer="); 1186 buf.append(trailer); 1187 buf.append(", totlen="); 1188 buf.append(totlen); 1189 buf.append(", endpayload="); 1190 buf.append(endpayload); 1191 buf.append(", position="); 1192 buf.append(position); 1193 buf.append(", fragment="); 1194 buf.append(fragment); 1195 buf.append(", offset="); 1196 buf.append(offset); 1197 buf.append(", fragmentCount="); 1198 buf.append(fragmentCount); 1199 buf.append(", maxsize="); 1200 buf.append(maxsize); 1201 buf.append("]"); 1202 1203 if (ConfigManager.logMsgContent) { 1204 buf.append(": "); 1205 } else { 1206 // Return without logging message content 1207 return buf.toString(); 1208 } 1209 1210 if (all) { 1211 for (int i = 0; i < fragmentCount-1; i++) { 1212 for (int j = header; j < endpayload; j++) { 1213 buf.append(" "); 1214 buf.append(Util.byte2str(fragments[i][j])); 1215 } 1216 } 1217 int stop = byteCount - (fragmentCount-1)*payload + header; 1218 for (int j = header; j < stop; j++) { 1219 buf.append(" "); 1220 buf.append(Util.byte2str(fragments[fragmentCount-1][j])); 1221 } 1222 } else { 1223 for (int i = 0, j = header; j < endpayload; j++) { 1224 buf.append(" "); 1225 buf.append(Util.byte2str(fragments[i][j])); 1226 } 1227 } 1228 return buf.toString(); 1229 } 1230 1231 1232 /** 1233 * Returns a string representation of this object 1234 */ 1235 public String toString() 1236 { 1237 return toString(false); 1238 } 1239 1240 } // END OutMessage