package org.limewire.nio.timeout; import java.util.concurrent.ScheduledExecutorService; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.limewire.collection.Periodic; import org.limewire.nio.observer.Shutdownable; /** * Kills an OutputStream after a certain amount of time has passed.<p> * * The flow has the following methodology: * When constructed, this class is inactive and does nothing. * To activate the stall-checking, you must call 'activate'. * When activate is called, it updates the 'check time' to be * DELAY_TIME plus the current time and also schedules the task * if necessary. * To de-active the stall-checking, you must call 'deactivate'. * Deactivate does NOT remove the task from the RouterService's schedule, * but it does tell the checker to not kill the output stream when * it is run.<p> * * Because the task can be reactivated without rescheduling, it is * possible that RouterService may 'run' the task before the most * recent delay time has expired. To counteract this, 'activate' * will store the time that it expects 'run' to be called. If 'run' is * called too soon, it will reschedule the task to be run at the * appropriate time.<p> * * All methods are synchronized and guaranteed to not lock the timer queue. */ public final class StalledUploadWatchdog extends Periodic { private static final Log LOG = LogFactory.getLog(StalledUploadWatchdog.class); /** * The amount of time to wait before we close this connection * if nothing has been written to the socket. * <p> * Non final for testing. */ public static long DELAY_TIME = 1000 * 60 * 2; //2 minutes private final Closer closer; private final long delayTime; public StalledUploadWatchdog(long delayTime, ScheduledExecutorService service) { super(new Closer(), service); this.closer = (Closer)getRunnable(); this.delayTime = delayTime; } /** * De-activates the killing of the NormalUploadState. */ public boolean deactivate() { if (LOG.isDebugEnabled()) LOG.debug("Deactivated on "+closer.shutdownable); unschedule(); closer.shutdownable = null; return closer.closed; } /** * Activates the checking. */ public synchronized void activate(Shutdownable shutdownable) { if(LOG.isDebugEnabled()) LOG.debug("Activated on: " + shutdownable); rescheduleIfLater(delayTime); closer.shutdownable = shutdownable; } /** * Kills the upload if we're active, and tells the state * to close the connection. * Reschedules if needed. */ private static class Closer implements Runnable { private volatile Shutdownable shutdownable; private volatile boolean closed; public void run() { closed = true; // If it was null, it was already closed // by an outside source. Shutdownable toShut = shutdownable; if( toShut != null ) { if(LOG.isDebugEnabled()) LOG.debug("STALLED! Killing: " + toShut); toShut.shutdown(); shutdownable = null; } } } }