diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java
index 6a0a6f20aa..5d90660038 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 d566c02b9b..c2b091df9d 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 6f2b9885e5..eb1a426767 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 a496121a7c..8133aadb43 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 593f72f7bd..bdff26de2b 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 bb26cbe549..ba52516cd0 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 6dc9a0f8ce..9da1ed77fe 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 bc3f8d7f15..282c1dae98 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 261651c7a6..162ee0e89e 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 0229b3ea02..331a133f87 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 8fb8592def..fa3df896de 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 332e4a79a9..018bd85af8 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 4e52047420..8d6bba3206 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 f1f60f7de7..1ee15f19d3 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 81426567f7..d5e5550a42 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 0000000000..2cc28f864f
--- /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 ab521d885c..26cc4f129e 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("
Peer Profiles
\n");
- buf.append("");
- buf.append("");
- buf.append("Peer (").append(order.size()).append(", hiding ").append(peers.size()-order.size()).append(") | ");
- buf.append("Groups | ");
- buf.append("Speed | ");
- buf.append("Capacity | ");
- buf.append("Integration | ");
- buf.append("Failing? | ");
- buf.append(" | ");
- buf.append("
");
- 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("");
- buf.append("");
- if (prof.getIsFailing()) {
- buf.append("--").append(peer.toBase64().substring(0,6)).append("");
- } else {
- if (prof.getIsActive()) {
- buf.append("++").append(peer.toBase64().substring(0,6)).append("");
- } else {
- buf.append("__").append(peer.toBase64().substring(0,6));
- }
- }
- buf.append(" | ");
- buf.append("");
- 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(" | ").append(num(prof.getSpeedValue())).append(" | ");
- buf.append("").append(num(prof.getCapacityValue())).append(" | ");
- buf.append("").append(num(prof.getIntegrationValue())).append(" | ");
- buf.append("").append(prof.getIsFailing()).append(" | ");
- //buf.append("profile.txt ");
- //buf.append(" netDb | ");
- buf.append("netDb | \n");
- buf.append("
");
- }
- buf.append("
");
- buf.append("Definitions:");
- buf.append("- speed: how many round trip messages can we pump through the peer per minute?
");
- buf.append("- capacity: how many tunnels can we ask them to join in an hour?
");
- buf.append("- integration: how many new peers have they told us about lately?
");
- buf.append("- failing?: is the peer currently swamped (and if possible we should avoid nagging them)?
");
- buf.append("
");
- 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
");
- buf.append("Thresholds:
");
- buf.append("Speed: ").append(num(_thresholdSpeedValue)).append(" (").append(fast).append(" fast peers)
");
- buf.append("Capacity: ").append(num(_thresholdCapacityValue)).append(" (").append(reliable).append(" high capacity peers)
");
- buf.append("Integration: ").append(num(_thresholdIntegrationValue)).append(" (").append(integrated).append(" well integrated peers)
");
- 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 0000000000..3d929afd8e
--- /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("Peer Profiles
\n");
+ buf.append("");
+ buf.append("");
+ buf.append("Peer (").append(order.size()).append(", hiding ").append(peers.size()-order.size()).append(") | ");
+ buf.append("Groups | ");
+ buf.append("Speed | ");
+ buf.append("Capacity | ");
+ buf.append("Integration | ");
+ buf.append("Failing? | ");
+ buf.append(" | ");
+ buf.append("
");
+ 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("");
+ buf.append("");
+ if (prof.getIsFailing()) {
+ buf.append("--").append(peer.toBase64().substring(0,6)).append("");
+ } else {
+ if (prof.getIsActive()) {
+ buf.append("++").append(peer.toBase64().substring(0,6)).append("");
+ } else {
+ buf.append("__").append(peer.toBase64().substring(0,6));
+ }
+ }
+ buf.append(" | ");
+ buf.append("");
+ 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(" | ").append(num(prof.getSpeedValue())).append(" | ");
+ buf.append("").append(num(prof.getCapacityValue())).append(" | ");
+ buf.append("").append(num(prof.getIntegrationValue())).append(" | ");
+ buf.append("").append(prof.getIsFailing()).append(" | ");
+ //buf.append("profile.txt ");
+ //buf.append(" netDb | ");
+ buf.append("netDb | \n");
+ buf.append("
");
+ }
+ buf.append("
");
+ buf.append("Definitions:");
+ buf.append("- speed: how many round trip messages can we pump through the peer per minute?
");
+ buf.append("- capacity: how many tunnels can we ask them to join in an hour?
");
+ buf.append("- integration: how many new peers have they told us about lately?
");
+ buf.append("- failing?: is the peer currently swamped (and if possible we should avoid nagging them)?
");
+ buf.append("
");
+ 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
");
+ buf.append("Thresholds:
");
+ buf.append("Speed: ").append(num(_organizer.getSpeedThreshold())).append(" (").append(fast).append(" fast peers)
");
+ buf.append("Capacity: ").append(num(_organizer.getCapacityThreshold())).append(" (").append(reliable).append(" high capacity peers)
");
+ buf.append("Integration: ").append(num(_organizer.getIntegrationThreshold())).append(" (").append(integrated).append(" well integrated peers)
");
+ 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); } }
+}