All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.acplt.oncrpc.XdrTcpDecodingStream Maven / Gradle / Ivy

There is a newer version: 1.1.6
Show newest version
/*
 * $Header: /home/harald/repos/remotetea.sf.net/remotetea/src/org/acplt/oncrpc/XdrTcpDecodingStream.java,v 1.2 2005/11/11 21:07:27 haraldalbrecht Exp $
 *
 * Copyright (c) 1999, 2000
 * Lehrstuhl fuer Prozessleittechnik (PLT), RWTH Aachen
 * D-52064 Aachen, Germany.
 * All rights reserved.
 *
 * This library is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Library General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * 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 Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this program (see the file LICENSE.txt for more
 * details); if not, write to the Free Software Foundation, Inc.,
 * 675 Mass Ave, Cambridge, MA 02139, USA.
 */

package org.acplt.oncrpc;

import java.io.*;
import java.net.*;

/**
 * The XdrTcpDecodingStream class provides the necessary
 * functionality to {@link XdrDecodingStream} to receive XDR records from the
 * network using the stream-oriented TCP/IP.
 *
 * @version $Revision: 1.2 $ $Date: 2005/11/11 21:07:27 $ $State: Exp $ $Locker:  $
 * @author Harald Albrecht
 */
public class XdrTcpDecodingStream extends XdrDecodingStream {

    /**
     * Construct a new XdrTcpDecodingStream object and associate
     * it with the given streamingSocket for TCP/IP-based
     * communication.
     *
     * @param streamingSocket Socket from which XDR data is received.
     * @param bufferSize Size of packet buffer for storing received XDR
     *   data.
     *   
     * @throws IOException if the passed socket instance fails to provide an input stream.
     */
    public XdrTcpDecodingStream(Socket streamingSocket,
                                int bufferSize)
           throws IOException {
        socket = streamingSocket;
        stream = socket.getInputStream();
        //
        // If the given buffer size is too small, start with a more sensible
        // size. Next, if bufferSize is not a multiple of four, round it up to
        // the next multiple of four.
        //
        if ( bufferSize < 1024 ) {
            bufferSize = 1024;
        }
        if ( (bufferSize & 3) != 0 ) {
            bufferSize = (bufferSize + 4) & ~3;
        }
        //
        // Set up the buffer and the buffer pointers (no, this is still
        // Java).
        //
        buffer = new byte[bufferSize];
        bufferIndex = 0;
        bufferHighmark = -4;
        lastFragment = false;
        fragmentLength = 0;
    }

     /**
     * Returns the Internet address of the sender of the current XDR data.
     * This method should only be called after {@link #beginDecoding}, otherwise
     * it might return stale information.
     *
     * @return InetAddress of the sender of the current XDR data.
     */
    public InetAddress getSenderAddress() {
        return socket.getInetAddress();
    }

    /**
     * Returns the port number of the sender of the current XDR data.
     * This method should only be called after {@link #beginDecoding}, otherwise
     * it might return stale information.
     *
     * @return Port number of the sender of the current XDR data.
     */
    public int getSenderPort() {
        return socket.getPort();
    }

