Java – Best way to handle a DataInputStream.readFully method throwing an EOFException in the code

exception-handlingjava

I am working with the DataInputStream.readFully method and trying to figure out the best way to handle the EOFException that is executed in this method. I am getting this log entry and could use some help trying to find out what the problem is as this exception does not appear to be handled and my problem is stuck in a loop with this same error. Any help/direction would be appreciated. Thank you.

    TRACE |     AccSysFNBBT-Socket-1-ReceiveQueuer-0 |                com.sharpbancsystems.ATM.channel.ActiveSocket |   528 |2013-11-22 11:10:02,311| - Received 2 bytes: 
S
  INFO |     AccSysFNBBT-Socket-1-ReceiveQueuer-0 |                com.sharpbancsystems.ATM.channel.ActiveSocket |   455 |2013-11-22 11:10:02,312| - Received message, length is 2643
  INFO |     AccSysFNBBT-Socket-1-ReceiveQueuer-1 |                com.sharpbancsystems.ATM.channel.ActiveSocket |   453 |2013-11-22 11:10:03,315| - Waiting for new message.
  WARN |     AccSysFNBBT-Socket-1-ReceiveQueuer-0 |                com.sharpbancsystems.ATM.channel.ActiveSocket |   483 |2013-11-22 11:10:03,315| - 
java.io.EOFException
    java.io.DataInputStream.readFully(Unknown Source)
    com.sharpbancsystems.ATM.channel.ActiveSocket.blockUntilGetMessageBytes(ActiveSocket.java:462)
    com.sharpbancsystems.ATM.channel.ActiveSocket.blockUntilReceiveMessage(ActiveSocket.java:266)
    com.sharpbancsystems.ATM.channel.ReceiveQueuer.run(ReceiveQueuer.java:113)
    java.lang.Thread.run(Unknown Source)

Here is my code where this is occurring:

private byte[] blockUntilGetMessageBytes(final Request request, final ProcessingTimer timer) throws ChannelException {

    byte[] data = null;
    int len;        
    try {
        // have to synchronize on the dataInputStream because the class was designed to have more than one ReceiveQueuer using this method
        synchronized (dataInputStream) {
            LOGGER.info("Waiting for new message.");
            len = blockUntilGetMessageLength();
            LOGGER.info("Received message, length is " + len);

            if (!owningChannel.shouldTerminate) {
                if (len > 0 && len <= 15000) {
                    request.setTimeStampDataFieldAsTimestamp(RequestConstants.SaveDataField.FromStartReceiveTMS);
                    timer.start();
                    data = new byte[len];
                    dataInputStream.readFully(data, 0, len);

                    LOGGER.trace("Full message: " + new String(data));
                    request.setTimeStampDataFieldAsTimestamp(RequestConstants.SaveDataField.FromFinishReceiveTMS);
                    timer.finish();
                    request.setNanosecondDataField(RequestConstants.SaveDataField.FromReceiveNS, timer.nanos());
                    if (channelStats != null) {
                        channelStats.addBytesReceived(data.length + 2); // include the 2 bytes for the length indicator
                        channelStats.increaseReceivedMsgs();
                    }
                } else if (len == 0) {
                    // Should probably log it, but continue
                    LOGGER.info("Empty message received.");
                    throw new ChannelException(getBankID(), "Empty message received");
                } else {
                    throw new ISOException("Invalid receive length: " + len + ".");
                }
            }
        }
        return data;
    } catch (final Exception ex) {
        LOGGER.warn(FormatData.fullStackTrace(ex));
        throw new ChannelException(getChannelID().getNumericId(), this.getClass().getSimpleName().concat(".getMessageBytes()"), ex);
    }
}

protected int blockUntilGetMessageLength() {
    final byte[] b = new byte[2];
    try {
        dataInputStream.readFully(b, 0, 2);
    } catch (final EOFException ex){
        // ***** 20131022 MS - Per Shannon add getNeedReconnect() here. *****
        getNeedReconnect().set(true);
        socket.isConnected();
        // ***** 20131022 MS - Per Shannon this means there was a premature EOF. *****
        if (this.shouldTerminate) {
            return 0;
        } else {
            synchronized (socket) {
                socket.notify();
            }
            LOGGER.warn(FormatData.formatStack(ex));
        }
    } catch (final IOException ex) {
        // ***** 20131022 MS - Per Shannon add getNeedReconnect() here. *****
        getNeedReconnect().set(true);
        socket.isConnected();
        if (this.shouldTerminate) {
            return 0;
        } else {
            synchronized (socket) {
                socket.notify();
            }
            LOGGER.warn(FormatData.formatStack(ex));
            return 0;
        }
    }
    LOGGER.trace("Received 2 bytes: " + new String(b));
    return ((((b[0]) & BYTEMASK) << BYTESHIFT) | ((b[1]) & BYTEMASK));
}

Best Solution

You get the exception, because len is greater than the number of bytes you can read from the stream. So apparently blockUntilGetMessageLength(); returns a bad value.

Without knowing how you compute len, it's not possible to tell you how to really fix this. But in any case, you could simply do something like this:

public final int readFully(InputStream in, byte b[], int off, int len) throws IOException {
    if (len < 0)
        throw new IndexOutOfBoundsException();
    int n = 0;
    while (n < len) {
        int count = in.read(b, off + n, len - n);
        if (count < 0)
            break;
        n += count;
    }
    return n;
}

The method would return the number of bytes really read.