diff --git a/router/java/src/net/i2p/router/ClientManagerFacade.java b/router/java/src/net/i2p/router/ClientManagerFacade.java index cdc9c41095952a3b49b3bb6574704769ffbf533b..980e6aeea322d79943b186e9f11bc61118b755ff 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 32f6500e6baf0f402703b527315acad862d817d7..aec948175acee6157b43f1ecd5a57bdb3be5fce1 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 61754fb48363cde0747fd3ee75f9e3690d8fd12b..64152b4fc1cacba99fd34b771688f6f5a62d8788 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 b48660dfcc30c457ac56e26f46a25fffec3e4366..f5ea41a329c0d119ab24502c6dc57b4fbe235795 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 6da53ec25f0652e3c830614cbd5c27ef5e6fbc46..0f0008c1365e3ed96707b12dacbd809d29aa2e93 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 108413a52f1de86fc7134cbc8b032b3094c18a33..ea9d202ed1c1ea8f6ef826265bff0e700fe84581 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 d7a1f7202f9f23778d832dce8d60daa1b04428ef..9fbd6a7aa694583183dc815b4b2f7c04dd52fcf6 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 1c276c8613c1bf7c43a726c9c627fba70c7bf575..0fe2f4639dcc20782e6af41b6c2606f800f4aaef 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 24aadc537d840d097d268f7ed2ac29fffbae1525..03de1bfd9039e56c78185e0f7ff60c997b0d5cf3 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 ceccc526f2ff7d4917b6b05e05862c8fad7c6384..eff93f7a004f0188f86cd4ae2cb5a60408d757aa 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 92b8bbcc3920729fabdf74af96cef3ef96423c67..6e28c03ab48f658ccdbc2b003c6bd1bf17f1e20b 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 7734e322a647c14be74ce2b368e7f29e41c5e03c..e835a0baaa341382725f32a4a889aee82b20a7c8 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 18343967d4496f7f7411e4d623be619755e044b2..71cc41890121024ed66d855b61c426d4cf016eaa 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 68db62729f6b9caf08f5717a54f1051e741cae63..43b870aaa99370c899d1c8fef4582c2152024a6a 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 8fcaeaa9c90c4947711e0797a07439705a664a44..c4a100be00883d0d0bc0b8fb4b94a4a2b21f87a4 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 d60c7b5cb010c1bb4dbee56fefe5b5aee31c91ed..961d5e032d709000895454ac0ec7da0287c9b2bc 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 c7f1e6816a2ade9e982e6fa64bd1d91557df4a4d..7fdd112f504a7b55a7fd5dff32b09ea7c91ddb8f 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 c8081e94d88184e021f550483b455bd3a224abea..c86c0dbf355691b8631888fb8b3e335a21e17e10 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 caf32dd90e736e43cbb95395007381cea6256027..0c806a6255bf53acb5b7a8893492ef529621144e 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 f2fecd974f48e17c18f066fda4498b0410521c7f..18ddff2d5fd02b3366c89372c9db17c365873b42 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 9790495c803df86535fbb6e9bb5bb1bbe273f762..e981438cc2cfdfd1c8124998ec7e3f731a875fb5 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 524eeeb7f129e063a9674e5ac7c899b6719a9221..59250a391bf72226aff485767391c091ef8688b8 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 086ab95d011d3fd6e6a79dfe70efae2a92a19133..cbd989c582f11ba880601c0049d9e53f70204b61 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 2b1971ba6b17343058972b1394fc150d900fb09e..809f73ffc3923815ae6bcdb8f5d96f85ebb3e8d9 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)