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:
jrandom
2004-09-07 07:17:02 +00:00
committed by zzz
parent e57aa68854
commit 6151d63eac
18 changed files with 611 additions and 394 deletions

View File

@ -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();

View File

@ -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;
}

View File

@ -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);
}
}
}
}

View File

@ -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)) {