/* Copyright (c) 2007 Health Market Science, Inc. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA You can contact Health Market Science at info@healthmarketscience.com or at the following address: Health Market Science 2700 Horizon Drive Suite 200 King of Prussia, PA 19406 */ package com.healthmarketscience.rmiio; import java.io.IOException; import java.io.InputStream; /** * Adds support for packet based access to data from an InputStream. Can be * more efficient for certain stream implementations, especially for remote * stream usage where data is coming across the wire in byte[] packets. * * @author James Ahlborn */ public abstract class PacketInputStream extends InputStream { /** target size of packets returned from packet related methods */ public static final int DEFAULT_PACKET_SIZE = 1024; /** empty packet. useful for returns from partial reads where no data is currently available, but it's not EOF. */ protected static final byte[] EMPTY_PACKET = new byte[0]; /** the packet size for buffers created in the overflow buffer list */ private final int _packetSize; /** whether or not packet reading should accept partial packets by default. allowing partial packet reads will generally cause more remote calls, but should reduce latency per-object */ private final boolean _noDelay; public PacketInputStream() { this(DEFAULT_PACKET_SIZE); } public PacketInputStream(int packetSize) { this(packetSize, false); } public PacketInputStream(int packetSize, boolean noDelay) { _packetSize = packetSize; _noDelay = noDelay; } public int getPacketSize() { return _packetSize; } public boolean getNoDelay() { return _noDelay; } /** * Gets the next "packet" from the internal buffer and returns it (if any). * By default, this method will block until a fully filled packet is created * (equivalent to calling <code>readPacket(false)</code>). If noDelay is * enabled, this method will allow partial packet reads (equivalent to * calling <code>readPacket(true)</code>). * * @return a fully filled array of byte's or <code>null</code> if the end of * stream has been reached */ public byte[] readPacket() throws IOException { return readPacket(_noDelay); } /** * Gets the next "packet" from the internal buffer and returns it (if any). * This method may block until a full packet is read, depending on the value * of readPartial. * * @param readPartial iff <code>false</code>, may block until a full packet * is read (or EOF), otherwise will return as much data * as is currently available (which may be 0). * @return a fully filled array of byte's or <code>null</code> if the end of * stream has been reached. if no data is available but EOF has not * been reached, the returned buffer will have length 0. */ public abstract byte[] readPacket(boolean readPartial) throws IOException; /** * Returns the number of full packets which can be read without blocking. */ public abstract int packetsAvailable() throws IOException; /** * Reads a packet of data from the given input stream. The given packet is * filled and returned if possible. If not enough bytes are available, a * new packet will be created and returned. If the stream is empty, {@code * null} will be returned. * * @param in the InputStream from which to read data * @param packet the potential output packet (if enough data is available) * @return a filled packet of data if any available, {@code null} if the * stream is empty */ public static byte[] readPacket(InputStream in, byte[] packet) throws IOException { int readLen = in.read(packet, 0, packet.length); if(readLen > 0) { if(readLen < packet.length) { // shrink buffer for output byte[] tmpPacket = new byte[readLen]; System.arraycopy(packet, 0, tmpPacket, 0, readLen); packet = tmpPacket; } return packet; } else if(readLen == 0) { return PacketInputStream.EMPTY_PACKET; } return null; } }