package net.i2p.data.i2cp; /* * free (adj.): unencumbered; not under the control of others * Written by jrandom in 2003 and released into the public domain * with no warranty of any kind, either expressed or implied. * It probably won't make your computer catch on fire, or eat * your children, but it might. Use at your own risk. * */ import java.io.IOException; import java.io.InputStream; import java.util.concurrent.atomic.AtomicLong; import net.i2p.I2PAppContext; import net.i2p.util.I2PThread; import net.i2p.util.Log; /** * The I2CPMessageReader reads an InputStream (using * {@link I2CPMessageHandler I2CPMessageHandler}) and passes out events to a registered * listener, where events are either messages being received, exceptions being * thrown, or the connection being closed. Applications should use this rather * than read from the stream themselves. * * @author jrandom */ public class I2CPMessageReader { private InputStream _stream; protected I2CPMessageEventListener _listener; protected I2CPMessageReaderRunner _reader; protected Thread _readerThread; protected static final AtomicLong __readerId = new AtomicLong(); public I2CPMessageReader(InputStream stream, I2CPMessageEventListener lsnr) { _stream = stream; setListener(lsnr); _reader = new I2CPMessageReaderRunner(); _readerThread = new I2PThread(_reader); _readerThread.setDaemon(true); _readerThread.setName("I2CP Reader " + __readerId.incrementAndGet()); } /** * For internal extension only. No stream. * @since 0.8.3 */ protected I2CPMessageReader(I2CPMessageEventListener lsnr) { setListener(lsnr); } public void setListener(I2CPMessageEventListener lsnr) { _listener = lsnr; } public I2CPMessageEventListener getListener() { return _listener; } /** * Instruct the reader to begin reading messages off the stream * */ public void startReading() { _readerThread.start(); } /** * Have the already started reader pause its reading indefinitely * @deprecated unused */ @Deprecated public void pauseReading() { _reader.pauseRunner(); } /** * Resume reading after a pause * @deprecated unused */ @Deprecated public void resumeReading() { _reader.resumeRunner(); } /** * Cancel reading. * */ public void stopReading() { _reader.cancelRunner(); } /** * Defines the different events the reader produces while reading the stream * */ public static interface I2CPMessageEventListener { /** * Notify the listener that a message has been received from the given * reader * * @param reader I2CPMessageReader to notify * @param message the I2CPMessage */ public void messageReceived(I2CPMessageReader reader, I2CPMessage message); /** * Notify the listener that an exception was thrown while reading from the given * reader * * @param reader I2CPMessageReader to notify * @param error Exception that was thrown, non-null */ public void readError(I2CPMessageReader reader, Exception error); /** * Notify the listener that the stream the given reader was running off * closed * * @param reader I2CPMessageReader to notify */ public void disconnected(I2CPMessageReader reader); } protected class I2CPMessageReaderRunner implements Runnable { protected volatile boolean _doRun; protected volatile boolean _stayAlive; private final Log _log = I2PAppContext.getGlobalContext().logManager().getLog(I2CPMessageReader.class); public I2CPMessageReaderRunner() { _doRun = true; _stayAlive = true; } /** deprecated unused */ public void pauseRunner() { _doRun = false; } /** deprecated unused */ public void resumeRunner() { _doRun = true; } public void cancelRunner() { _doRun = false; _stayAlive = false; // prevent race NPE InputStream in = _stream; if (in != null) { try { in.close(); } catch (IOException ioe) { _log.error("Error closing the stream", ioe); } } } public void run() { try { run2(); } catch (RuntimeException e) { _log.log(Log.CRIT, "Uncaught I2CP error", e); _listener.readError(I2CPMessageReader.this, e); cancelRunner(); } } /** * Called by run() * @since 0.9.21 */ protected void run2() { while (_stayAlive) { while (_doRun) { // do read try { I2CPMessage msg = I2CPMessageHandler.readMessage(_stream); if (msg != null) { //_log.debug("Before handling the newly received message"); _listener.messageReceived(I2CPMessageReader.this, msg); //_log.debug("After handling the newly received message"); } } catch (I2CPMessageException ime) { _log.warn("Error handling message", ime); _listener.readError(I2CPMessageReader.this, ime); cancelRunner(); } catch (IOException ioe) { _log.warn("IO Error handling message", ioe); _listener.disconnected(I2CPMessageReader.this); cancelRunner(); } catch (OutOfMemoryError oom) { // ooms seen here... maybe log and keep going? throw oom; } catch (RuntimeException e) { _log.log(Log.CRIT, "Unhandled error reading I2CP stream", e); _listener.disconnected(I2CPMessageReader.this); cancelRunner(); } } // ??? unused if (_stayAlive && !_doRun) { // pause .5 secs when we're paused try { Thread.sleep(500); } catch (InterruptedException ie) { // we should break away here. _log.warn("Breaking away stream", ie); _listener.disconnected(I2CPMessageReader.this); cancelRunner(); } } } _stream = null; } } }