package net.i2p.client.impl; /* * free (adj.): unencumbered; not under the control of others * */ import java.util.Properties; import net.i2p.I2PAppContext; import net.i2p.client.I2PSessionException; import net.i2p.data.DataHelper; import net.i2p.util.Log; import net.i2p.util.SimpleTimer; /** * Reduce tunnels or shutdown the session on idle if so configured * * @author zzz */ class SessionIdleTimer implements SimpleTimer.TimedEvent { public static final long MINIMUM_TIME = 5*60*1000; private static final long DEFAULT_REDUCE_TIME = 20*60*1000; private static final long DEFAULT_CLOSE_TIME = 30*60*1000; private final Log _log; private final I2PAppContext _context; private final I2PSessionImpl _session; private final boolean _reduceEnabled; private final int _reduceQuantity; private final long _reduceTime; private final boolean _shutdownEnabled; private final long _shutdownTime; private final long _minimumTime; private long _lastActive; /** * reduce, shutdown, or both must be true */ public SessionIdleTimer(I2PAppContext context, I2PSessionImpl session, boolean reduce, boolean shutdown) { if (! (reduce || shutdown)) throw new IllegalArgumentException("At least one must be enabled"); _context = context; _log = context.logManager().getLog(SessionIdleTimer.class); _session = session; Properties props = session.getOptions(); long minimumTime = Long.MAX_VALUE; long reduceTime = 0; long shutdownTime = 0; int reduceQuantity = 0; if (reduce) { reduceQuantity = 1; String p = props.getProperty("i2cp.reduceQuantity"); if (p != null) { try { reduceQuantity = Math.max(Integer.parseInt(p), 1); // also check vs. configured quantities? } catch (NumberFormatException nfe) {} } reduceTime = DEFAULT_REDUCE_TIME; p = props.getProperty("i2cp.reduceIdleTime"); if (p != null) { try { reduceTime = Math.max(Long.parseLong(p), MINIMUM_TIME); } catch (NumberFormatException nfe) {} } minimumTime = reduceTime; } if (shutdown) { shutdownTime = DEFAULT_CLOSE_TIME; String p = props.getProperty("i2cp.closeIdleTime"); if (p != null) { try { shutdownTime = Math.max(Long.parseLong(p), MINIMUM_TIME); } catch (NumberFormatException nfe) {} } minimumTime = Math.min(minimumTime, shutdownTime); if (reduce && shutdownTime <= reduceTime) reduce = false; } _reduceEnabled = reduce; _reduceQuantity = reduceQuantity; _reduceTime = reduceTime; _shutdownEnabled = shutdown; _shutdownTime = shutdownTime; _minimumTime = minimumTime; } public void timeReached() { if (_session.isClosed()) return; long now = _context.clock().now(); long lastActivity = _session.lastActivity(); if (_log.shouldLog(Log.INFO)) _log.info("Fire idle timer, last activity: " + DataHelper.formatDuration(now - lastActivity) + " ago "); long nextDelay = 0; if (_shutdownEnabled && now - lastActivity >= _shutdownTime) { if (_log.shouldLog(Log.WARN)) _log.warn("Closing on idle " + _session); _session.destroySession(); return; } else if (lastActivity <= _lastActive && !_shutdownEnabled) { if (_log.shouldLog(Log.WARN)) _log.warn("Still idle, sleeping again " + _session); nextDelay = _reduceTime; } else if (_reduceEnabled && now - lastActivity >= _reduceTime) { if (_log.shouldLog(Log.WARN)) _log.warn("Reducing quantity on idle " + _session); try { _session.getProducer().updateTunnels(_session, _reduceQuantity); } catch (I2PSessionException ise) { _log.error("bork idle reduction " + ise); } _session.setReduced(); _lastActive = lastActivity; if (_shutdownEnabled) nextDelay = _shutdownTime - (now - lastActivity); else nextDelay = _reduceTime; } else { nextDelay = _minimumTime - (now - lastActivity); } _context.simpleTimer2().addEvent(this, nextDelay); } }