diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java index 6a0a6f20aacb568a5766f8577ea4aecd9fca2400..5d90660038c36c8fb89849e672976f6e7199c8f3 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java @@ -17,6 +17,8 @@ import java.util.List; import java.util.Properties; import net.i2p.I2PException; +import net.i2p.client.I2PSession; +import net.i2p.client.I2PSessionException; import net.i2p.client.streaming.I2PSocket; import net.i2p.client.streaming.I2PSocketManager; import net.i2p.client.streaming.I2PSocketManagerFactory; @@ -118,7 +120,16 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna return getSocketManager(getTunnel()); } protected static synchronized I2PSocketManager getSocketManager(I2PTunnel tunnel) { - if (socketManager == null) { + if (socketManager != null) { + I2PSession s = socketManager.getSession(); + if ( (s == null) || (s.isClosed()) ) { + _log.info("Building a new socket manager since the old one closed [s=" + s + "]"); + socketManager = buildSocketManager(tunnel); + } else { + _log.info("Not building a new socket manager since the old one is open [s=" + s + "]"); + } + } else { + _log.info("Building a new socket manager since there is no other one"); socketManager = buildSocketManager(tunnel); } return socketManager; @@ -277,7 +288,10 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna } return false; } - getTunnel().removeSession(sockMgr.getSession()); + I2PSession session = sockMgr.getSession(); + if (session != null) { + getTunnel().removeSession(session); + } l.log("Closing client " + toString()); try { if (ss != null) ss.close(); diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java index d566c02b9b995f7c7d2a3c176449c1fd20aca317..c2b091df9deb7541f048f0596865534f0e08ae61 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java @@ -28,6 +28,7 @@ public class TunnelController implements Logging { private Properties _config; private I2PTunnel _tunnel; private List _messages; + private List _sessions; private boolean _running; /** @@ -144,9 +145,42 @@ public class TunnelController implements Logging { _tunnel.runHttpClient(new String[] { listenPort }, this); else _tunnel.runHttpClient(new String[] { listenPort, proxyList }, this); + acquire(); _running = true; } + /** + * Note the fact that we are using some sessions, so that they dont get + * closed by some other tunnels + */ + private void acquire() { + List sessions = _tunnel.getSessions(); + if (sessions != null) { + for (int i = 0; i < sessions.size(); i++) { + I2PSession session = (I2PSession)sessions.get(i); + TunnelControllerGroup.getInstance().acquire(this, session); + } + _sessions = sessions; + } else { + _log.error("No sessions to acquire?"); + } + } + + /** + * Note the fact that we are no longer using some sessions, and if + * no other tunnels are using them, close them. + */ + private void release() { + if (_sessions != null) { + for (int i = 0; i < _sessions.size(); i++) { + I2PSession s = (I2PSession)_sessions.get(i); + TunnelControllerGroup.getInstance().release(this, s); + } + } else { + _log.error("No sessions to release?"); + } + } + private void startClient() { setI2CPOptions(); setSessionOptions(); @@ -154,6 +188,7 @@ public class TunnelController implements Logging { String listenPort = getListenPort(); String dest = getTargetDestination(); _tunnel.runClient(new String[] { listenPort, dest }, this); + acquire(); _running = true; } @@ -164,6 +199,7 @@ public class TunnelController implements Logging { String targetPort = getTargetPort(); String privKeyFile = getPrivKeyFile(); _tunnel.runServer(new String[] { targetHost, targetPort, privKeyFile }, this); + acquire(); _running = true; } @@ -201,6 +237,7 @@ public class TunnelController implements Logging { public void stopTunnel() { _tunnel.runClose(new String[] { "forced", "all" }, this); + release(); _running = false; } diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelControllerGroup.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelControllerGroup.java index 6f2b9885e5751b529388ed3ffd654d66a80c5a8d..eb1a42676785f81c1c0baf14443dc0fed68949f6 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelControllerGroup.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelControllerGroup.java @@ -8,13 +8,18 @@ import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Properties; +import java.util.Set; import java.util.TreeMap; import net.i2p.I2PAppContext; +import net.i2p.client.I2PSession; +import net.i2p.client.I2PSessionException; import net.i2p.util.Log; /** @@ -30,23 +35,33 @@ public class TunnelControllerGroup { private List _controllers; private String _configFile = DEFAULT_CONFIG_FILE; + /** + * Map of I2PSession to a Set of TunnelController objects + * using the session (to prevent closing the session until + * no more tunnels are using it) + * + */ + private Map _sessions; + public static TunnelControllerGroup getInstance() { return _instance; } private TunnelControllerGroup(String configFile) { _log = I2PAppContext.getGlobalContext().logManager().getLog(TunnelControllerGroup.class); + _instance = this; _controllers = new ArrayList(); _configFile = configFile; + _sessions = new HashMap(4); loadControllers(_configFile); } public static void main(String args[]) { if ( (args == null) || (args.length <= 0) ) { - _instance = new TunnelControllerGroup(DEFAULT_CONFIG_FILE); + new TunnelControllerGroup(DEFAULT_CONFIG_FILE); } else if (args.length == 1) { if (DEFAULT_CONFIG_FILE.equals(args[0])) - _instance = new TunnelControllerGroup(DEFAULT_CONFIG_FILE); + new TunnelControllerGroup(DEFAULT_CONFIG_FILE); else - _instance = new TunnelControllerGroup(args[0]); + new TunnelControllerGroup(args[0]); } else { System.err.println("Usage: TunnelControllerGroup [filename]"); return; @@ -281,4 +296,61 @@ public class TunnelControllerGroup { */ public List getControllers() { return _controllers; } + + /** + * Note the fact that the controller is using the session so that + * it isn't destroyed prematurely. + * + */ + void acquire(TunnelController controller, I2PSession session) { + synchronized (_sessions) { + Set owners = (Set)_sessions.get(session); + if (owners == null) { + owners = new HashSet(1); + _sessions.put(session, owners); + } + owners.add(controller); + } + if (_log.shouldLog(Log.INFO)) + _log.info("Acquiring session " + session + " for " + controller); + + } + + /** + * Note the fact that the controller is no longer using the session, and if + * no other controllers are using it, destroy the session. + * + */ + void release(TunnelController controller, I2PSession session) { + boolean shouldClose = false; + synchronized (_sessions) { + Set owners = (Set)_sessions.get(session); + if (owners != null) { + owners.remove(controller); + if (owners.size() <= 0) { + if (_log.shouldLog(Log.INFO)) + _log.info("After releasing session " + session + " by " + controller + ", no more owners remain"); + shouldClose = true; + _sessions.remove(session); + } else { + if (_log.shouldLog(Log.INFO)) + _log.info("After releasing session " + session + " by " + controller + ", " + owners.size() + " owners remain"); + shouldClose = false; + } + } else { + if (_log.shouldLog(Log.WARN)) + _log.warn("After releasing session " + session + " by " + controller + ", no owners were even known?!"); + shouldClose = true; + } + } + if (shouldClose) { + try { + session.destroySession(); + if (_log.shouldLog(Log.INFO)) + _log.info("Session destroyed: " + session); + } catch (I2PSessionException ise) { + _log.error("Error closing the client session", ise); + } + } + } } diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigServiceHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigServiceHandler.java index a496121a7c1880428b2f4620fb4709f1115ca423..8133aadb43d7caae2ecac675164ec116663df85c 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigServiceHandler.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigServiceHandler.java @@ -15,32 +15,40 @@ import org.tanukisoftware.wrapper.WrapperManager; public class ConfigServiceHandler extends FormHandler { public void ConfigNetHandler() {} + private class UpdateWrapperManagerTask implements Runnable { + private int _exitCode; + public UpdateWrapperManagerTask(int exitCode) { + _exitCode = exitCode; + } + public void run() { + try { + WrapperManager.signalStopped(_exitCode); + } catch (Throwable t) { + t.printStackTrace(); + } + } + } + protected void processForm() { if (_action == null) return; if ("Shutdown gracefully".equals(_action)) { - try { - WrapperManager.signalStopped(Router.EXIT_GRACEFUL); - } catch (Throwable t) { - addFormError("Warning: unable to contact the service manager - " + t.getMessage()); - } + _context.router().addShutdownTask(new UpdateWrapperManagerTask(Router.EXIT_GRACEFUL)); _context.router().shutdownGracefully(); addFormNotice("Graceful shutdown initiated"); } else if ("Shutdown immediately".equals(_action)) { - try { - WrapperManager.signalStopped(Router.EXIT_HARD); - } catch (Throwable t) { - addFormError("Warning: unable to contact the service manager - " + t.getMessage()); - } + _context.router().addShutdownTask(new UpdateWrapperManagerTask(Router.EXIT_HARD)); _context.router().shutdown(Router.EXIT_HARD); addFormNotice("Shutdown immediately! boom bye bye bad bwoy"); } else if ("Cancel graceful shutdown".equals(_action)) { _context.router().cancelGracefulShutdown(); addFormNotice("Graceful shutdown cancelled"); } else if ("Graceful restart".equals(_action)) { + _context.router().addShutdownTask(new UpdateWrapperManagerTask(Router.EXIT_GRACEFUL_RESTART)); _context.router().shutdownGracefully(Router.EXIT_GRACEFUL_RESTART); addFormNotice("Graceful restart requested"); } else if ("Hard restart".equals(_action)) { + _context.router().addShutdownTask(new UpdateWrapperManagerTask(Router.EXIT_HARD_RESTART)); _context.router().shutdown(Router.EXIT_HARD_RESTART); addFormNotice("Hard restart requested"); } else if ("Run I2P on startup".equals(_action)) { diff --git a/core/java/src/freenet/support/CPUInformation/CPUID.java b/core/java/src/freenet/support/CPUInformation/CPUID.java index 593f72f7bd2cfa24481f109d4d78e1d741569b6c..bdff26de2bedc33c754ba0170ff9e1023b426c4b 100644 --- a/core/java/src/freenet/support/CPUInformation/CPUID.java +++ b/core/java/src/freenet/support/CPUInformation/CPUID.java @@ -417,17 +417,17 @@ public class CPUID { String wantedProp = System.getProperty("jcpuid.enable", "true"); boolean wantNative = "true".equalsIgnoreCase(wantedProp); if (wantNative) { - boolean loaded = loadFromResource(); + boolean loaded = loadGeneric(); if (loaded) { _nativeOk = true; if (_doLog) - System.err.println("INFO: Native CPUID library '"+getResourceName()+"' loaded from resource"); + System.err.println("INFO: Native CPUID library '"+getLibraryMiddlePart()+"' loaded from somewhere in the path"); } else { - loaded = loadGeneric(); + loaded = loadFromResource(); if (loaded) { _nativeOk = true; if (_doLog) - System.err.println("INFO: Native CPUID library '"+getLibraryMiddlePart()+"' loaded from somewhere in the path"); + System.err.println("INFO: Native CPUID library '"+getResourceName()+"' loaded from resource"); } else { _nativeOk = false; if (_doLog) @@ -451,6 +451,12 @@ public class CPUID { * */ private static final boolean loadGeneric() { + try { + System.loadLibrary("jcpuid"); + return true; + } catch (UnsatisfiedLinkError ule) { + // fallthrough, try the OS-specific filename + } try { System.loadLibrary(getLibraryMiddlePart()); return true; @@ -486,7 +492,7 @@ public class CPUID { FileOutputStream fos = null; try { InputStream libStream = resource.openStream(); - outFile = File.createTempFile(libPrefix + "jcpuid", "lib.tmp" + libSuffix); + outFile = new File(libPrefix + "jcpuid" + libSuffix); fos = new FileOutputStream(outFile); byte buf[] = new byte[4096*1024]; while (true) { @@ -515,10 +521,6 @@ public class CPUID { if (fos != null) { try { fos.close(); } catch (IOException ioe) {} } - if (outFile != null) { - if (!outFile.delete()) - outFile.deleteOnExit(); - } } } diff --git a/core/java/src/net/i2p/client/I2PSession.java b/core/java/src/net/i2p/client/I2PSession.java index bb26cbe5499364b50ac1390ba60e7a35adecc13f..ba52516cd0f88430d4bf66d6b41d1e6857bc3310 100644 --- a/core/java/src/net/i2p/client/I2PSession.java +++ b/core/java/src/net/i2p/client/I2PSession.java @@ -101,6 +101,13 @@ public interface I2PSession { */ public void connect() throws I2PSessionException; + /** + * Have we closed the session? + * + * @return true if the session is closed + */ + public boolean isClosed(); + /** * Retrieve the Destination this session serves as the endpoint for. * Returns null if no destination is available. diff --git a/core/java/src/net/i2p/client/I2PSessionImpl.java b/core/java/src/net/i2p/client/I2PSessionImpl.java index 6dc9a0f8ceccba8d95ae5e18655d1be3a0969bb6..9da1ed77febed42f0d612aeaf9277a2d69bcbbd6 100644 --- a/core/java/src/net/i2p/client/I2PSessionImpl.java +++ b/core/java/src/net/i2p/client/I2PSessionImpl.java @@ -354,7 +354,12 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa _pendingSizes = new ArrayList(2); } - public void stopNotifying() { _alive = false; } + public void stopNotifying() { + _alive = false; + synchronized (AvailabilityNotifier.this) { + AvailabilityNotifier.this.notifyAll(); + } + } public void available(int msgId, int size) { synchronized (AvailabilityNotifier.this) { @@ -499,7 +504,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa public void destroySession(boolean sendDisconnect) { if (_closed) return; - if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "Destroy the session", new Exception("DestroySession()")); + if (_log.shouldLog(Log.INFO)) _log.info(getPrefix() + "Destroy the session", new Exception("DestroySession()")); if (sendDisconnect) { try { _producer.disconnect(this); @@ -518,7 +523,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa * */ private void closeSocket() { - if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "Closing the socket", new Exception("closeSocket")); + if (_log.shouldLog(Log.INFO)) _log.info(getPrefix() + "Closing the socket", new Exception("closeSocket")); _closed = true; if (_reader != null) _reader.stopReading(); _reader = null; diff --git a/core/java/src/net/i2p/client/RequestLeaseSetMessageHandler.java b/core/java/src/net/i2p/client/RequestLeaseSetMessageHandler.java index bc3f8d7f15831918cea65c05763c67488da50a72..282c1dae98063edb8a0f8b8714dc008d60813cc8 100644 --- a/core/java/src/net/i2p/client/RequestLeaseSetMessageHandler.java +++ b/core/java/src/net/i2p/client/RequestLeaseSetMessageHandler.java @@ -67,9 +67,13 @@ class RequestLeaseSetMessageHandler extends HandlerImpl { synchronized (_existingLeaseSets) { _existingLeaseSets.put(session.getMyDestination(), li); } - _log.debug("Creating new leaseInfo keys", new Exception("new leaseInfo keys")); + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Creating new leaseInfo keys for " + + session.getMyDestination().calculateHash().toBase64()); } else { - _log.debug("Caching the old leaseInfo keys", new Exception("cached! w00t")); + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Caching the old leaseInfo keys for " + + session.getMyDestination().calculateHash().toBase64()); } leaseSet.setEncryptionKey(li.getPublicKey()); diff --git a/core/java/src/net/i2p/util/NativeBigInteger.java b/core/java/src/net/i2p/util/NativeBigInteger.java index 261651c7a635616eccb2d3cd6cdcb92f364ac0f6..162ee0e89e6a5b1db2d767fb16e233fcbf2e3b85 100644 --- a/core/java/src/net/i2p/util/NativeBigInteger.java +++ b/core/java/src/net/i2p/util/NativeBigInteger.java @@ -485,7 +485,7 @@ public class NativeBigInteger extends BigInteger { FileOutputStream fos = null; try { InputStream libStream = resource.openStream(); - outFile = File.createTempFile(_libPrefix + "jbigi", "lib.tmp" + _libSuffix); + outFile = new File(_libPrefix + "jbigi" + _libSuffix); fos = new FileOutputStream(outFile); byte buf[] = new byte[4096*1024]; while (true) { @@ -514,10 +514,6 @@ public class NativeBigInteger extends BigInteger { if (fos != null) { try { fos.close(); } catch (IOException ioe) {} } - if (outFile != null) { - if (!outFile.delete()) - outFile.deleteOnExit(); - } } } diff --git a/core/java/src/net/i2p/util/SimpleTimer.java b/core/java/src/net/i2p/util/SimpleTimer.java index 0229b3ea02c86a0b1ba1ce084b34ac6243453a8b..331a133f878ecf95fead4b3d26f05708960e8952 100644 --- a/core/java/src/net/i2p/util/SimpleTimer.java +++ b/core/java/src/net/i2p/util/SimpleTimer.java @@ -64,6 +64,8 @@ public class SimpleTimer { private class SimpleTimerRunner implements Runnable { public void run() { + List eventsToFire = new ArrayList(1); + List timesToRemove = new ArrayList(1); while (true) { try { synchronized (_events) { @@ -71,34 +73,48 @@ public class SimpleTimer { _events.wait(); long now = System.currentTimeMillis(); long nextEventDelay = -1; - List removed = null; for (Iterator iter = _events.keySet().iterator(); iter.hasNext(); ) { Long when = (Long)iter.next(); if (when.longValue() <= now) { TimedEvent evt = (TimedEvent)_events.get(when); - try { - evt.timeReached(); - } catch (Throwable t) { - log("wtf, event borked: " + evt, t); - } - if (removed == null) - removed = new ArrayList(1); - removed.add(when); + eventsToFire.add(evt); + timesToRemove.add(when); } else { nextEventDelay = when.longValue() - now; break; } } - if (removed != null) { - for (int i = 0; i < removed.size(); i++) - _events.remove(removed.get(i)); + if (timesToRemove.size() > 0) { + for (int i = 0; i < timesToRemove.size(); i++) + _events.remove(timesToRemove.get(i)); + } else { + if (nextEventDelay != -1) + _events.wait(nextEventDelay); + else + _events.wait(); } - if (nextEventDelay != -1) - _events.wait(nextEventDelay); - else - _events.wait(); } - } catch (InterruptedException ie) {} + } catch (InterruptedException ie) { + // ignore + } catch (Throwable t) { + if (_log != null) { + _log.log(Log.CRIT, "Uncaught exception in the SimpleTimer!", t); + } else { + System.err.println("Uncaught exception in SimpleTimer"); + t.printStackTrace(); + } + } + + for (int i = 0; i < eventsToFire.size(); i++) { + TimedEvent evt = (TimedEvent)eventsToFire.get(i); + try { + evt.timeReached(); + } catch (Throwable t) { + log("wtf, event borked: " + evt, t); + } + } + eventsToFire.clear(); + timesToRemove.clear(); } } } diff --git a/history.txt b/history.txt index 8fb8592def8f3b32d384302cbbb509f04fea5b6c..fa3df896dec913e45afcc71e218007dcf6e51023 100644 --- a/history.txt +++ b/history.txt @@ -1,4 +1,26 @@ -$Id: history.txt,v 1.7 2004/09/04 16:54:09 jrandom Exp $ +$Id: history.txt,v 1.8 2004/09/06 00:20:42 jrandom Exp $ + +2004-09-07 jrandom + * Write the native libraries to the current directory when they are loaded + from a resource, and load them from that file on subsequent runs (in + turn, we no longer *cough* delete the running libraries...) + * Added support for a graceful restart. + * Added new pseudo-shutdown hook specific to the router, allowing + applications to request tasks to be run when the router shuts down. We + use this for integration with the service manager, since otherwise a + graceful shutdown would cause a timeout, followed by a forced hard + shutdown. + * Handle a bug in the SimpleTimer with requeued tasks. + * Made the capacity calculator a bit more dynamic by not outright ignoring + the otherwise valid capacity data for a period with a single rejected + tunnel (except for the 10 minute period). In addition, peers with an + equal capacity are ordered by speed rather than by their hashes. + * Cleaned up the SimpleTimer, addressing some threading and synchronization + issues. + * When an I2PTunnel client or httpclient is explicitly closed, destroy the + associated session (unless there are other clients using it), and deal + with a closed session when starting a new I2PTunnel instance. + * Refactoring and logging. 2004-09-06 jrandom * Address a race condition in the key management code that would manifest diff --git a/installer/resources/wrapper.conf b/installer/resources/wrapper.conf index 332e4a79a9907febf20aea6bf1f9c559d38646d0..018bd85af8e4f2b84e191d5af0ee210322a0baf2 100644 --- a/installer/resources/wrapper.conf +++ b/installer/resources/wrapper.conf @@ -1,145 +1,147 @@ -#******************************************************************** -# Wrapper Properties -#******************************************************************** -# Java Application -wrapper.java.command=java - -# Java Main class. This class must implement the WrapperListener interface -# or guarantee that the WrapperManager class is initialized. Helper -# classes are provided to do this for you. See the Integration section -# of the documentation for details. -wrapper.java.mainclass=org.tanukisoftware.wrapper.WrapperSimpleApp - -# Java Classpath (include wrapper.jar) Add class path elements as -# needed starting from 1 -wrapper.java.classpath.1=lib/ant.jar -wrapper.java.classpath.2=lib/heartbeat.jar -wrapper.java.classpath.3=lib/i2p.jar -wrapper.java.classpath.4=lib/i2ptunnel.jar -wrapper.java.classpath.5=lib/jasper-compiler.jar -wrapper.java.classpath.6=lib/jasper-runtime.jar -wrapper.java.classpath.7=lib/javax.servlet.jar -wrapper.java.classpath.8=lib/jnet.jar -wrapper.java.classpath.9=lib/mstreaming.jar -wrapper.java.classpath.10=lib/netmonitor.jar -wrapper.java.classpath.11=lib/org.mortbay.jetty.jar -wrapper.java.classpath.12=lib/router.jar -wrapper.java.classpath.13=lib/routerconsole.jar -wrapper.java.classpath.14=lib/sam.jar -wrapper.java.classpath.15=lib/wrapper.jar -wrapper.java.classpath.16=lib/xercesImpl.jar -wrapper.java.classpath.17=lib/xml-apis.jar -wrapper.java.classpath.18=lib/jbigi.jar -wrapper.java.classpath.19=lib/systray.jar -wrapper.java.classpath.20=lib/systray4j.jar - -# Java Library Path (location of Wrapper.DLL or libwrapper.so) -wrapper.java.library.path.1=. -wrapper.java.library.path.2=lib - -# Java Additional Parameters -wrapper.java.additional.1=-DloggerFilenameOverride=logs/log-router-@.txt - -# Initial Java Heap Size (in MB) -#wrapper.java.initmemory=4 - -# Maximum Java Heap Size (in MB) -#wrapper.java.maxmemory=32 - -# Application parameters. Add parameters as needed starting from 1 -wrapper.app.parameter.1=net.i2p.router.Router - -#******************************************************************** -# Wrapper Logging Properties -#******************************************************************** -# Format of output for the console. (See docs for formats) -wrapper.console.format=PM - -# Log Level for console output. (See docs for log levels) -wrapper.console.loglevel=INFO - -# Log file to use for wrapper output logging. -wrapper.logfile=wrapper.log - -# Format of output for the log file. (See docs for formats) -wrapper.logfile.format=LPTM - -# Log Level for log file output. (See docs for log levels) -wrapper.logfile.loglevel=INFO - -# Maximum size that the log file will be allowed to grow to before -# the log is rolled. Size is specified in bytes. The default value -# of 0, disables log rolling. May abbreviate with the 'k' (kb) or -# 'm' (mb) suffix. For example: 10m = 10 megabytes. -wrapper.logfile.maxsize=1m - -# Maximum number of rolled log files which will be allowed before old -# files are deleted. The default value of 0 implies no limit. -wrapper.logfile.maxfiles=2 - -# Log Level for sys/event log output. (See docs for log levels) -wrapper.syslog.loglevel=NONE - -# choose what to do if the JVM kills itself based on the exit code -wrapper.on_exit.default=SHUTDOWN -wrapper.on_exit.0=SHUTDOWN -wrapper.on_exit.1=SHUTDOWN -# OOM -wrapper.on_exit.10=RESTART -# graceful shutdown -wrapper.on_exit.2=SHUTDOWN -# hard shutdown -wrapper.on_exit.3=SHUTDOWN -# hard restart -wrapper.on_exit.4=RESTART - -# the router may take a few seconds to save state, etc -wrapper.jvm_exit.timeout=60 - -# give the OS 60s to clear all the old sockets / etc before restarting -wrapper.restart.delay=60 - -# use the wrapper's internal timer thread. otherwise this would -# force a restart of the router during daylight savings time as well -# as any time that the OS clock changes -wrapper.use_system_time=false - -# pid file for the JVM -wrapper.java.pidfile=routerjvm.pid -# pid file for the service monitoring the JVM -# -# From i2prouter: -# -# PIDDIR="." -# APP_NAME="i2p" -# PIDFILE="$PIDDIR/$APP_NAME.pid" -# -# This means i2prouter looks for './i2p.pid'. -wrapper.pidfile=i2p.pid - -#******************************************************************** -# Wrapper NT Service Properties -#******************************************************************** -# WARNING - Do not modify any of these properties when an application -# using this configuration file has been installed as a service. -# Please uninstall the service before modifying this section. The -# service can then be reinstalled. - -# Name of the service -wrapper.ntservice.name=i2p - -# Display name of the service -wrapper.ntservice.displayname=I2P Service - -# Description of the service -wrapper.ntservice.description=The I2P router service - -# Service dependencies. Add dependencies as needed starting from 1 -wrapper.ntservice.dependency.1= - -# Mode in which the service is installed. AUTO_START or DEMAND_START -wrapper.ntservice.starttype=AUTO_START - -# Allow the service to interact with the desktop. -wrapper.ntservice.interactive=true - +#******************************************************************** +# Wrapper Properties +#******************************************************************** +# Java Application +wrapper.java.command=java + +# Java Main class. This class must implement the WrapperListener interface +# or guarantee that the WrapperManager class is initialized. Helper +# classes are provided to do this for you. See the Integration section +# of the documentation for details. +wrapper.java.mainclass=org.tanukisoftware.wrapper.WrapperSimpleApp + +# Java Classpath (include wrapper.jar) Add class path elements as +# needed starting from 1 +wrapper.java.classpath.1=lib/ant.jar +wrapper.java.classpath.2=lib/heartbeat.jar +wrapper.java.classpath.3=lib/i2p.jar +wrapper.java.classpath.4=lib/i2ptunnel.jar +wrapper.java.classpath.5=lib/jasper-compiler.jar +wrapper.java.classpath.6=lib/jasper-runtime.jar +wrapper.java.classpath.7=lib/javax.servlet.jar +wrapper.java.classpath.8=lib/jnet.jar +wrapper.java.classpath.9=lib/mstreaming.jar +wrapper.java.classpath.10=lib/netmonitor.jar +wrapper.java.classpath.11=lib/org.mortbay.jetty.jar +wrapper.java.classpath.12=lib/router.jar +wrapper.java.classpath.13=lib/routerconsole.jar +wrapper.java.classpath.14=lib/sam.jar +wrapper.java.classpath.15=lib/wrapper.jar +wrapper.java.classpath.16=lib/xercesImpl.jar +wrapper.java.classpath.17=lib/xml-apis.jar +wrapper.java.classpath.18=lib/jbigi.jar +wrapper.java.classpath.19=lib/systray.jar +wrapper.java.classpath.20=lib/systray4j.jar + +# Java Library Path (location of Wrapper.DLL or libwrapper.so) +wrapper.java.library.path.1=. +wrapper.java.library.path.2=lib + +# Java Additional Parameters +wrapper.java.additional.1=-DloggerFilenameOverride=logs/log-router-@.txt + +# Initial Java Heap Size (in MB) +#wrapper.java.initmemory=4 + +# Maximum Java Heap Size (in MB) +#wrapper.java.maxmemory=32 + +# Application parameters. Add parameters as needed starting from 1 +wrapper.app.parameter.1=net.i2p.router.Router + +#******************************************************************** +# Wrapper Logging Properties +#******************************************************************** +# Format of output for the console. (See docs for formats) +wrapper.console.format=PM + +# Log Level for console output. (See docs for log levels) +wrapper.console.loglevel=INFO + +# Log file to use for wrapper output logging. +wrapper.logfile=wrapper.log + +# Format of output for the log file. (See docs for formats) +wrapper.logfile.format=LPTM + +# Log Level for log file output. (See docs for log levels) +wrapper.logfile.loglevel=INFO + +# Maximum size that the log file will be allowed to grow to before +# the log is rolled. Size is specified in bytes. The default value +# of 0, disables log rolling. May abbreviate with the 'k' (kb) or +# 'm' (mb) suffix. For example: 10m = 10 megabytes. +wrapper.logfile.maxsize=1m + +# Maximum number of rolled log files which will be allowed before old +# files are deleted. The default value of 0 implies no limit. +wrapper.logfile.maxfiles=2 + +# Log Level for sys/event log output. (See docs for log levels) +wrapper.syslog.loglevel=NONE + +# choose what to do if the JVM kills itself based on the exit code +wrapper.on_exit.default=SHUTDOWN +wrapper.on_exit.0=SHUTDOWN +wrapper.on_exit.1=SHUTDOWN +# OOM +wrapper.on_exit.10=RESTART +# graceful shutdown +wrapper.on_exit.2=SHUTDOWN +# hard shutdown +wrapper.on_exit.3=SHUTDOWN +# hard restart +wrapper.on_exit.4=RESTART +# hard restart +wrapper.on_exit.5=RESTART + +# the router may take a few seconds to save state, etc +wrapper.jvm_exit.timeout=10 + +# give the OS 60s to clear all the old sockets / etc before restarting +wrapper.restart.delay=60 + +# use the wrapper's internal timer thread. otherwise this would +# force a restart of the router during daylight savings time as well +# as any time that the OS clock changes +wrapper.use_system_time=false + +# pid file for the JVM +wrapper.java.pidfile=routerjvm.pid +# pid file for the service monitoring the JVM +# +# From i2prouter: +# +# PIDDIR="." +# APP_NAME="i2p" +# PIDFILE="$PIDDIR/$APP_NAME.pid" +# +# This means i2prouter looks for './i2p.pid'. +wrapper.pidfile=i2p.pid + +#******************************************************************** +# Wrapper NT Service Properties +#******************************************************************** +# WARNING - Do not modify any of these properties when an application +# using this configuration file has been installed as a service. +# Please uninstall the service before modifying this section. The +# service can then be reinstalled. + +# Name of the service +wrapper.ntservice.name=i2p + +# Display name of the service +wrapper.ntservice.displayname=I2P Service + +# Description of the service +wrapper.ntservice.description=The I2P router service + +# Service dependencies. Add dependencies as needed starting from 1 +wrapper.ntservice.dependency.1= + +# Mode in which the service is installed. AUTO_START or DEMAND_START +wrapper.ntservice.starttype=AUTO_START + +# Allow the service to interact with the desktop. +wrapper.ntservice.interactive=true + diff --git a/router/java/src/net/i2p/router/Router.java b/router/java/src/net/i2p/router/Router.java index 4e52047420b0402f48ca59f2d644167782eb8a50..8d6bba32060ace06af4db504967cb0a3a974de2d 100644 --- a/router/java/src/net/i2p/router/Router.java +++ b/router/java/src/net/i2p/router/Router.java @@ -59,6 +59,7 @@ public class Router { private I2PThread.OOMEventListener _oomListener; private ShutdownHook _shutdownHook; private I2PThread _gracefulShutdownDetector; + private Set _shutdownTasks; public final static String PROP_CONFIG_FILE = "router.configLocation"; @@ -123,6 +124,8 @@ public class Router { _gracefulShutdownDetector.setDaemon(true); _gracefulShutdownDetector.setName("Graceful shutdown hook"); _gracefulShutdownDetector.start(); + + _shutdownTasks = new HashSet(0); } /** @@ -542,6 +545,12 @@ public class Router { buf.setLength(0); } + public void addShutdownTask(Runnable task) { + synchronized (_shutdownTasks) { + _shutdownTasks.add(task); + } + } + public static final int EXIT_GRACEFUL = 2; public static final int EXIT_HARD = 3; public static final int EXIT_OOM = 10; @@ -564,6 +573,14 @@ public class Router { try { _sessionKeyPersistenceHelper.shutdown(); } catch (Throwable t) { _log.log(Log.CRIT, "Error shutting down the session key manager", t); } _context.listContexts().remove(_context); dumpStats(); + try { + for (Iterator iter = _shutdownTasks.iterator(); iter.hasNext(); ) { + Runnable task = (Runnable)iter.next(); + task.run(); + } + } catch (Throwable t) { + _log.log(Log.CRIT, "Error running shutdown task", t); + } _log.log(Log.CRIT, "Shutdown(" + exitCode + ") complete", new Exception("Shutdown")); try { _context.logManager().shutdown(); } catch (Throwable t) { } File f = new File(getPingFile()); diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index f1f60f7de70a7cc1a38a0eec622d99526efab735..1ee15f19d3cefb2e87ea37b1f65c646703244071 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -15,9 +15,9 @@ import net.i2p.CoreVersion; * */ public class RouterVersion { - public final static String ID = "$Revision: 1.23 $ $Date: 2004/09/04 16:54:09 $"; + public final static String ID = "$Revision: 1.24 $ $Date: 2004/09/06 00:21:26 $"; public final static String VERSION = "0.4"; - public final static long BUILD = 6; + public final static long BUILD = 7; public static void main(String args[]) { System.out.println("I2P Router version: " + VERSION); System.out.println("Router ID: " + RouterVersion.ID); diff --git a/router/java/src/net/i2p/router/peermanager/CapacityCalculator.java b/router/java/src/net/i2p/router/peermanager/CapacityCalculator.java index 81426567f79ab2a7c31f6c5a2192c5c92495e431..d5e5550a423e53867ff51e417ed4b56e73158953 100644 --- a/router/java/src/net/i2p/router/peermanager/CapacityCalculator.java +++ b/router/java/src/net/i2p/router/peermanager/CapacityCalculator.java @@ -78,8 +78,6 @@ public class CapacityCalculator extends Calculator { Rate curAccepted = acceptStat.getRate(period); Rate curRejected = rejectStat.getRate(period); Rate curFailed = failedStat.getRate(period); - if (curRejected.getCurrentEventCount() + curRejected.getLastEventCount() > 0) - return 0.0d; long eventCount = 0; if (curAccepted != null) @@ -91,6 +89,12 @@ public class CapacityCalculator extends Calculator { failed = curFailed.getCurrentEventCount() + curFailed.getLastEventCount(); if (failed > 0) val -= failed * stretch; + + if ( (period == 10*60*1000) && (curRejected.getCurrentEventCount() + curRejected.getLastEventCount() > 0) ) + return 0.0d; + else + val -= stretch * (curRejected.getCurrentEventCount() + curRejected.getLastEventCount()); + if (val >= 0) { return (val + GROWTH_FACTOR) * periodWeight(period); } else { diff --git a/router/java/src/net/i2p/router/peermanager/InverseCapacityCalculator.java b/router/java/src/net/i2p/router/peermanager/InverseCapacityCalculator.java new file mode 100644 index 0000000000000000000000000000000000000000..2cc28f864fff1d728128eb2b8cb6782fe58ef98c --- /dev/null +++ b/router/java/src/net/i2p/router/peermanager/InverseCapacityCalculator.java @@ -0,0 +1,54 @@ +package net.i2p.router.peermanager; + +import java.util.Comparator; +import net.i2p.data.DataHelper; + +/** + * Order profiles by their capacity, but backwards (highest capacity / value first). + * + */ +class InverseCapacityComparator implements Comparator { + /** + * Compare the two objects backwards. The standard comparator returns + * -1 if lhs is less than rhs, 1 if lhs is greater than rhs, or 0 if they're + * equal. To keep a strict ordering, we measure peers with equal capacity + * values according to their speed + * + * @return -1 if the right hand side is smaller, 1 if the left hand side is + * smaller, or 0 if they are the same peer (Comparator.compare() inverted) + */ + public int compare(Object lhs, Object rhs) { + if ( (lhs == null) || (rhs == null) || (!(lhs instanceof PeerProfile)) || (!(rhs instanceof PeerProfile)) ) + throw new ClassCastException("Only profiles can be compared - lhs = " + lhs + " rhs = " + rhs); + PeerProfile left = (PeerProfile)lhs; + PeerProfile right= (PeerProfile)rhs; + + double rval = right.getCapacityValue(); + double lval = left.getCapacityValue(); + + if (lval == rval) { + rval = right.getSpeedValue(); + lval = left.getSpeedValue(); + if (lval == rval) { + // note the following call inverts right and left (see: classname) + return DataHelper.compareTo(right.getPeer().getData(), left.getPeer().getData()); + } else { + // ok, fall through and compare based on speed, since the capacity is equal + } + } + + boolean rightBigger = rval > lval; + + //if (_log.shouldLog(Log.DEBUG)) + // _log.debug("The capacity of " + right.getPeer().toBase64() + // + " and " + left.getPeer().toBase64() + " marks " + (rightBigger ? "right" : "left") + // + " as larger: r=" + right.getCapacityValue() + // + " l=" + // + left.getCapacityValue()); + + if (rightBigger) + return 1; + else + return -1; + } +} diff --git a/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java b/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java index ab521d885ccc57c8be00f11502194eca44242523..26cc4f129e43bbc0e497dd68572ce101367b7be4 100644 --- a/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java +++ b/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java @@ -99,49 +99,12 @@ public class ProfileOrganizer { _persistenceHelper = new ProfilePersistenceHelper(_context); } - /** - * Order profiles by their capacity, but backwards (highest capacity / value first). - * - */ - private final class InverseCapacityComparator implements Comparator { - /** - * Compare the two objects backwards. The standard comparator returns - * -1 if lhs is less than rhs, 1 if lhs is greater than rhs, or 0 if they're - * equal. To keep a strict ordering, we measure peers with equal capacity - * values according to their hashes - * - * @return -1 if the right hand side is smaller, 1 if the left hand side is - * smaller, or 0 if they are the same peer (Comparator.compare() inverted) - */ - public int compare(Object lhs, Object rhs) { - if ( (lhs == null) || (rhs == null) || (!(lhs instanceof PeerProfile)) || (!(rhs instanceof PeerProfile)) ) - throw new ClassCastException("Only profiles can be compared - lhs = " + lhs + " rhs = " + rhs); - PeerProfile left = (PeerProfile)lhs; - PeerProfile right= (PeerProfile)rhs; - - double rval = right.getCapacityValue(); - double lval = left.getCapacityValue(); - - if (lval == rval) // note the following call inverts right and left (see: classname) - return DataHelper.compareTo(right.getPeer().getData(), left.getPeer().getData()); - - boolean rightBigger = rval > lval; - - //if (_log.shouldLog(Log.DEBUG)) - // _log.debug("The capacity of " + right.getPeer().toBase64() - // + " and " + left.getPeer().toBase64() + " marks " + (rightBigger ? "right" : "left") - // + " as larger: r=" + right.getCapacityValue() - // + " l=" - // + left.getCapacityValue()); - - if (rightBigger) - return 1; - else - return -1; - } - } - public void setUs(Hash us) { _us = us; } + Hash getUs() { return _us; } + + public double getSpeedThreshold() { return _thresholdSpeedValue; } + public double getCapacityThreshold() { return _thresholdCapacityValue; } + public double getIntegrationThreshold() { return _thresholdIntegrationValue; } /** * Retrieve the profile for the given peer, if one exists (else null) @@ -207,7 +170,6 @@ public class ProfileOrganizer { public boolean isHighCapacity(Hash peer) { synchronized (_reorganizeLock) { return _highCapacityPeers.containsKey(peer); } } public boolean isWellIntegrated(Hash peer) { synchronized (_reorganizeLock) { return _wellIntegratedPeers.containsKey(peer); } } public boolean isFailing(Hash peer) { synchronized (_reorganizeLock) { return _failingPeers.containsKey(peer); } } - /** * if a peer sends us more than 5 replies in a searchReply that we cannot @@ -234,8 +196,17 @@ public class ProfileOrganizer { } return false; } - + public void exportProfile(Hash profile, OutputStream out) throws IOException { + PeerProfile prof = getProfile(profile); + if (prof != null) + _persistenceHelper.writeProfile(prof, out); + } + + public void renderStatusHTML(OutputStream out) throws IOException { + ProfileOrganizerRenderer rend = new ProfileOrganizerRenderer(this, _context); + rend.renderStatusHTML(out); + } /** * Return a set of Hashes for peers that are both fast and reliable. If an insufficient @@ -422,12 +393,7 @@ public class ProfileOrganizer { allPeers.addAll(_notFailingPeers.values()); allPeers.addAll(_highCapacityPeers.values()); allPeers.addAll(_fastPeers.values()); - - _failingPeers.clear(); - _notFailingPeers.clear(); - _highCapacityPeers.clear(); - _fastPeers.clear(); - + Set reordered = new TreeSet(_comp); for (Iterator iter = _strictCapacityOrder.iterator(); iter.hasNext(); ) { PeerProfile prof = (PeerProfile)iter.next(); @@ -534,9 +500,6 @@ public class ProfileOrganizer { } } - public double getSpeedThreshold() { return _thresholdSpeedValue; } - public double getCapacityThreshold() { return _thresholdCapacityValue; } - //////// // no more public stuff below //////// @@ -578,7 +541,6 @@ public class ProfileOrganizer { _thresholdIntegrationValue = 1.0d * avg(totalIntegration, reordered.size()); } - /** * Update the _thresholdCapacityValue by using a few simple formulas run * against the specified peers. Ideally, we set the threshold capacity to @@ -775,116 +737,6 @@ public class ProfileOrganizer { */ private boolean shouldDrop(PeerProfile profile) { return false; } - public void exportProfile(Hash profile, OutputStream out) throws IOException { - PeerProfile prof = getProfile(profile); - if (prof != null) - _persistenceHelper.writeProfile(prof, out); - } - - public void renderStatusHTML(OutputStream out) throws IOException { - Set peers = selectAllPeers(); - - long hideBefore = _context.clock().now() - 6*60*60*1000; - - TreeMap order = new TreeMap(); - for (Iterator iter = peers.iterator(); iter.hasNext();) { - Hash peer = (Hash)iter.next(); - if (_us.equals(peer)) continue; - PeerProfile prof = getProfile(peer); - if (prof.getLastSendSuccessful() <= hideBefore) continue; - order.put(peer.toBase64(), prof); - } - - int fast = 0; - int reliable = 0; - int integrated = 0; - int failing = 0; - StringBuffer buf = new StringBuffer(16*1024); - buf.append("<h2>Peer Profiles</h2>\n"); - buf.append("<table border=\"1\">"); - buf.append("<tr>"); - buf.append("<td><b>Peer</b> (").append(order.size()).append(", hiding ").append(peers.size()-order.size()).append(")</td>"); - buf.append("<td><b>Groups</b></td>"); - buf.append("<td><b>Speed</b></td>"); - buf.append("<td><b>Capacity</b></td>"); - buf.append("<td><b>Integration</b></td>"); - buf.append("<td><b>Failing?</b></td>"); - buf.append("<td> </td>"); - buf.append("</tr>"); - for (Iterator iter = order.keySet().iterator(); iter.hasNext();) { - String name = (String)iter.next(); - PeerProfile prof = (PeerProfile)order.get(name); - Hash peer = prof.getPeer(); - - buf.append("<tr>"); - buf.append("<td><code>"); - if (prof.getIsFailing()) { - buf.append("<font color=\"red\">--").append(peer.toBase64().substring(0,6)).append("</font>"); - } else { - if (prof.getIsActive()) { - buf.append("<font color=\"blue\">++").append(peer.toBase64().substring(0,6)).append("</font>"); - } else { - buf.append("__").append(peer.toBase64().substring(0,6)); - } - } - buf.append("</code></td>"); - buf.append("<td>"); - int tier = 0; - boolean isIntegrated = false; - synchronized (_reorganizeLock) { - if (_fastPeers.containsKey(peer)) { - tier = 1; - fast++; - reliable++; - } else if (_highCapacityPeers.containsKey(peer)) { - tier = 2; - reliable++; - } else if (_notFailingPeers.containsKey(peer)) { - tier = 3; - } else { - failing++; - } - - if (_wellIntegratedPeers.containsKey(peer)) { - isIntegrated = true; - integrated++; - } - } - - switch (tier) { - case 1: buf.append("Fast"); break; - case 2: buf.append("High Capacity"); break; - case 3: buf.append("Not Failing"); break; - default: buf.append("Failing"); break; - } - if (isIntegrated) buf.append(", Integrated"); - - buf.append("<td align=\"right\">").append(num(prof.getSpeedValue())).append("</td>"); - buf.append("<td align=\"right\">").append(num(prof.getCapacityValue())).append("</td>"); - buf.append("<td align=\"right\">").append(num(prof.getIntegrationValue())).append("</td>"); - buf.append("<td align=\"right\">").append(prof.getIsFailing()).append("</td>"); - //buf.append("<td><a href=\"/profile/").append(prof.getPeer().toBase64().substring(0, 32)).append("\">profile.txt</a> "); - //buf.append(" <a href=\"#").append(prof.getPeer().toBase64().substring(0, 32)).append("\">netDb</a></td>"); - buf.append("<td><a href=\"netdb.jsp#").append(peer.toBase64().substring(0,6)).append("\">netDb</a></td>\n"); - buf.append("</tr>"); - } - buf.append("</table>"); - buf.append("<i>Definitions:<ul>"); - buf.append("<li><b>speed</b>: how many round trip messages can we pump through the peer per minute?</li>"); - buf.append("<li><b>capacity</b>: how many tunnels can we ask them to join in an hour?</li>"); - buf.append("<li><b>integration</b>: how many new peers have they told us about lately?</li>"); - buf.append("<li><b>failing?</b>: is the peer currently swamped (and if possible we should avoid nagging them)?</li>"); - buf.append("</ul></i>"); - buf.append("Red peers prefixed with '--' means the peer is failing, and blue peers prefixed "); - buf.append("with '++' means we've sent or received a message from them "); - buf.append("in the last five minutes</i><br />"); - buf.append("<b>Thresholds:</b><br />"); - buf.append("<b>Speed:</b> ").append(num(_thresholdSpeedValue)).append(" (").append(fast).append(" fast peers)<br />"); - buf.append("<b>Capacity:</b> ").append(num(_thresholdCapacityValue)).append(" (").append(reliable).append(" high capacity peers)<br />"); - buf.append("<b>Integration:</b> ").append(num(_thresholdIntegrationValue)).append(" (").append(integrated).append(" well integrated peers)<br />"); - out.write(buf.toString().getBytes()); - } - /** * Defines the minimum number of 'fast' peers that the organizer should select. If * the profile calculators derive a threshold that does not select at least this many peers, @@ -895,20 +747,6 @@ public class ProfileOrganizer { * @return minimum number of peers to be placed in the 'fast' group */ protected int getMinimumFastPeers() { - if (_context.router() != null) { - String val = _context.router().getConfigSetting(PROP_MINIMUM_FAST_PEERS); - if (val != null) { - try { - int rv = Integer.parseInt(val); - if (_log.shouldLog(Log.DEBUG)) - _log.debug("router config said " + PROP_MINIMUM_FAST_PEERS + '=' + val); - return rv; - } catch (NumberFormatException nfe) { - if (_log.shouldLog(Log.WARN)) - _log.warn("Minimum fast peers improperly set in the router config [" + val + "]", nfe); - } - } - } String val = _context.getProperty(PROP_MINIMUM_FAST_PEERS, ""+DEFAULT_MINIMUM_FAST_PEERS); if (val != null) { try { @@ -938,20 +776,6 @@ public class ProfileOrganizer { * @return minimum number of peers to be placed in the 'fast' group */ protected int getMinimumHighCapacityPeers() { - if (_context.router() != null) { - String val = _context.router().getConfigSetting(PROP_MINIMUM_HIGH_CAPACITY_PEERS); - if (val != null) { - try { - int rv = Integer.parseInt(val); - if (_log.shouldLog(Log.DEBUG)) - _log.debug("router config said " + PROP_MINIMUM_HIGH_CAPACITY_PEERS + '=' + val); - return rv; - } catch (NumberFormatException nfe) { - if (_log.shouldLog(Log.WARN)) - _log.warn("Minimum high capacity peers improperly set in the router config [" + val + "]", nfe); - } - } - } String val = _context.getProperty(PROP_MINIMUM_HIGH_CAPACITY_PEERS, ""+DEFAULT_MINIMUM_HIGH_CAPACITY_PEERS); if (val != null) { try { @@ -970,7 +794,6 @@ public class ProfileOrganizer { return DEFAULT_MINIMUM_HIGH_CAPACITY_PEERS; } - private final static DecimalFormat _fmt = new DecimalFormat("###,##0.00", new DecimalFormatSymbols(Locale.UK)); private final static String num(double num) { synchronized (_fmt) { return _fmt.format(num); } } diff --git a/router/java/src/net/i2p/router/peermanager/ProfileOrganizerRenderer.java b/router/java/src/net/i2p/router/peermanager/ProfileOrganizerRenderer.java new file mode 100644 index 0000000000000000000000000000000000000000..3d929afd8e274c1c2f02c476d3dd6734081fd80c --- /dev/null +++ b/router/java/src/net/i2p/router/peermanager/ProfileOrganizerRenderer.java @@ -0,0 +1,134 @@ +package net.i2p.router.peermanager; + +import java.io.IOException; +import java.io.OutputStream; + +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; + +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; + +import net.i2p.data.Hash; +import net.i2p.router.RouterContext; + +/** + * Helper class to refactor the HTML rendering from out of the ProfileOrganizer + * + */ +class ProfileOrganizerRenderer { + private RouterContext _context; + private ProfileOrganizer _organizer; + + public ProfileOrganizerRenderer(ProfileOrganizer organizer, RouterContext context) { + _context = context; + _organizer = organizer; + } + public void renderStatusHTML(OutputStream out) throws IOException { + Set peers = _organizer.selectAllPeers(); + + long hideBefore = _context.clock().now() - 3*60*60*1000; + + TreeMap order = new TreeMap(); + for (Iterator iter = peers.iterator(); iter.hasNext();) { + Hash peer = (Hash)iter.next(); + if (_organizer.getUs().equals(peer)) continue; + PeerProfile prof = _organizer.getProfile(peer); + if (prof.getLastSendSuccessful() <= hideBefore) continue; + order.put(peer.toBase64(), prof); + } + + int fast = 0; + int reliable = 0; + int integrated = 0; + int failing = 0; + StringBuffer buf = new StringBuffer(16*1024); + buf.append("<h2>Peer Profiles</h2>\n"); + buf.append("<table border=\"1\">"); + buf.append("<tr>"); + buf.append("<td><b>Peer</b> (").append(order.size()).append(", hiding ").append(peers.size()-order.size()).append(")</td>"); + buf.append("<td><b>Groups</b></td>"); + buf.append("<td><b>Speed</b></td>"); + buf.append("<td><b>Capacity</b></td>"); + buf.append("<td><b>Integration</b></td>"); + buf.append("<td><b>Failing?</b></td>"); + buf.append("<td> </td>"); + buf.append("</tr>"); + for (Iterator iter = order.keySet().iterator(); iter.hasNext();) { + String name = (String)iter.next(); + PeerProfile prof = (PeerProfile)order.get(name); + Hash peer = prof.getPeer(); + + buf.append("<tr>"); + buf.append("<td><code>"); + if (prof.getIsFailing()) { + buf.append("<font color=\"red\">--").append(peer.toBase64().substring(0,6)).append("</font>"); + } else { + if (prof.getIsActive()) { + buf.append("<font color=\"blue\">++").append(peer.toBase64().substring(0,6)).append("</font>"); + } else { + buf.append("__").append(peer.toBase64().substring(0,6)); + } + } + buf.append("</code></td>"); + buf.append("<td>"); + int tier = 0; + boolean isIntegrated = false; + if (_organizer.isFast(peer)) { + tier = 1; + fast++; + reliable++; + } else if (_organizer.isHighCapacity(peer)) { + tier = 2; + reliable++; + } else if (_organizer.isFailing(peer)) { + failing++; + } else { + tier = 3; + } + + if (_organizer.isWellIntegrated(peer)) { + isIntegrated = true; + integrated++; + } + + switch (tier) { + case 1: buf.append("Fast"); break; + case 2: buf.append("High Capacity"); break; + case 3: buf.append("Not Failing"); break; + default: buf.append("Failing"); break; + } + if (isIntegrated) buf.append(", Integrated"); + + buf.append("<td align=\"right\">").append(num(prof.getSpeedValue())).append("</td>"); + buf.append("<td align=\"right\">").append(num(prof.getCapacityValue())).append("</td>"); + buf.append("<td align=\"right\">").append(num(prof.getIntegrationValue())).append("</td>"); + buf.append("<td align=\"right\">").append(prof.getIsFailing()).append("</td>"); + //buf.append("<td><a href=\"/profile/").append(prof.getPeer().toBase64().substring(0, 32)).append("\">profile.txt</a> "); + //buf.append(" <a href=\"#").append(prof.getPeer().toBase64().substring(0, 32)).append("\">netDb</a></td>"); + buf.append("<td><a href=\"netdb.jsp#").append(peer.toBase64().substring(0,6)).append("\">netDb</a></td>\n"); + buf.append("</tr>"); + } + buf.append("</table>"); + buf.append("<i>Definitions:<ul>"); + buf.append("<li><b>speed</b>: how many round trip messages can we pump through the peer per minute?</li>"); + buf.append("<li><b>capacity</b>: how many tunnels can we ask them to join in an hour?</li>"); + buf.append("<li><b>integration</b>: how many new peers have they told us about lately?</li>"); + buf.append("<li><b>failing?</b>: is the peer currently swamped (and if possible we should avoid nagging them)?</li>"); + buf.append("</ul></i>"); + buf.append("Red peers prefixed with '--' means the peer is failing, and blue peers prefixed "); + buf.append("with '++' means we've sent or received a message from them "); + buf.append("in the last five minutes</i><br />"); + buf.append("<b>Thresholds:</b><br />"); + buf.append("<b>Speed:</b> ").append(num(_organizer.getSpeedThreshold())).append(" (").append(fast).append(" fast peers)<br />"); + buf.append("<b>Capacity:</b> ").append(num(_organizer.getCapacityThreshold())).append(" (").append(reliable).append(" high capacity peers)<br />"); + buf.append("<b>Integration:</b> ").append(num(_organizer.getIntegrationThreshold())).append(" (").append(integrated).append(" well integrated peers)<br />"); + out.write(buf.toString().getBytes()); + } + + private final static DecimalFormat _fmt = new DecimalFormat("###,##0.00", new DecimalFormatSymbols(Locale.UK)); + private final static String num(double num) { synchronized (_fmt) { return _fmt.format(num); } } +}