// Note that the read offset is still valid since the
// MarshalledObject extraction should always succeed.
// It's just that the RemoteEvent within it could not
// be reconstituted. Therefore, just skip to the next
// MarshalledObject in the stream.
public RemoteEventData[] readAhead(int maxEvents) 
        throws IOException, ClassNotFoundException 
    {
        boolean IOExceptionCaught = false;
        stateCheck();
	if (debugState) {
	    assertInvariants();
	}
        // Check if empty
	if (isEmpty()) 
	    throw new NoSuchElementException();    
        long readCount = rcount;
        long readPosition = rpos;
        long logNum = getLogNum(readCount); 
        File log = getLogFile(logNum);
        RemoteEvent evt = null;
        ArrayList rData = new ArrayList();
        int i = 0;
        LogInputStream in = null;
        boolean done = false;
        printControlData(persistenceLogger, "Before read::readAhead");
        if (persistenceLogger.isLoggable(Level.FINEST)) {
            persistenceLogger.log(Level.FINEST, 
                "EventLog::readAhead() maxEvents = {0}", 
                new Object[] {Integer.valueOf(maxEvents)});
        } 
        while ((readCount < wcount) && 
               (rData.size() < maxEvents) &&
               !done) 
        {
            if (logNum != getLogNum(readCount)) {
                logNum = getLogNum(readCount);
                log = getLogFile(logNum);
                readPosition = 0;
            }
            try {
                // get the input stream
                in = streamPool.getLogInputStream(log, readPosition);
                // read the event
                evt = eventReader.read(in); 
                // update readCount and readPosition
                readCount++;
                readPosition = in.getOffset(); // offset to next unread event
                //Generate new entry
                rData.add(new RemoteEventData(
                   evt, 
                   new RemoteEventDataCursor(readCount, readPosition)));
            } catch (IOException ie) {
                // We'll get interrupted when asked to shutdown.
                // In this case, we can skip the call to nextReadAheadLog.
                if (ie instanceof InterruptedIOException) {
                    if (persistenceLogger.isLoggable(Level.FINEST)) {
                        persistenceLogger.log(Level.FINEST, 
                            "EventLog::readAhead() interrupted ");
                    } 
                    // Stop processing events
                    done = true;
                } else {
                    if (persistenceLogger.isLoggable(Level.FINEST)) {
                        persistenceLogger.log(Level.FINEST, 
                            "EventLog::readAhead() received IOException " 
                            + "... skipping to next read log"); 
                    }
                    readCount = nextReadAheadLog(readCount);
                    // TODO - rcount = readCount;
                    // TODO - if bump rcount you need to bump wcount too
                    // TODO - if bump counts then you need to persist them
                    readPosition = 0;
                    IOExceptionCaught = true; 
                    if (persistenceLogger.isLoggable(Level.FINEST)) {
                        persistenceLogger.log(Level.FINEST, 
                            "EventLog::readAhead() new readCount is {0}", 
                            Long.valueOf(readCount)); 
                    }                    
                }
                if (persistenceLogger.isLoggable(Levels.HANDLED)) {
                        persistenceLogger.log(Levels.HANDLED, 
                            "Exception: ", ie);
                }
            } catch (ClassNotFoundException cnfe) {
                // Note that the read offset is still valid since the
                // MarshalledObject extraction should always succeed.
                // It's just that the RemoteEvent within it could not
                // be reconstituted. Therefore, just skip to the next
                // MarshalledObject in the stream.
                readCount++;
                readPosition = in.getOffset();
                //Generate new entry
                rData.add(new RemoteEventData(
                   null, 
                   new RemoteEventDataCursor(readCount, readPosition)));                
                if (persistenceLogger.isLoggable(Levels.HANDLED)) {
                        persistenceLogger.log(Levels.HANDLED, 
                            "Exception: ", cnfe);
                }            
            } finally {
                // If an IOException occurs then the eventReader is in 
                // a bad state. Therefore, we get rid of our existing
                // reference and create a fresh one. The underlying
                // stream is also removed from from the pool, forcing
                // a fresh copy to be returned on the next "get" request.
                if (IOExceptionCaught) {
                    //TODO (FCS) add close to interface
                    //eventReader.close();
                    eventReader = new EventReader();
                    if (in != null) {
                        streamPool.removeLogStream(in);
                        in = null;
                    }
                } else { 
                    if (in != null)
                        streamPool.releaseLogStream(in);
                    /* Shouldn't have to release since we have exclusive
                     * access, but the next getLogInputStream assumes things
                     * are on the freelist.
                     */
                }
            }
        }
        printControlData(persistenceLogger, "After Event::readAhead");
	if (debugState) {
	    assertInvariants();
	}
        return (RemoteEventData[]) rData.toArray(new RemoteEventData[0]);
    }
}
} catch (ClassNotFoundException cnfe) {
// Note that the read offset is still valid since the
// MarshalledObject extraction should always succeed.
// It's just that the RemoteEvent within it could not
// be reconstituted. Therefore, just skip to the next
// MarshalledObject in the stream.
readCount++;
readPosition = in.getOffset();
persistenceLogger.log(Level.FINEST,
"EventLog::readAhead() new readCount is {0}",
Long.valueOf(readCount));
}
}
if (persistenceLogger.isLoggable(Levels.HANDLED)) {
persistenceLogger.log(Levels.HANDLED,
"Exception: ", ie);
}
} catch (ClassNotFoundException cnfe) {
// Note that the read offset is still valid since the
// MarshalledObject extraction should always succeed.
// It's just that the RemoteEvent within it could not
// be reconstituted. Therefore, just skip to the next
// MarshalledObject in the stream.
readCount++;
readPosition = in.getOffset();
//Generate new entry
rData.add(new RemoteEventData(
null,
new RemoteEventDataCursor(readCount, readPosition)));
if (persistenceLogger.isLoggable(Levels.HANDLED)) {
persistenceLogger.log(Levels.HANDLED,
"Exception: ", cnfe);
}
"EventLog::readAhead() received IOException "
+ "... skipping to next read log");
}
readCount = nextReadAheadLog(readCount);
// TODO - rcount = readCount;
// TODO - if bump rcount you need to bump wcount too
// TODO - if bump counts then you need to persist them
readPosition = 0;
IOExceptionCaught = true;
if (persistenceLogger.isLoggable(Level.FINEST)) {
persistenceLogger.log(Level.FINEST,
"EventLog::readAhead() new readCount is {0}",
Long.valueOf(readCount));
}
}
if (persistenceLogger.isLoggable(Levels.HANDLED)) {
persistenceLogger.log(Levels.HANDLED,
"Exception: ", ie);
}
} catch (ClassNotFoundException cnfe) {
// Note that the read offset is still valid since the
// MarshalledObject extraction should always succeed.
// It's just that the RemoteEvent within it could not
// be reconstituted. Therefore, just skip to the next
// MarshalledObject in the stream.
readCount++;
readPosition = in.getOffset();
//Generate new entry
rData.add(new RemoteEventData(
null,
new RemoteEventDataCursor(readCount, readPosition)));
if (persistenceLogger.isLoggable(Levels.HANDLED)) {
persistenceLogger.log(Levels.HANDLED,
"Exception: ", cnfe);
}
} finally {
// If an IOException occurs then the eventReader is in
// a bad state. Therefore, we get rid of our existing
// reference and create a fresh one. The underlying
// stream is also removed from from the pool, forcing
// a fresh copy to be returned on the next "get" request.
if (IOExceptionCaught) {
//TODO (FCS) add close to interface
//eventReader.close();
eventReader = new EventReader();