KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > catalina > cluster > io > XByteBuffer


1 /*
2  * Copyright 1999,2004 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package org.apache.catalina.cluster.io;
18
19 import java.io.ByteArrayInputStream JavaDoc;
20 import java.io.ByteArrayOutputStream JavaDoc;
21 import java.util.zip.GZIPInputStream JavaDoc;
22 import java.util.zip.GZIPOutputStream JavaDoc;
23
24 /**
25  * The XByteBuffer provides a dual functionality.
26  * One, it stores message bytes and automatically extends the byte buffer if needed.<BR>
27  * Two, it can encode and decode packages so that they can be defined and identified
28  * as they come in on a socket.
29  * <br/>
30  * Transfer package:
31  * <ul>
32  * <li><b>START_DATA/b> - 7 bytes - <i>FLT2002</i></li>
33  * <li><b>SIZE</b> - 4 bytes - size of the data package</li>
34  * <li><b>DATA</b> - should be as many bytes as the prev SIZE</li>
35  * <li><b>END_DATA</b> - 7 bytes - <i>TLF2003</i></lI>
36  * </ul>
37  *
38  * @author Filip Hanik
39  * @author Peter Rossbach
40  * @version $Revision: 1.11 $, $Date: 2005/03/25 22:21:26 $
41  */

