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.
This commit is contained in:
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)) {
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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());
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
24
history.txt
24
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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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());
|
||||
|
@ -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);
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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); } }
|
||||
|
||||
|
@ -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); } }
|
||||
}
|
Reference in New Issue
Block a user