init
This commit is contained in:
596
java/org/apache/catalina/tribes/io/XByteBuffer.java
Normal file
596
java/org/apache/catalina/tribes/io/XByteBuffer.java
Normal file
@@ -0,0 +1,596 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.catalina.tribes.io;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.apache.catalina.tribes.util.StringManager;
|
||||
import org.apache.juli.logging.Log;
|
||||
import org.apache.juli.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* The XByteBuffer provides a dual functionality.
|
||||
* One, it stores message bytes and automatically extends the byte buffer if needed.<BR>
|
||||
* Two, it can encode and decode packages so that they can be defined and identified
|
||||
* as they come in on a socket.
|
||||
* <br>
|
||||
* <b>THIS CLASS IS NOT THREAD SAFE</B><BR>
|
||||
* <br>
|
||||
* Transfer package:
|
||||
* <ul>
|
||||
* <li><b>START_DATA</b>- 7 bytes - <i>FLT2002</i></li>
|
||||
* <li><b>SIZE</b> - 4 bytes - size of the data package</li>
|
||||
* <li><b>DATA</b> - should be as many bytes as the prev SIZE</li>
|
||||
* <li><b>END_DATA</b> - 7 bytes - <i>TLF2003</i></li>
|
||||
* </ul>
|
||||
*/
|
||||
public class XByteBuffer implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final Log log = LogFactory.getLog(XByteBuffer.class);
|
||||
protected static final StringManager sm = StringManager.getManager(XByteBuffer.class);
|
||||
|
||||
/**
|
||||
* This is a package header, 7 bytes (FLT2002)
|
||||
*/
|
||||
private static final byte[] START_DATA = {70,76,84,50,48,48,50};
|
||||
|
||||
/**
|
||||
* This is the package footer, 7 bytes (TLF2003)
|
||||
*/
|
||||
private static final byte[] END_DATA = {84,76,70,50,48,48,51};
|
||||
|
||||
/**
|
||||
* Variable to hold the data
|
||||
*/
|
||||
protected byte[] buf = null;
|
||||
|
||||
/**
|
||||
* Current length of data in the buffer
|
||||
*/
|
||||
protected int bufSize = 0;
|
||||
|
||||
/**
|
||||
* Flag for discarding invalid packages
|
||||
* If this flag is set to true, and append(byte[],...) is called,
|
||||
* the data added will be inspected, and if it doesn't start with
|
||||
* <code>START_DATA</code> it will be thrown away.
|
||||
*
|
||||
*/
|
||||
protected boolean discard = true;
|
||||
|
||||
/**
|
||||
* Constructs a new XByteBuffer.<br>
|
||||
* TODO use a pool of byte[] for performance
|
||||
* @param size the initial size of the byte buffer
|
||||
* @param discard Flag for discarding invalid packages
|
||||
*/
|
||||
public XByteBuffer(int size, boolean discard) {
|
||||
buf = new byte[size];
|
||||
this.discard = discard;
|
||||
}
|
||||
|
||||
public XByteBuffer(byte[] data,boolean discard) {
|
||||
this(data,data.length+128,discard);
|
||||
}
|
||||
|
||||
public XByteBuffer(byte[] data, int size,boolean discard) {
|
||||
int length = Math.max(data.length,size);
|
||||
buf = new byte[length];
|
||||
System.arraycopy(data,0,buf,0,data.length);
|
||||
bufSize = data.length;
|
||||
this.discard = discard;
|
||||
}
|
||||
|
||||
public int getLength() {
|
||||
return bufSize;
|
||||
}
|
||||
|
||||
public void setLength(int size) {
|
||||
if ( size > buf.length ) throw new ArrayIndexOutOfBoundsException(sm.getString("xByteBuffer.size.larger.buffer"));
|
||||
bufSize = size;
|
||||
}
|
||||
|
||||
public void trim(int length) {
|
||||
if ( (bufSize - length) < 0 )
|
||||
throw new ArrayIndexOutOfBoundsException(sm.getString("xByteBuffer.unableTrim",
|
||||
Integer.toString(bufSize), Integer.toString(length)));
|
||||
bufSize -= length;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
bufSize = 0;
|
||||
}
|
||||
|
||||
public byte[] getBytesDirect() {
|
||||
return this.buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the bytes in the buffer, in its exact length
|
||||
*/
|
||||
public byte[] getBytes() {
|
||||
byte[] b = new byte[bufSize];
|
||||
System.arraycopy(buf,0,b,0,bufSize);
|
||||
return b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the buffer
|
||||
*/
|
||||
public void clear() {
|
||||
bufSize = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the data to the buffer. If the data is incorrectly formatted, ie, the data should always start with the
|
||||
* header, false will be returned and the data will be discarded.
|
||||
* @param b - bytes to be appended
|
||||
* @param len - the number of bytes to append.
|
||||
* @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
|
||||
*/
|
||||
public boolean append(ByteBuffer b, int len) {
|
||||
int newcount = bufSize + len;
|
||||
if (newcount > buf.length) {
|
||||
expand(newcount);
|
||||
}
|
||||
b.get(buf,bufSize,len);
|
||||
|
||||
bufSize = newcount;
|
||||
|
||||
if ( discard ) {
|
||||
if (bufSize > START_DATA.length && (firstIndexOf(buf, 0, START_DATA) == -1)) {
|
||||
bufSize = 0;
|
||||
log.error(sm.getString("xByteBuffer.discarded.invalidHeader"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
public boolean append(byte i) {
|
||||
int newcount = bufSize + 1;
|
||||
if (newcount > buf.length) {
|
||||
expand(newcount);
|
||||
}
|
||||
buf[bufSize] = i;
|
||||
bufSize = newcount;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public boolean append(boolean i) {
|
||||
int newcount = bufSize + 1;
|
||||
if (newcount > buf.length) {
|
||||
expand(newcount);
|
||||
}
|
||||
XByteBuffer.toBytes(i,buf,bufSize);
|
||||
bufSize = newcount;
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean append(long i) {
|
||||
int newcount = bufSize + 8;
|
||||
if (newcount > buf.length) {
|
||||
expand(newcount);
|
||||
}
|
||||
XByteBuffer.toBytes(i,buf,bufSize);
|
||||
bufSize = newcount;
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean append(int i) {
|
||||
int newcount = bufSize + 4;
|
||||
if (newcount > buf.length) {
|
||||
expand(newcount);
|
||||
}
|
||||
XByteBuffer.toBytes(i,buf,bufSize);
|
||||
bufSize = newcount;
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean append(byte[] b, int off, int len) {
|
||||
if ((off < 0) || (off > b.length) || (len < 0) ||
|
||||
((off + len) > b.length) || ((off + len) < 0)) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
} else if (len == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int newcount = bufSize + len;
|
||||
if (newcount > buf.length) {
|
||||
expand(newcount);
|
||||
}
|
||||
System.arraycopy(b, off, buf, bufSize, len);
|
||||
bufSize = newcount;
|
||||
|
||||
if ( discard ) {
|
||||
if (bufSize > START_DATA.length && (firstIndexOf(buf, 0, START_DATA) == -1)) {
|
||||
bufSize = 0;
|
||||
log.error(sm.getString("xByteBuffer.discarded.invalidHeader"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void expand(int newcount) {
|
||||
//don't change the allocation strategy
|
||||
byte newbuf[] = new byte[Math.max(buf.length << 1, newcount)];
|
||||
System.arraycopy(buf, 0, newbuf, 0, bufSize);
|
||||
buf = newbuf;
|
||||
}
|
||||
|
||||
public int getCapacity() {
|
||||
return buf.length;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Internal mechanism to make a check if a complete package exists
|
||||
* within the buffer
|
||||
* @return - true if a complete package (header,compress,size,data,footer) exists within the buffer
|
||||
*/
|
||||
public int countPackages() {
|
||||
return countPackages(false);
|
||||
}
|
||||
|
||||
public int countPackages(boolean first)
|
||||
{
|
||||
int cnt = 0;
|
||||
int pos = START_DATA.length;
|
||||
int start = 0;
|
||||
|
||||
while ( start < bufSize ) {
|
||||
//first check start header
|
||||
int index = XByteBuffer.firstIndexOf(buf,start,START_DATA);
|
||||
//if the header (START_DATA) isn't the first thing or
|
||||
//the buffer isn't even 14 bytes
|
||||
if ( index != start || ((bufSize-start)<14) ) break;
|
||||
//next 4 bytes are compress flag not needed for count packages
|
||||
//then get the size 4 bytes
|
||||
int size = toInt(buf, pos);
|
||||
//now the total buffer has to be long enough to hold
|
||||
//START_DATA.length+4+size+END_DATA.length
|
||||
pos = start + START_DATA.length + 4 + size;
|
||||
if ( (pos + END_DATA.length) > bufSize) break;
|
||||
//and finally check the footer of the package END_DATA
|
||||
int newpos = firstIndexOf(buf, pos, END_DATA);
|
||||
//mismatch, there is no package
|
||||
if (newpos != pos) break;
|
||||
//increase the packet count
|
||||
cnt++;
|
||||
//reset the values
|
||||
start = pos + END_DATA.length;
|
||||
pos = start + START_DATA.length;
|
||||
//we only want to verify that we have at least one package
|
||||
if ( first ) break;
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to check if a package exists in this byte buffer.
|
||||
* @return - true if a complete package (header,options,size,data,footer) exists within the buffer
|
||||
*/
|
||||
public boolean doesPackageExist() {
|
||||
return (countPackages(true)>0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the message bytes from a package.
|
||||
* If no package exists, a IllegalStateException will be thrown.
|
||||
* @param clearFromBuffer - if true, the package will be removed from the byte buffer
|
||||
* @return - returns the actual message bytes (header, compress,size and footer not included).
|
||||
*/
|
||||
public XByteBuffer extractDataPackage(boolean clearFromBuffer) {
|
||||
int psize = countPackages(true);
|
||||
if (psize == 0) {
|
||||
throw new java.lang.IllegalStateException(sm.getString("xByteBuffer.no.package"));
|
||||
}
|
||||
int size = toInt(buf, START_DATA.length);
|
||||
XByteBuffer xbuf = BufferPool.getBufferPool().getBuffer(size,false);
|
||||
xbuf.setLength(size);
|
||||
System.arraycopy(buf, START_DATA.length + 4, xbuf.getBytesDirect(), 0, size);
|
||||
if (clearFromBuffer) {
|
||||
int totalsize = START_DATA.length + 4 + size + END_DATA.length;
|
||||
bufSize = bufSize - totalsize;
|
||||
System.arraycopy(buf, totalsize, buf, 0, bufSize);
|
||||
}
|
||||
return xbuf;
|
||||
|
||||
}
|
||||
|
||||
public ChannelData extractPackage(boolean clearFromBuffer) {
|
||||
XByteBuffer xbuf = extractDataPackage(clearFromBuffer);
|
||||
ChannelData cdata = ChannelData.getDataFromPackage(xbuf);
|
||||
return cdata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a complete data package
|
||||
* @param cdata - the message data to be contained within the package
|
||||
* @return - a full package (header,size,data,footer)
|
||||
*/
|
||||
public static byte[] createDataPackage(ChannelData cdata) {
|
||||
// return createDataPackage(cdata.getDataPackage());
|
||||
//avoid one extra byte array creation
|
||||
int dlength = cdata.getDataPackageLength();
|
||||
int length = getDataPackageLength(dlength);
|
||||
byte[] data = new byte[length];
|
||||
int offset = 0;
|
||||
System.arraycopy(START_DATA, 0, data, offset, START_DATA.length);
|
||||
offset += START_DATA.length;
|
||||
toBytes(dlength,data, START_DATA.length);
|
||||
offset += 4;
|
||||
cdata.getDataPackage(data,offset);
|
||||
offset += dlength;
|
||||
System.arraycopy(END_DATA, 0, data, offset, END_DATA.length);
|
||||
offset += END_DATA.length;
|
||||
return data;
|
||||
}
|
||||
|
||||
public static byte[] createDataPackage(byte[] data, int doff, int dlength, byte[] buffer, int bufoff) {
|
||||
if ( (buffer.length-bufoff) > getDataPackageLength(dlength) ) {
|
||||
throw new ArrayIndexOutOfBoundsException(sm.getString("xByteBuffer.unableCreate"));
|
||||
}
|
||||
System.arraycopy(START_DATA, 0, buffer, bufoff, START_DATA.length);
|
||||
toBytes(data.length,buffer, bufoff+START_DATA.length);
|
||||
System.arraycopy(data, doff, buffer, bufoff+START_DATA.length + 4, dlength);
|
||||
System.arraycopy(END_DATA, 0, buffer, bufoff+START_DATA.length + 4 + data.length, END_DATA.length);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
||||
public static int getDataPackageLength(int datalength) {
|
||||
int length =
|
||||
START_DATA.length + //header length
|
||||
4 + //data length indicator
|
||||
datalength + //actual data length
|
||||
END_DATA.length; //footer length
|
||||
return length;
|
||||
|
||||
}
|
||||
|
||||
public static byte[] createDataPackage(byte[] data) {
|
||||
int length = getDataPackageLength(data.length);
|
||||
byte[] result = new byte[length];
|
||||
return createDataPackage(data,0,data.length,result,0);
|
||||
}
|
||||
|
||||
|
||||
// public static void fillDataPackage(byte[] data, int doff, int dlength, XByteBuffer buf) {
|
||||
// int pkglen = getDataPackageLength(dlength);
|
||||
// if ( buf.getCapacity() < pkglen ) buf.expand(pkglen);
|
||||
// createDataPackage(data,doff,dlength,buf.getBytesDirect(),buf.getLength());
|
||||
// }
|
||||
|
||||
/**
|
||||
* Convert four bytes to an int
|
||||
* @param b - the byte array containing the four bytes
|
||||
* @param off - the offset
|
||||
* @return the integer value constructed from the four bytes
|
||||
*/
|
||||
public static int toInt(byte[] b,int off){
|
||||
return ( ( b[off+3]) & 0xFF) +
|
||||
( ( ( b[off+2]) & 0xFF) << 8) +
|
||||
( ( ( b[off+1]) & 0xFF) << 16) +
|
||||
( ( ( b[off+0]) & 0xFF) << 24);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert eight bytes to a long
|
||||
* @param b - the byte array containing the four bytes
|
||||
* @param off - the offset
|
||||
* @return the long value constructed from the eight bytes
|
||||
*/
|
||||
public static long toLong(byte[] b,int off){
|
||||
return ( ( (long) b[off+7]) & 0xFF) +
|
||||
( ( ( (long) b[off+6]) & 0xFF) << 8) +
|
||||
( ( ( (long) b[off+5]) & 0xFF) << 16) +
|
||||
( ( ( (long) b[off+4]) & 0xFF) << 24) +
|
||||
( ( ( (long) b[off+3]) & 0xFF) << 32) +
|
||||
( ( ( (long) b[off+2]) & 0xFF) << 40) +
|
||||
( ( ( (long) b[off+1]) & 0xFF) << 48) +
|
||||
( ( ( (long) b[off+0]) & 0xFF) << 56);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts a boolean and put it in a byte array.
|
||||
* @param bool the integer
|
||||
* @param data the byte buffer in which the boolean will be placed
|
||||
* @param offset the offset in the byte array
|
||||
* @return the byte array
|
||||
*/
|
||||
public static byte[] toBytes(boolean bool, byte[] data, int offset) {
|
||||
data[offset] = (byte)(bool?1:0);
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a byte array entry to boolean.
|
||||
* @param b byte array
|
||||
* @param offset within byte array
|
||||
* @return true if byte array entry is non-zero, false otherwise
|
||||
*/
|
||||
public static boolean toBoolean(byte[] b, int offset) {
|
||||
return b[offset] != 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts an integer to four bytes.
|
||||
* @param n the integer
|
||||
* @param b the byte buffer in which the integer will be placed
|
||||
* @param offset the offset in the byte array
|
||||
* @return four bytes in an array
|
||||
*/
|
||||
public static byte[] toBytes(int n, byte[] b, int offset) {
|
||||
b[offset+3] = (byte) (n);
|
||||
n >>>= 8;
|
||||
b[offset+2] = (byte) (n);
|
||||
n >>>= 8;
|
||||
b[offset+1] = (byte) (n);
|
||||
n >>>= 8;
|
||||
b[offset+0] = (byte) (n);
|
||||
return b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a long to eight bytes.
|
||||
* @param n the long
|
||||
* @param b the byte buffer in which the integer will be placed
|
||||
* @param offset the offset in the byte array
|
||||
* @return eight bytes in an array
|
||||
*/
|
||||
public static byte[] toBytes(long n, byte[] b, int offset) {
|
||||
b[offset+7] = (byte) (n);
|
||||
n >>>= 8;
|
||||
b[offset+6] = (byte) (n);
|
||||
n >>>= 8;
|
||||
b[offset+5] = (byte) (n);
|
||||
n >>>= 8;
|
||||
b[offset+4] = (byte) (n);
|
||||
n >>>= 8;
|
||||
b[offset+3] = (byte) (n);
|
||||
n >>>= 8;
|
||||
b[offset+2] = (byte) (n);
|
||||
n >>>= 8;
|
||||
b[offset+1] = (byte) (n);
|
||||
n >>>= 8;
|
||||
b[offset+0] = (byte) (n);
|
||||
return b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to a String.IndexOf, but uses pure bytes.
|
||||
* @param src - the source bytes to be searched
|
||||
* @param srcOff - offset on the source buffer
|
||||
* @param find - the string to be found within src
|
||||
* @return - the index of the first matching byte. -1 if the find array is not found
|
||||
*/
|
||||
public static int firstIndexOf(byte[] src, int srcOff, byte[] find){
|
||||
int result = -1;
|
||||
if (find.length > src.length) return result;
|
||||
if (find.length == 0 || src.length == 0) return result;
|
||||
if (srcOff >= src.length ) throw new java.lang.ArrayIndexOutOfBoundsException();
|
||||
boolean found = false;
|
||||
int srclen = src.length;
|
||||
int findlen = find.length;
|
||||
byte first = find[0];
|
||||
int pos = srcOff;
|
||||
while (!found) {
|
||||
//find the first byte
|
||||
while (pos < srclen){
|
||||
if (first == src[pos])
|
||||
break;
|
||||
pos++;
|
||||
}
|
||||
if (pos >= srclen)
|
||||
return -1;
|
||||
|
||||
//we found the first character
|
||||
//match the rest of the bytes - they have to match
|
||||
if ( (srclen - pos) < findlen)
|
||||
return -1;
|
||||
//assume it does exist
|
||||
found = true;
|
||||
for (int i = 1; ( (i < findlen) && found); i++) {
|
||||
found = (find[i] == src[pos + i]);
|
||||
}
|
||||
if (found) {
|
||||
result = pos;
|
||||
} else if ( (srclen - pos) < findlen) {
|
||||
return -1; //no more matches possible
|
||||
} else {
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public static Serializable deserialize(byte[] data)
|
||||
throws IOException, ClassNotFoundException, ClassCastException {
|
||||
return deserialize(data,0,data.length);
|
||||
}
|
||||
|
||||
public static Serializable deserialize(byte[] data, int offset, int length)
|
||||
throws IOException, ClassNotFoundException, ClassCastException {
|
||||
return deserialize(data,offset,length,null);
|
||||
}
|
||||
|
||||
private static final AtomicInteger invokecount = new AtomicInteger(0);
|
||||
|
||||
public static Serializable deserialize(byte[] data, int offset, int length, ClassLoader[] cls)
|
||||
throws IOException, ClassNotFoundException, ClassCastException {
|
||||
invokecount.addAndGet(1);
|
||||
Object message = null;
|
||||
if ( cls == null ) cls = new ClassLoader[0];
|
||||
if (data != null && length > 0) {
|
||||
InputStream instream = new ByteArrayInputStream(data,offset,length);
|
||||
ObjectInputStream stream = null;
|
||||
stream = (cls.length>0)? new ReplicationStream(instream,cls):new ObjectInputStream(instream);
|
||||
message = stream.readObject();
|
||||
instream.close();
|
||||
stream.close();
|
||||
}
|
||||
if ( message == null ) {
|
||||
return null;
|
||||
} else if (message instanceof Serializable)
|
||||
return (Serializable) message;
|
||||
else {
|
||||
throw new ClassCastException(sm.getString("xByteBuffer.wrong.class", message.getClass().getName()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes a message into cluster data
|
||||
* @param msg ClusterMessage
|
||||
* @return serialized content as byte[] array
|
||||
* @throws IOException Serialization error
|
||||
*/
|
||||
public static byte[] serialize(Serializable msg) throws IOException {
|
||||
ByteArrayOutputStream outs = new ByteArrayOutputStream();
|
||||
ObjectOutputStream out = new ObjectOutputStream(outs);
|
||||
out.writeObject(msg);
|
||||
out.flush();
|
||||
byte[] data = outs.toByteArray();
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setDiscard(boolean discard) {
|
||||
this.discard = discard;
|
||||
}
|
||||
|
||||
public boolean getDiscard() {
|
||||
return discard;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user