/*
* $Id: LengthProtocol.java 19191 2010-08-25 21:05:23Z tcarlson $
* --------------------------------------------------------------------------------------
* Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com
*
* The software in this package is published under the terms of the CPAL v1.0
* license, a copy of which has been included with this distribution in the
* LICENSE.txt file.
*/
package org.mule.transport.tcp.protocols;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* The LengthProtocol is an application level tcp protocol that can be used to
* transfer large amounts of data without risking some data to be loss. The protocol
* is defined by sending / reading an integer (the packet length) and then the data
* to be transferred.
*
* <p>Note that use of this protocol must be symmetric - both the sending and receiving
* connectors must use the same protocol.</p>
*/
public class LengthProtocol extends DirectProtocol
{
private static final Log logger = LogFactory.getLog(LengthProtocol.class);
// TODO - can we not get this from the API somewhere?
private static final int SIZE_INT = 4;
public static final int NO_MAX_LENGTH = -1;
private int maxMessageLength;
public LengthProtocol()
{
this(NO_MAX_LENGTH);
}
public LengthProtocol(int maxMessageLength)
{
super(NO_STREAM, SIZE_INT);
this.setMaxMessageLength(maxMessageLength);
}
public Object read(InputStream is) throws IOException
{
// original comments indicated that we need to use read(byte[]) rather than readInt()
// to avoid socket timeouts - don't understand, but don't want to risk change.
// first read the data necessary to know the length of the payload
DataInputStream dis = new DataInputStream(is);
dis.mark(SIZE_INT);
// this pulls through SIZE_INT bytes
if (null == super.read(dis, SIZE_INT))
{
return null; // eof
}
// reset and read the integer
dis.reset();
int length = dis.readInt();
if (logger.isDebugEnabled())
{
logger.debug("length: " + length);
}
if (length < 0 || (getMaxMessageLength() > 0 && length > getMaxMessageLength()))
{
throw new IOException("Length " + length + " exceeds limit: " + getMaxMessageLength());
}
// finally read the rest of the data
byte[] buffer = new byte[length];
dis.readFully(buffer);
if (logger.isDebugEnabled())
{
logger.debug("length read: " + buffer.length);
}
return buffer;
}
@Override
protected void writeByteArray(OutputStream os, byte[] data) throws IOException
{
// Write the length and then the data.
DataOutputStream dos = new DataOutputStream(os);
dos.writeInt(data.length);
dos.write(data);
// DataOutputStream size is SIZE_INT + the byte length, due to the writeInt call
// this should fix EE-1494
if (dos.size() != data.length + SIZE_INT)
{
// only flush if the sizes don't match up
dos.flush();
}
}
/**
* Read all four bytes for initial integer (limit is set in read)
*
* @param len Amount transferred last call (-1 on EOF or socket error)
* @param available Amount available
* @return true if the transfer should continue
*/
@Override
protected boolean isRepeat(int len, int available)
{
return true;
}
public int getMaxMessageLength()
{
return maxMessageLength;
}
public void setMaxMessageLength(int maxMessageLength)
{
this.maxMessageLength = maxMessageLength;
}
}