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