    /**
     * Initiates decoding of the next XDR record. For TCP-based XDR decoding
     * streams this reads in the next chunk of data from the network socket
     * (a chunk of data is not necessary the same as a fragment, just enough
     * to fill the internal buffer or receive the remaining part of a fragment).
     *
     * 

Read in the next bunch of bytes. This can be either a complete fragment, * or if the fragments sent by the communication partner are too large for * our buffer, only parts of fragments. In every case, this method ensures * that there will be more data available in the buffer (or else an * exception thrown). * * @throws OncRpcException if an ONC/RPC error occurs. * @throws IOException if an I/O error occurs. */ public void beginDecoding() throws OncRpcException, IOException { fill(); } /** * Read into buffer exactly the amound of bytes specified. * * @param stream Input stream to read byte data from. * @param bytes buffer receiving data. * @param bytesToRead number of bytes to read into buffer. * * @throws OncRpcException if EOF is reached before all bytes could * be read. * @throws IOException if an I/O error occurs. */ private void readBuffer(InputStream stream, byte [] bytes, int bytesToRead) throws IOException, OncRpcException { int bytesRead; int byteOffset = 0; while ( bytesToRead > 0 ) { bytesRead = stream.read(bytes, byteOffset, bytesToRead); if ( bytesRead <= 0 ) { // // Stream is at EOF -- note that bytesRead is not allowed // to be zero here, as we asked for at least one byte... // throw(new OncRpcException(OncRpcException.RPC_CANTRECV)); } bytesToRead -= bytesRead; byteOffset += bytesRead; } } /** * Fills the internal buffer with the next chunk of data. The chunk is * either as long as the buffer or as long as the remaining part of the * current XDR fragment, whichever is smaller. * *

This method does not accept empty XDR record fragments with the * only exception of a final trailing empty fragment. This special case * is accepted as some ONC/RPC implementations emit such trailing * empty fragments whenever the encoded data is a full multiple of their * internal record buffer size. * * @throws OncRpcException if an ONC/RPC error occurs. * @throws IOException if an I/O error occurs. */ private void fill() throws OncRpcException, IOException { // // If the buffer is empty but there are still bytes left to read, // refill the buffer. We have also to take care of the record marking // within the stream. // // Remember that lastFragment is reset by the endDecoding() method. // This once used to be a while loop, but it has been dropped since // we do not accept empty records any more -- with the only exception // being a final trailing empty XDR record. // // Did we already read in all data belonging to the current XDR // record, or are there bytes left to be read? // if ( fragmentLength <= 0 ) { if ( lastFragment ) { // // In case there is no more data in the current XDR record // (as we already saw the last fragment), throw an exception. // throw(new OncRpcException(OncRpcException.RPC_BUFFERUNDERFLOW)); } // // First read in the header of the next fragment. // byte bytes[] = new byte[4]; readBuffer(stream, bytes, 4); // // Watch the sign bit! // fragmentLength = bytes[0] & 0xFF; fragmentLength = (fragmentLength << 8) + (bytes[1] & 0xFF); fragmentLength = (fragmentLength << 8) + (bytes[2] & 0xFF); fragmentLength = (fragmentLength << 8) + (bytes[3] & 0xFF); if ( (fragmentLength & 0x80000000) != 0 ) { fragmentLength &= 0x7FFFFFFF; lastFragment = true; } else { lastFragment = false; } // // Sanity check on incomming fragment length: the length must // be at least four bytes long, otherwise this fragment does // not make sense. There are ONC/RPC implementations that send // empty trailing fragments, so we accept them here. // Also check for fragment lengths which are not a multiple of // four -- and thus are invalid. // if ( (fragmentLength & 3) != 0 ) { throw(new IOException("ONC/RPC XDR fragment length is not a multiple of four")); } if ( (fragmentLength == 0) && !lastFragment ) { throw(new IOException("empty ONC/RPC XDR fragment which is not a trailing fragment")); } } // // When the reach this stage, there is (still) data to be read for the // current XDR record *fragment*. // // Now read in the next buffer. Depending on how much bytes are still // to read within this frame, we either fill the buffer not completely // (with still some bytes to read in from the next round) or // completely. // bufferIndex = 0; if ( fragmentLength < buffer.length ) { readBuffer(stream, buffer, fragmentLength); bufferHighmark = fragmentLength - 4; fragmentLength = 0; } else { readBuffer(stream, buffer, buffer.length); bufferHighmark = buffer.length - 4; fragmentLength -= buffer.length; } } /** * End decoding of the current XDR record. The general contract of * endDecoding is that calling it is an indication that * the current record is no more interesting to the caller and any * allocated data for this record can be freed. * *

This method overrides {@link XdrDecodingStream#endDecoding}. It reads in * and throws away fragments until it reaches the last fragment. * * @throws OncRpcException if an ONC/RPC error occurs. * @throws IOException if an I/O error occurs. */ public void endDecoding() throws OncRpcException, IOException { try { // // Drain the stream until we reach the end of the current record. // while ( !lastFragment || (fragmentLength != 0) ) { fill(); } } finally { // // Try to reach a sane state, although this is rather questionable // in case of timeouts in the middle of a record. // bufferIndex = 0; bufferHighmark = -4; lastFragment = false; fragmentLength = 0; } } /** * Closes this decoding XDR stream and releases any system resources * associated with this stream. A closed XDR stream cannot perform decoding * operations and cannot be reopened. * *

This implementation frees the allocated buffer but does not close * the associated datagram socket. It only throws away the reference to * this socket. * * @throws OncRpcException if an ONC/RPC error occurs. * @throws IOException if an I/O error occurs. */ public void close() throws OncRpcException, IOException { buffer = null; stream = null; socket = null; } /** * Decodes (aka "deserializes") a "XDR int" value received from a * XDR stream. A XDR int is 32 bits wide -- the same width Java's "int" * data type has. * * @return The decoded int value. * * @throws OncRpcException if an ONC/RPC error occurs. * @throws IOException if an I/O error occurs. */ public int xdrDecodeInt() throws OncRpcException, IOException { // // This might look funny in the first place, but this way we can // properly handle trailing empty XDR record fragments. In this // case fill() will return without any now data the first time // and on the second time a buffer underflow exception is thrown. // while ( bufferIndex > bufferHighmark ) { fill(); } // // There's enough space in the buffer to hold at least one // XDR int. So let's retrieve it now. // Note: buffer[...] gives a byte, which is signed. So if we // add it to the value (which is int), it has to be widened // to 32 bit, so its sign is propagated. To avoid this sign // madness, we have to "and" it with 0xFF, so all unwanted // bits are cut off after sign extension. Sigh. // int value = buffer[bufferIndex++] & 0xFF; value = (value << 8) + (buffer[bufferIndex++] & 0xFF); value = (value << 8) + (buffer[bufferIndex++] & 0xFF); value = (value << 8) + (buffer[bufferIndex++] & 0xFF); return value; } /** * Decodes (aka "deserializes") an opaque value, which is nothing more * than a series of octets (or 8 bits wide bytes). Because the length * of the opaque value is given, we don't need to retrieve it from the * XDR stream. This is different from * {@link #xdrDecodeOpaque(byte[], int, int)} where * first the length of the opaque value is retrieved from the XDR stream. * * @param length Length of opaque data to decode. * * @return Opaque data as a byte vector. * * @throws OncRpcException if an ONC/RPC error occurs. * @throws IOException if an I/O error occurs. */ public byte [] xdrDecodeOpaque(int length) throws OncRpcException, IOException { int padding = (4 - (length & 3)) & 3; int offset = 0; // current offset into bytes vector int toCopy; // // Now allocate enough memory to hold the data to be retrieved and // get part after part from the buffer. // byte [] bytes = new byte[length]; // // As for the while loop, see the comment in xdrDecodeInt(). // while ( bufferIndex > bufferHighmark ) { fill(); } while ( length > 0 ) { toCopy = bufferHighmark - bufferIndex + 4; if ( toCopy >= length ) { // // The buffer holds more data than we need. So copy the bytes // and leave the stage. // System.arraycopy(buffer, bufferIndex, bytes, offset, length); bufferIndex += length; // No need to adjust "offset", because this is the last round. break; } else { // // We need to copy more data than currently available from our // buffer, so we copy all we can get our hands on, then fill // the buffer again and repeat this until we got all we want. // System.arraycopy(buffer, bufferIndex, bytes, offset, toCopy); bufferIndex += toCopy; offset += toCopy; length -= toCopy; // NB: no problems with trailing empty fragments, so we skip // the while loop here. fill(); } } bufferIndex += padding; return bytes; } /** * Decodes (aka "deserializes") a XDR opaque value, which is represented * by a vector of byte values, and starts at offset with a * length of length. Only the opaque value is decoded, so the * caller has to know how long the opaque value will be. The decoded data * is always padded to be a multiple of four (because that's what the * sender does). * * @param opaque Byte vector which will receive the decoded opaque value. * @param offset Start offset in the byte vector. * @param length the number of bytes to decode. * * @throws OncRpcException if an ONC/RPC error occurs. * @throws IOException if an I/O error occurs. */ public void xdrDecodeOpaque(byte [] opaque, int offset, int length) throws OncRpcException, IOException { int padding = (4 - (length & 3)) & 3; int toCopy; // // Now get part after part and fill the byte vector. // if ( bufferIndex > bufferHighmark ) { fill(); } while ( length > 0 ) { toCopy = bufferHighmark - bufferIndex + 4; if ( toCopy >= length ) { // // The buffer holds more data than we need. So copy the bytes // and leave the stage. // System.arraycopy(buffer, bufferIndex, opaque, offset, length); bufferIndex += length; // No need to adjust "offset", because this is the last round. break; } else { // // We need to copy more data than currently available from our // buffer, so we copy all we can get our hands on, then fill // the buffer again and repeat this until we got all we want. // System.arraycopy(buffer, bufferIndex, opaque, offset, toCopy); bufferIndex += toCopy; offset += toCopy; length -= toCopy; fill(); } } bufferIndex += padding; } /** * The streaming socket to be used when receiving this XDR stream's * buffer contents. */ private Socket socket; /** * The input stream used to pull the bytes off the network. */ InputStream stream; /** * The buffer which will be filled from the datagram socket and then * be used to supply the information when decoding data. */ private byte [] buffer; /** * The read pointer is an index into the buffer. */ private int bufferIndex; /** * Index of the last four byte word in the buffer, which has been read * in from the datagram socket. */ private int bufferHighmark; /** * Remaining number of bytes in this fragment -- and still to read. */ private int fragmentLength; /** * Flag indicating that we've read the last fragment and thus reached * the end of the record. */ private boolean lastFragment; } // End of XdrTcpDecodingStream





© 2015 - 2024 Weber Informatics LLC | Privacy Policy