42 public class XByteBuffer
43 {
44     
45     public static org.apache.commons.logging.Log log =
46         org.apache.commons.logging.LogFactory.getLog( XByteBuffer.class );
47     
48     /**
49      * This is a package header, 7 bytes (FLT2002)
50      */

51     public static final byte[] START_DATA = {70,76,84,50,48,48,50};
52     
53     /**
54      * This is the package footer, 7 bytes (TLF2003)
55      */

56     public static final byte[] END_DATA = {84,76,70,50,48,48,51};
57  
58     /**
59      * Default size on the initial byte buffer
60      */

61     static final int DEF_SIZE = 1024;
62  
63     /**
64      * Default size to extend the buffer with
65      */

66     static final int DEF_EXT = 1024;
67     
68     /**
69      * Variable to hold the data
70      */

71     protected byte[] buf = null;
72     
73     /**
74      * Current length of data in the buffer
75      */

76     protected int bufSize = 0;
77
78     /**
79      * Compress/Decompress user data
80      */

81     protected boolean compress = true ;
82     
83     /**
84      * Constructs a new XByteBuffer
85      * @param size - the initial size of the byte buffer
86      */

87     public XByteBuffer(int size) {
88         buf = new byte[size];
89     }
90
91     /**
92      * Constructs a new XByteBuffer with an initial size of 1024 bytes
93      */

94     public XByteBuffer() {
95         this(DEF_SIZE);
96     }
97
98     /**
99      * Create Buffer and switch compress mode (off)
100      * @param compress
101      */

102     public XByteBuffer(boolean compress) {
103         this(DEF_SIZE);
104         this.compress = compress ;
105     }
106
107     /**
108      * Returns the bytes in the buffer, in its exact length
109      */

110     public byte[] getBytes() {
111         byte[] b = new byte[bufSize];
112         System.arraycopy(buf,0,b,0,bufSize);
113         return b;
114     }
115
116     /**
117      * Resets the buffer
118      */

119     public void clear() {
120         bufSize = 0;
121     }
122
123     /**
124      * Appends the data to the buffer. If the data is incorrectly formatted, ie, the data should always start with the
125      * header, false will be returned and the data will be discarded.
126      * @param b - bytes to be appended
127      * @param off - the offset to extract data from
128      * @param len - the number of bytes to append.
129      * @return true if the data was appended correctly. Returns false if the package is incorrect, ie missing header or something, or the length of data is 0
130      */

131     public boolean append(byte[] b, int off, int len) {
132         if ((off < 0) || (off > b.length) || (len < 0) ||
133             ((off + len) > b.length) || ((off + len) < 0)) {
134             throw new IndexOutOfBoundsException JavaDoc();
135         } else if (len == 0) {
136             return false;
137         }
138
139         int newcount = bufSize + len;
140         if (newcount > buf.length) {
141             byte newbuf[] = new byte[Math.max(buf.length << 1, newcount)];
142             System.arraycopy(buf, 0, newbuf, 0, bufSize);
143             buf = newbuf;
144         }
145         System.arraycopy(b, off, buf, bufSize, len);
146         bufSize = newcount;
147
148         if (bufSize > START_DATA.length && (firstIndexOf(buf,0,START_DATA)==-1)){
149             bufSize = 0;
150             log.error("Discarded the package, invalid header");
151             return false;
152         }
153         return true;
154     }
155
156
157     /**
158      * Internal mechanism to make a check if a complete package exists
159      * within the buffer
160      * @return - true if a complete package (header,size,data,footer) exists within the buffer
161      */

162     public int countPackages()
163     {
164         int cnt = 0;
165         int pos = START_DATA.length;
166         int start = 0;
167
168         while ( start < bufSize ) {
169             //first check start header
170
int index = XByteBuffer.firstIndexOf(buf,start,START_DATA);
171             //if the header (START_DATA) isn't the first thing or
172
//the buffer isn't even 10 bytes
173
if ( index != start || ((bufSize-start)<10) ) break;
174             //then get the size 4 bytes
175
int size = toInt(buf, pos);
176             //now the total buffer has to be long enough to hold
177
//START_DATA.length+4+size+END_DATA.length
178
pos = start + START_DATA.length + 4 + size;
179             if ( (pos + END_DATA.length) > bufSize) break;
180             //and finally check the footer of the package END_DATA
181
int newpos = firstIndexOf(buf, pos, END_DATA);
182             //mismatch, there is no package
183
if (newpos != pos) break;
184             //increase the packet count
185
cnt++;
186             //reset the values
187
start = pos + END_DATA.length;
188             pos = start + START_DATA.length;
189         }
190         return cnt;
191     }
192
193     /**
194      * Method to check if a package exists in this byte buffer.
195      * @return - true if a complete package (header,size,data,footer) exists within the buffer
196      */

197     public boolean doesPackageExist() {
198         return (countPackages()>0);
199     }
200
201     /**
202      * Extracts the message bytes from a package.
203      * If no package exists, a IllegalStateException will be thrown.
204      * @param clearFromBuffer - if true, the package will be removed from the byte buffer
205      * @return - returns the actual message bytes (header, size and footer not included).
206      */

207     public byte[] extractPackage(boolean clearFromBuffer)
208             throws java.io.IOException JavaDoc {
209         int psize = countPackages();
210         if (psize == 0)
211             throw new java.lang.IllegalStateException JavaDoc(
212                     "No package exists in XByteBuffer");
213         int size = toInt(buf, START_DATA.length);
214         byte[] data = new byte[size];
215         System.arraycopy(buf, START_DATA.length + 4, data, 0, size);
216         if (clearFromBuffer) {
217             int totalsize = START_DATA.length + 4 + size + END_DATA.length;
218             bufSize = bufSize - totalsize;
219             System.arraycopy(buf, totalsize, buf, 0, bufSize);
220         }
221         byte[] result;
222         if (compress) { // decompress user data
223
// FIXME: This generate a lot of garbagge for messages larger than 1024 bytes
224
ByteArrayInputStream JavaDoc bin =
225                 new ByteArrayInputStream JavaDoc(data);
226             GZIPInputStream JavaDoc gin =
227                 new GZIPInputStream JavaDoc(bin);
228             byte[] tmp = new byte[1024];
229             int length = gin.read(tmp);
230             result = new byte[0];
231             while (length > 0) {
232                 byte[] tmpdata = result;
233                 result = new byte[result.length + length];
234                 System.arraycopy(tmpdata, 0, result, 0, tmpdata.length);
235                 System.arraycopy(tmp, 0, result, tmpdata.length, length);
236                 length = gin.read(tmp);
237             }
238             gin.close();
239         } else { // send data direct
240
result = data;
241         }
242         return result;
243     }
244
245     /**
246      * Convert four bytes to an int
247      * @param b - the byte array containing the four bytes
248      * @param off - the offset
249      * @return the integer value constructed from the four bytes
250      * @exception java.lang.ArrayIndexOutOfBoundsException
251      */

252     public static int toInt(byte[] b,int off){
253         return ( ( (int) b[off+3]) & 0xFF) +
254             ( ( ( (int) b[off+2]) & 0xFF) << 8) +
255             ( ( ( (int) b[off+1]) & 0xFF) << 16) +
256             ( ( ( (int) b[off+0]) & 0xFF) << 24);
257     }
258
259     /**
260      * Convert eight bytes to a long
261      * @param b - the byte array containing the four bytes
262      * @param off - the offset
263      * @return the long value constructed from the eight bytes
264      * @exception java.lang.ArrayIndexOutOfBoundsException
265      */

266     public static long toLong(byte[] b,int off){
267         return ( ( (long) b[off+7]) & 0xFF) +
268             ( ( ( (long) b[off+6]) & 0xFF) << 8) +
269             ( ( ( (long) b[off+5]) & 0xFF) << 16) +
270             ( ( ( (long) b[off+4]) & 0xFF) << 24) +
271             ( ( ( (long) b[off+3]) & 0xFF) << 32) +
272             ( ( ( (long) b[off+2]) & 0xFF) << 40) +
273             ( ( ( (long) b[off+1]) & 0xFF) << 48) +
274             ( ( ( (long) b[off+0]) & 0xFF) << 56);
275     }
276
277     /**
278      * Converts an integer to four bytes
279      * @param n - the integer
280      * @return - four bytes in an array
281      */

282     public static byte[] toBytes(int n) {
283         byte[] b = new byte[4];
284         b[3] = (byte) (n);
285         n >>>= 8;
286         b[2] = (byte) (n);
287         n >>>= 8;
288         b[1] = (byte) (n);
289         n >>>= 8;
290         b[0] = (byte) (n);
291         return b;
292     }
293
294     /**
295      * Converts an long to eight bytes
296      * @param n - the long
297      * @return - eight bytes in an array
298      */

299     public static byte[] toBytes(long n) {
300         byte[] b = new byte[8];
301         b[7] = (byte) (n);
302         n >>>= 8;
303         b[6] = (byte) (n);
304         n >>>= 8;
305         b[5] = (byte) (n);
306         n >>>= 8;
307         b[4] = (byte) (n);
308         n >>>= 8;
309         b[3] = (byte) (n);
310         n >>>= 8;
311         b[2] = (byte) (n);
312         n >>>= 8;
313         b[1] = (byte) (n);
314         n >>>= 8;
315         b[0] = (byte) (n);
316         return b;
317     }
318
319     /**
320      * Similar to a String.IndexOf, but uses pure bytes
321      * @param src - the source bytes to be searched
322      * @param srcOff - offset on the source buffer
323      * @param find - the string to be found within src
324      * @return - the index of the first matching byte. -1 if the find array is not found
325      */

326     public static int firstIndexOf(byte[] src, int srcOff, byte[] find){
327         int result = -1;
328         if (find.length > src.length) return result;
329         if (find.length == 0 || src.length == 0) return result;
330         if (srcOff >= src.length ) throw new java.lang.ArrayIndexOutOfBoundsException JavaDoc();
331         boolean found = false;
332         int srclen = src.length;
333         int findlen = find.length;
334         byte first = find[0];
335         int pos = srcOff;
336         while (!found) {
337             //find the first byte
338
while (pos < srclen){
339                 if (first == src[pos])
340                     break;
341                 pos++;
342             }
343             if (pos >= srclen)
344                 return -1;
345
346             //we found the first character
347
//match the rest of the bytes - they have to match
348
if ( (srclen - pos) < findlen)
349                 return -1;
350             //assume it does exist
351
found = true;
352             for (int i = 1; ( (i < findlen) && found); i++)
353                 found = found && (find[i] == src[pos + i]);
354             if (found)
355                 result = pos;
356             else if ( (srclen - pos) < findlen)
357                 return -1; //no more matches possible
358
else
359                 pos++;
360         }
361         return result;
362     }
363
364     /**
365      * Creates a complete data package
366      * @param indata - the message data to be contained within the package
367      * @param compress - compress message data or not
368      * @return - a full package (header,size,data,footer)
369      */

370     public static byte[] createDataPackage(byte[] indata, boolean compress)
371             throws java.io.IOException JavaDoc {
372         byte[] data;
373         if (compress) {
374             ByteArrayOutputStream JavaDoc bout = new ByteArrayOutputStream JavaDoc(
375                     indata.length / 2);
376             GZIPOutputStream JavaDoc gout = new GZIPOutputStream JavaDoc(bout);
377             gout.write(indata);
378             gout.flush();
379             gout.close();
380             data = bout.toByteArray();
381         } else {
382             data = indata;
383         }
384         byte[] result = new byte[START_DATA.length + 4 + data.length
385                 + END_DATA.length];
386         System.arraycopy(START_DATA, 0, result, 0, START_DATA.length);
387         System.arraycopy(toBytes(data.length), 0, result, START_DATA.length, 4);
388         System.arraycopy(data, 0, result, START_DATA.length + 4, data.length);
389         System.arraycopy(END_DATA, 0, result, START_DATA.length + 4
390                 + data.length, END_DATA.length);
391
392         return result;
393
394     }
395
396     // FIXME: extract this to test code!
397
public static void main(String JavaDoc[] args) throws Exception JavaDoc {
398        log.info("Before="+Integer.MAX_VALUE);
399        byte[] d = toBytes(Integer.MAX_VALUE);
400        log.info("After="+toInt(d,0));
401
402
403        log.info("Before="+Long.MAX_VALUE);
404        d = toBytes(Long.MAX_VALUE);
405        log.info("After="+toLong(d,0));
406
407        log.info("Before=" + 4564564);
408        d = toBytes((long)4564564);
409        log.info("After=" + toLong(d, 0));
410
411        byte[] d1 = createDataPackage(new byte[] {1},true);
412        byte[] d2 = createDataPackage(new byte[] {2},true);
413        byte[] d3 = createDataPackage(new byte[] {3},true);
414        byte[] test = new byte[d1.length+d2.length+d3.length+5];
415        System.arraycopy(d1,0,test,0,d1.length);
416        System.arraycopy(d2,0,test,d1.length,d2.length);
417        System.arraycopy(d3,0,test,d2.length+d1.length,d3.length);
418        printBuf(d1);
419        printBuf(d2);
420        printBuf(d3);
421        printBuf(test);
422        XByteBuffer b = new XByteBuffer();
423        b.append(test,0,test.length);
424        int s = b.countPackages();
425        log.info("Nr of packages="+s);
426        while ( s > 0 ) {
427            d = b.extractPackage(true);
428            log.info("Package d1=");
429            printBuf(d);
430            s--;
431        }//while
432

433     }
434
435     public static void printBuf(byte[] b) {
436         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
437         for ( int i=0; i<b.length; i++ ) {
438             buf.append(b[i] + " ");
439         }
440         log.info(buf);
441     }
442
443 }
444
Popular Tags