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