From a6993fa489e4142ea62dbac9f9c97b6779558379 Mon Sep 17 00:00:00 2001 From: jrandom <jrandom> Date: Sat, 31 Jul 2004 02:21:46 +0000 Subject: [PATCH] now allow restarting within the same jvm (loading all the config options again, rebinding sockets, etc - it does NOT fire up all the clientApp tasks though - those aren't part of the router) --- .../net/i2p/router/ClientManagerFacade.java | 1 + .../src/net/i2p/router/CommSystemFacade.java | 3 +- router/java/src/net/i2p/router/JobQueue.java | 15 ++++++- .../src/net/i2p/router/JobQueueRunner.java | 1 + .../net/i2p/router/NetworkDatabaseFacade.java | 3 +- .../src/net/i2p/router/PeerManagerFacade.java | 1 + router/java/src/net/i2p/router/Router.java | 43 +++++++++++++++++++ router/java/src/net/i2p/router/Service.java | 6 +++ .../router/SessionKeyPersistenceHelper.java | 9 +++- .../src/net/i2p/router/StatisticsManager.java | 3 ++ .../net/i2p/router/admin/AdminListener.java | 15 ++++++- .../net/i2p/router/admin/AdminManager.java | 21 ++++++--- .../net/i2p/router/client/ClientManager.java | 22 ++++++++++ .../client/ClientManagerFacadeImpl.java | 7 +++ .../router/networkdb/kademlia/DataStore.java | 1 + .../KademliaNetworkDatabaseFacade.java | 20 +++++++++ .../kademlia/PersistentDataStore.java | 4 ++ .../kademlia/TransientDataStore.java | 6 +++ .../peermanager/PeerManagerFacadeImpl.java | 6 +++ .../transport/CommSystemFacadeImpl.java | 7 +++ .../router/transport/TransportManager.java | 6 +++ .../i2p/router/transport/VMCommSystem.java | 5 +++ .../PoolingTunnelManagerFacade.java | 5 +++ .../i2p/router/tunnelmanager/TunnelPool.java | 16 +++++++ 24 files changed, 215 insertions(+), 11 deletions(-) diff --git a/router/java/src/net/i2p/router/ClientManagerFacade.java b/router/java/src/net/i2p/router/ClientManagerFacade.java index cdc9c41095..980e6aeea3 100644 --- a/router/java/src/net/i2p/router/ClientManagerFacade.java +++ b/router/java/src/net/i2p/router/ClientManagerFacade.java @@ -89,6 +89,7 @@ class DummyClientManagerFacade extends ClientManagerFacade { public void startup() {} public void stopAcceptingClients() { } public void shutdown() {} + public void restart() {} public void messageDeliveryStatusUpdate(Destination fromDest, MessageId id, boolean delivered) {} diff --git a/router/java/src/net/i2p/router/CommSystemFacade.java b/router/java/src/net/i2p/router/CommSystemFacade.java index 32f6500e6b..aec948175a 100644 --- a/router/java/src/net/i2p/router/CommSystemFacade.java +++ b/router/java/src/net/i2p/router/CommSystemFacade.java @@ -30,5 +30,6 @@ public abstract class CommSystemFacade implements Service { class DummyCommSystemFacade extends CommSystemFacade { public void shutdown() {} public void startup() {} - public void processMessage(OutNetMessage msg) { } + public void restart() {} + public void processMessage(OutNetMessage msg) { } } diff --git a/router/java/src/net/i2p/router/JobQueue.java b/router/java/src/net/i2p/router/JobQueue.java index 61754fb483..64152b4fc1 100644 --- a/router/java/src/net/i2p/router/JobQueue.java +++ b/router/java/src/net/i2p/router/JobQueue.java @@ -239,6 +239,16 @@ public class JobQueue { } public void allowParallelOperation() { _allowParallelOperation = true; } + + public void restart() { + synchronized (_timedJobs) { + _timedJobs.clear(); + } + synchronized (_readyJobs) { + _readyJobs.clear(); + } + } + void shutdown() { _alive = false; StringBuffer buf = new StringBuffer(1024); @@ -344,7 +354,10 @@ public class JobQueue { t.start(); } } else if (_queueRunners.size() == numThreads) { - // noop + for (Iterator iter = _queueRunners.values().iterator(); iter.hasNext(); ) { + JobQueueRunner runner = (JobQueueRunner)iter.next(); + runner.startRunning(); + } } else { // numThreads < # runners, so shrink //for (int i = _queueRunners.size(); i > numThreads; i++) { // QueueRunner runner = (QueueRunner)_queueRunners.get(new Integer(i)); diff --git a/router/java/src/net/i2p/router/JobQueueRunner.java b/router/java/src/net/i2p/router/JobQueueRunner.java index b48660dfcc..f5ea41a329 100644 --- a/router/java/src/net/i2p/router/JobQueueRunner.java +++ b/router/java/src/net/i2p/router/JobQueueRunner.java @@ -30,6 +30,7 @@ class JobQueueRunner implements Runnable { public Job getLastJob() { return _lastJob; } public int getRunnerId() { return _id; } public void stopRunning() { _keepRunning = false; } + public void startRunning() { _keepRunning = true; } public void run() { long lastActive = _context.clock().now(); long jobNum = 0; diff --git a/router/java/src/net/i2p/router/NetworkDatabaseFacade.java b/router/java/src/net/i2p/router/NetworkDatabaseFacade.java index 6da53ec25f..0f0008c136 100644 --- a/router/java/src/net/i2p/router/NetworkDatabaseFacade.java +++ b/router/java/src/net/i2p/router/NetworkDatabaseFacade.java @@ -57,7 +57,8 @@ class DummyNetworkDatabaseFacade extends NetworkDatabaseFacade { _routers = new HashMap(); _context = ctx; } - + + public void restart() {} public void shutdown() {} public void startup() { RouterInfo info = _context.router().getRouterInfo(); diff --git a/router/java/src/net/i2p/router/PeerManagerFacade.java b/router/java/src/net/i2p/router/PeerManagerFacade.java index 108413a52f..ea9d202ed1 100644 --- a/router/java/src/net/i2p/router/PeerManagerFacade.java +++ b/router/java/src/net/i2p/router/PeerManagerFacade.java @@ -30,6 +30,7 @@ public interface PeerManagerFacade extends Service { class DummyPeerManagerFacade implements PeerManagerFacade { public void shutdown() {} public void startup() {} + public void restart() {} public void renderStatusHTML(OutputStream out) { } public List selectPeers(PeerSelectionCriteria criteria) { return null; } } diff --git a/router/java/src/net/i2p/router/Router.java b/router/java/src/net/i2p/router/Router.java index d7a1f7202f..9fbd6a7aa6 100644 --- a/router/java/src/net/i2p/router/Router.java +++ b/router/java/src/net/i2p/router/Router.java @@ -512,6 +512,49 @@ public class Router { } } + /** + * Save the current config options (returning true if save was + * successful, false otherwise) + * + */ + public boolean saveConfig() { + FileOutputStream fos = null; + try { + fos = new FileOutputStream(_configFilename); + _config.store(fos, "I2P Router config"); + } catch (IOException ioe) { + if (_log.shouldLog(Log.ERROR)) + _log.error("Error saving the config to " + _configFilename, ioe); + return false; + } finally { + if (fos != null) try { fos.close(); } catch (IOException ioe) {} + } + + return true; + } + + public void restart() { + _isAlive = false; + + try { _context.commSystem().restart(); } catch (Throwable t) { _log.log(Log.CRIT, "Error restarting the comm system", t); } + try { _context.clientManager().restart(); } catch (Throwable t) { _log.log(Log.CRIT, "Error restarting the client manager", t); } + try { _context.tunnelManager().restart(); } catch (Throwable t) { _log.log(Log.CRIT, "Error restarting the tunnel manager", t); } + try { _context.peerManager().restart(); } catch (Throwable t) { _log.log(Log.CRIT, "Error restarting the peer manager", t); } + try { _context.netDb().restart(); } catch (Throwable t) { _log.log(Log.CRIT, "Error restarting the networkDb", t); } + + //try { _context.jobQueue().restart(); } catch (Throwable t) { _log.log(Log.CRIT, "Error restarting the job queue", t); } + + _log.log(Log.CRIT, "Restart teardown complete... "); + try { Thread.sleep(10*1000); } catch (InterruptedException ie) {} + + _log.log(Log.CRIT, "Restarting..."); + + _isAlive = true; + _started = _context.clock().now(); + + _log.log(Log.CRIT, "Restart complete"); + } + private void dumpStats() { //_log.log(Log.CRIT, "Lifetime stats:\n\n" + StatsGenerator.generateStatsPage()); } diff --git a/router/java/src/net/i2p/router/Service.java b/router/java/src/net/i2p/router/Service.java index 1c276c8613..0fe2f4639d 100644 --- a/router/java/src/net/i2p/router/Service.java +++ b/router/java/src/net/i2p/router/Service.java @@ -31,5 +31,11 @@ public interface Service { */ public void shutdown(); + /** + * Perform a soft restart. + * + */ + public void restart(); + public void renderStatusHTML(OutputStream out) throws IOException; } diff --git a/router/java/src/net/i2p/router/SessionKeyPersistenceHelper.java b/router/java/src/net/i2p/router/SessionKeyPersistenceHelper.java index 24aadc537d..03de1bfd90 100644 --- a/router/java/src/net/i2p/router/SessionKeyPersistenceHelper.java +++ b/router/java/src/net/i2p/router/SessionKeyPersistenceHelper.java @@ -18,6 +18,7 @@ import net.i2p.util.Log; public class SessionKeyPersistenceHelper implements Service { private Log _log; private RouterContext _context; + private SessionKeyWriterJob _writerJob; private final static long PERSIST_DELAY = 3*60*1000; private final static String PROP_SESSION_KEY_FILE = "router.sessionKeys.location"; private final static String DEFAULT_SESSION_KEY_FILE = "sessionKeys.dat"; @@ -25,12 +26,18 @@ public class SessionKeyPersistenceHelper implements Service { public SessionKeyPersistenceHelper(RouterContext context) { _context = context; _log = _context.logManager().getLog(SessionKeyPersistenceHelper.class); + _writerJob = new SessionKeyWriterJob(); } public void shutdown() { writeState(); } + public void restart() { + writeState(); + startup(); + } + private String getKeyFile() { String val = _context.router().getConfigSetting(PROP_SESSION_KEY_FILE); if (val == null) @@ -58,7 +65,7 @@ public class SessionKeyPersistenceHelper implements Service { if (fin != null) try { fin.close(); } catch (IOException ioe) {} } } - _context.jobQueue().addJob(new SessionKeyWriterJob()); + _context.jobQueue().addJob(_writerJob); } } diff --git a/router/java/src/net/i2p/router/StatisticsManager.java b/router/java/src/net/i2p/router/StatisticsManager.java index ceccc526f2..eff93f7a00 100644 --- a/router/java/src/net/i2p/router/StatisticsManager.java +++ b/router/java/src/net/i2p/router/StatisticsManager.java @@ -47,6 +47,9 @@ public class StatisticsManager implements Service { } public void shutdown() {} + public void restart() { + startup(); + } public void startup() { String val = _context.router().getConfigSetting(PROP_PUBLISH_RANKINGS); try { diff --git a/router/java/src/net/i2p/router/admin/AdminListener.java b/router/java/src/net/i2p/router/admin/AdminListener.java index 92b8bbcc39..6e28c03ab4 100644 --- a/router/java/src/net/i2p/router/admin/AdminListener.java +++ b/router/java/src/net/i2p/router/admin/AdminListener.java @@ -37,6 +37,19 @@ public class AdminListener implements Runnable { _running = false; } + public void restart() { + // this works by taking advantage of the auto-retry mechanism in the + // startup() loop (which we reset to wait 1s). by failing the socket + // (through close()) and nulling it, we will have to try to build a new + // serverSocket (using the *new* _port) + _nextFailDelay = 1000; + ServerSocket s = _socket; + try { + _socket = null; + s.close(); + } catch (IOException ioe) {} + } + public void setPort(int port) { _port = port; } public int getPort() { return _port; } @@ -58,7 +71,7 @@ public class AdminListener implements Runnable { _log.info("Starting up listening for connections on port " + _port); _socket = new ServerSocket(_port); curDelay = 0; - while (_running) { + while (_running && (_socket != null) ) { try { Socket socket = _socket.accept(); _log.debug("Connection received"); diff --git a/router/java/src/net/i2p/router/admin/AdminManager.java b/router/java/src/net/i2p/router/admin/AdminManager.java index 7734e322a6..e835a0baaa 100644 --- a/router/java/src/net/i2p/router/admin/AdminManager.java +++ b/router/java/src/net/i2p/router/admin/AdminManager.java @@ -30,6 +30,10 @@ public class AdminManager implements Service { } } + public void restart() { + startup(); + } + public void startup() { int port = DEFAULT_ADMIN_PORT; String str = _context.router().getConfigSetting(PARAM_ADMIN_PORT); @@ -46,11 +50,16 @@ public class AdminManager implements Service { } private void startup(int port) { - _listener = new AdminListener(_context, port); - I2PThread t = new I2PThread(_listener); - t.setName("Admin Listener:" + port); - t.setDaemon(true); - //t.setPriority(Thread.MIN_PRIORITY); - t.start(); + if (_listener == null) { + _listener = new AdminListener(_context, port); + I2PThread t = new I2PThread(_listener); + t.setName("Admin Listener:" + port); + t.setDaemon(true); + //t.setPriority(Thread.MIN_PRIORITY); + t.start(); + } else { + _listener.setPort(port); + _listener.restart(); + } } } diff --git a/router/java/src/net/i2p/router/client/ClientManager.java b/router/java/src/net/i2p/router/client/ClientManager.java index 18343967d4..71cc418901 100644 --- a/router/java/src/net/i2p/router/client/ClientManager.java +++ b/router/java/src/net/i2p/router/client/ClientManager.java @@ -63,6 +63,28 @@ public class ClientManager { t.start(); } + public void restart() { + shutdown(); + + // to let the old listener die + try { Thread.sleep(2*1000); } catch (InterruptedException ie) {} + + int port = ClientManagerFacadeImpl.DEFAULT_PORT; + String portStr = _context.router().getConfigSetting(ClientManagerFacadeImpl.PROP_CLIENT_PORT); + if (portStr != null) { + try { + port = Integer.parseInt(portStr); + } catch (NumberFormatException nfe) { + _log.error("Error setting the port: " + portStr + " is not valid", nfe); + } + } + _listener = new ClientListenerRunner(_context, this, port); + Thread t = new I2PThread(_listener); + t.setName("ClientListener:" + port); + t.setDaemon(true); + t.start(); + } + public void shutdown() { _log.info("Shutting down the ClientManager"); _listener.stopListening(); diff --git a/router/java/src/net/i2p/router/client/ClientManagerFacadeImpl.java b/router/java/src/net/i2p/router/client/ClientManagerFacadeImpl.java index 68db62729f..43b870aaa9 100644 --- a/router/java/src/net/i2p/router/client/ClientManagerFacadeImpl.java +++ b/router/java/src/net/i2p/router/client/ClientManagerFacadeImpl.java @@ -61,6 +61,13 @@ public class ClientManagerFacadeImpl extends ClientManagerFacade { _manager.shutdown(); } + public void restart() { + if (_manager != null) + _manager.restart(); + else + startup(); + } + /** * Request that a particular client authorize the Leases contained in the * LeaseSet, after which the onCreateJob is queued up. If that doesn't occur diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/DataStore.java b/router/java/src/net/i2p/router/networkdb/kademlia/DataStore.java index 8fcaeaa9c9..c4a100be00 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/DataStore.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/DataStore.java @@ -19,4 +19,5 @@ public interface DataStore { public void put(Hash key, DataStructure data); public DataStructure remove(Hash key); public Set getKeys(); + public void restart(); } diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java b/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java index d60c7b5cb0..961d5e032d 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java @@ -180,6 +180,26 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade { _exploreKeys = null; _lastSent = null; } + + public void restart() { + _dbDir = _context.router().getConfigSetting(PROP_DB_DIR); + if (_dbDir == null) { + _log.info("No DB dir specified [" + PROP_DB_DIR + "], using [" + DEFAULT_DB_DIR + "]"); + _dbDir = DEFAULT_DB_DIR; + } + _ds.restart(); + synchronized (_explicitSendKeys) { _explicitSendKeys.clear(); } + synchronized (_exploreKeys) { _exploreKeys.clear(); } + synchronized (_passiveSendKeys) { _passiveSendKeys.clear(); } + + _initialized = true; + + RouterInfo ri = _context.router().getRouterInfo(); + publish(ri); + } + + String getDbDir() { return _dbDir; } + public void startup() { _log.info("Starting up the kademlia network database"); RouterInfo ri = _context.router().getRouterInfo(); diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/PersistentDataStore.java b/router/java/src/net/i2p/router/networkdb/kademlia/PersistentDataStore.java index c7f1e6816a..7fdd112f50 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/PersistentDataStore.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/PersistentDataStore.java @@ -45,6 +45,10 @@ class PersistentDataStore extends TransientDataStore { _context.jobQueue().addJob(new ReadJob()); } + public void restart() { + _dbDir = _facade.getDbDir(); + } + public DataStructure remove(Hash key) { _context.jobQueue().addJob(new RemoveJob(key)); return super.remove(key); diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/TransientDataStore.java b/router/java/src/net/i2p/router/networkdb/kademlia/TransientDataStore.java index c8081e94d8..c86c0dbf35 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/TransientDataStore.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/TransientDataStore.java @@ -36,6 +36,12 @@ class TransientDataStore implements DataStore { _log.info("Data Store initialized"); } + public void restart() { + synchronized (_data) { + _data.clear(); + } + } + public Set getKeys() { synchronized (_data) { return new HashSet(_data.keySet()); diff --git a/router/java/src/net/i2p/router/peermanager/PeerManagerFacadeImpl.java b/router/java/src/net/i2p/router/peermanager/PeerManagerFacadeImpl.java index caf32dd90e..0c806a6255 100644 --- a/router/java/src/net/i2p/router/peermanager/PeerManagerFacadeImpl.java +++ b/router/java/src/net/i2p/router/peermanager/PeerManagerFacadeImpl.java @@ -49,6 +49,12 @@ public class PeerManagerFacadeImpl implements PeerManagerFacade { _manager.storeProfiles(); } + public void restart() { + _manager.storeProfiles(); + _persistenceHelper.setUs(_context.routerHash()); + _manager.loadProfiles(); + } + public List selectPeers(PeerSelectionCriteria criteria) { return new ArrayList(_manager.selectPeers(criteria)); } diff --git a/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java b/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java index f2fecd974f..18ddff2d5f 100644 --- a/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java +++ b/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java @@ -45,6 +45,13 @@ public class CommSystemFacadeImpl extends CommSystemFacade { _manager.stopListening(); } + public void restart() { + if (_manager == null) + startup(); + else + _manager.restart(); + } + public List getBids(OutNetMessage msg) { return _manager.getBids(msg); } diff --git a/router/java/src/net/i2p/router/transport/TransportManager.java b/router/java/src/net/i2p/router/transport/TransportManager.java index 9790495c80..e981438cc2 100644 --- a/router/java/src/net/i2p/router/transport/TransportManager.java +++ b/router/java/src/net/i2p/router/transport/TransportManager.java @@ -101,6 +101,12 @@ public class TransportManager implements TransportEventListener { _log.debug("Done start listening on transports"); } + public void restart() { + stopListening(); + try { Thread.sleep(1*1000); } catch (InterruptedException ie) {} + startListening(); + } + public void stopListening() { for (int i = 0; i < _transports.size(); i++) { ((Transport)_transports.get(i)).stopListening(); diff --git a/router/java/src/net/i2p/router/transport/VMCommSystem.java b/router/java/src/net/i2p/router/transport/VMCommSystem.java index 524eeeb7f1..59250a391b 100644 --- a/router/java/src/net/i2p/router/transport/VMCommSystem.java +++ b/router/java/src/net/i2p/router/transport/VMCommSystem.java @@ -148,5 +148,10 @@ public class VMCommSystem extends CommSystemFacade { _commSystemFacades.put(_context.routerHash(), this); } + public void restart() { + _commSystemFacades.remove(_context.routerHash()); + _commSystemFacades.put(_context.routerHash(), this); + } + public void renderStatusHTML(OutputStream out) {} } diff --git a/router/java/src/net/i2p/router/tunnelmanager/PoolingTunnelManagerFacade.java b/router/java/src/net/i2p/router/tunnelmanager/PoolingTunnelManagerFacade.java index 086ab95d01..cbd989c582 100644 --- a/router/java/src/net/i2p/router/tunnelmanager/PoolingTunnelManagerFacade.java +++ b/router/java/src/net/i2p/router/tunnelmanager/PoolingTunnelManagerFacade.java @@ -56,6 +56,10 @@ public class PoolingTunnelManagerFacade implements TunnelManagerFacade { _testManager = null; } + public void restart() { + _pool.restart(); + } + /** * React to a request to join the specified tunnel. * @@ -230,4 +234,5 @@ public class PoolingTunnelManagerFacade implements TunnelManagerFacade { if (_pool != null) _pool.renderStatusHTML(out); } + } diff --git a/router/java/src/net/i2p/router/tunnelmanager/TunnelPool.java b/router/java/src/net/i2p/router/tunnelmanager/TunnelPool.java index 2b1971ba6b..809f73ffc3 100644 --- a/router/java/src/net/i2p/router/tunnelmanager/TunnelPool.java +++ b/router/java/src/net/i2p/router/tunnelmanager/TunnelPool.java @@ -576,6 +576,22 @@ class TunnelPool { _context.jobQueue().addJob(new TunnelPoolExpirationJob(_context, this)); } + public void restart() { + try { + String str = _context.router().getConfigSetting(TUNNEL_CREATION_TIMEOUT_PARAM); + _tunnelCreationTimeout = Long.parseLong(str); + } catch (Throwable t) { + _tunnelCreationTimeout = TUNNEL_CREATION_TIMEOUT_DEFAULT; + } + _targetClients = TARGET_CLIENTS_DEFAULT; + try { + String str = _context.router().getConfigSetting(TARGET_CLIENTS_PARAM); + _targetClients = Integer.parseInt(str); + } catch (Throwable t) { + _targetClients = TARGET_CLIENTS_DEFAULT; + } + } + public void shutdown() { if (_log.shouldLog(Log.INFO)) _log.info("Shutting down tunnel pool"); if (_persistenceHelper != null) -- GitLab