States for TunnelController (#815)

This commit is contained in:
str4d
2015-03-23 13:59:05 +00:00
parent 9790d3ba64
commit 46fe4298b9

View File

@@ -42,8 +42,17 @@ public class TunnelController implements Logging {
private final I2PTunnel _tunnel; private final I2PTunnel _tunnel;
private final List<String> _messages; private final List<String> _messages;
private List<I2PSession> _sessions; private List<I2PSession> _sessions;
private boolean _running; private volatile TunnelState _state;
private boolean _starting;
private enum TunnelState {
START_ON_LOAD,
STARTING,
RUNNING,
STOPPING,
STOPPED,
DESTROYING,
DESTROYED,
}
public static final String KEY_BACKUP_DIR = "i2ptunnel-keyBackup"; public static final String KEY_BACKUP_DIR = "i2ptunnel-keyBackup";
@@ -124,11 +133,10 @@ public class TunnelController implements Logging {
_log = I2PAppContext.getGlobalContext().logManager().getLog(TunnelController.class); _log = I2PAppContext.getGlobalContext().logManager().getLog(TunnelController.class);
setConfig(config, prefix); setConfig(config, prefix);
_messages = new ArrayList<String>(4); _messages = new ArrayList<String>(4);
_running = false;
boolean keyOK = true; boolean keyOK = true;
if (createKey && (getType().endsWith("server") || getPersistentClientKey())) if (createKey && (getType().endsWith("server") || getPersistentClientKey()))
keyOK = createPrivateKey(); keyOK = createPrivateKey();
_starting = keyOK && getStartOnLoad(); _state = keyOK && getStartOnLoad() ? TunnelState.START_ON_LOAD : TunnelState.STOPPED;
} }
/** /**
@@ -193,8 +201,10 @@ public class TunnelController implements Logging {
} }
public void startTunnelBackground() { public void startTunnelBackground() {
if (_running) return; synchronized (this) {
_starting = true; if (_state != TunnelState.STOPPED && _state != TunnelState.START_ON_LOAD)
return;
}
new I2PAppThread(new Runnable() { public void run() { startTunnel(); } }).start(); new I2PAppThread(new Runnable() { public void run() { startTunnel(); } }).start();
} }
@@ -203,7 +213,17 @@ public class TunnelController implements Logging {
* *
*/ */
public void startTunnel() { public void startTunnel() {
_starting = true; synchronized (this) {
if (_state != TunnelState.STOPPED && _state != TunnelState.START_ON_LOAD) {
if (_state == TunnelState.RUNNING) {
if (_log.shouldLog(Log.INFO))
_log.info("Already running");
log("Tunnel " + getName() + " is already running");
}
return;
}
changeState(TunnelState.STARTING);
}
try { try {
doStartTunnel(); doStartTunnel();
} catch (Exception e) { } catch (Exception e) {
@@ -213,21 +233,20 @@ public class TunnelController implements Logging {
acquire(); acquire();
stopTunnel(); stopTunnel();
} }
_starting = false;
} }
/** /**
* @throws IllegalArgumentException via methods in I2PTunnel * @throws IllegalArgumentException via methods in I2PTunnel
*/ */
private void doStartTunnel() { private void doStartTunnel() {
if (_running) { synchronized (this) {
if (_log.shouldLog(Log.INFO)) if (_state != TunnelState.STARTING)
_log.info("Already running"); return;
log("Tunnel " + getName() + " is already running");
return;
} }
String type = getType(); String type = getType();
if ( (type == null) || (type.length() <= 0) ) { if ( (type == null) || (type.length() <= 0) ) {
changeState(TunnelState.STOPPED);
if (_log.shouldLog(Log.ERROR)) if (_log.shouldLog(Log.ERROR))
_log.error("Cannot start the tunnel - no type specified"); _log.error("Cannot start the tunnel - no type specified");
return; return;
@@ -237,6 +256,7 @@ public class TunnelController implements Logging {
if (type.endsWith("server") || getPersistentClientKey()) { if (type.endsWith("server") || getPersistentClientKey()) {
boolean ok = createPrivateKey(); boolean ok = createPrivateKey();
if (!ok) { if (!ok) {
changeState(TunnelState.STOPPED);
log("Failed to start tunnel " + getName() + " as the private key file could not be created"); log("Failed to start tunnel " + getName() + " as the private key file could not be created");
return; return;
} }
@@ -268,12 +288,13 @@ public class TunnelController implements Logging {
} else if (TYPE_STREAMR_SERVER.equals(type)) { } else if (TYPE_STREAMR_SERVER.equals(type)) {
startStreamrServer(); startStreamrServer();
} else { } else {
changeState(TunnelState.STOPPED);
if (_log.shouldLog(Log.ERROR)) if (_log.shouldLog(Log.ERROR))
_log.error("Cannot start tunnel - unknown type [" + type + "]"); _log.error("Cannot start tunnel - unknown type [" + type + "]");
return; return;
} }
acquire(); acquire();
_running = true; changeState(TunnelState.RUNNING);
} }
private void startHttpClient() { private void startHttpClient() {
@@ -554,12 +575,17 @@ public class TunnelController implements Logging {
* and it may have timer threads that continue running. * and it may have timer threads that continue running.
*/ */
public void stopTunnel() { public void stopTunnel() {
synchronized (this) {
if (_state != TunnelState.RUNNING)
return;
changeState(TunnelState.STOPPING);
}
// I2PTunnel removes the session in close(), // I2PTunnel removes the session in close(),
// so save the sessions to pass to release() and TCG // so save the sessions to pass to release() and TCG
Collection<I2PSession> sessions = getAllSessions(); Collection<I2PSession> sessions = getAllSessions();
_tunnel.runClose(new String[] { "forced", "all" }, this); _tunnel.runClose(new String[] { "forced", "all" }, this);
release(sessions); release(sessions);
_running = false; changeState(TunnelState.STOPPED);
} }
/** /**
@@ -569,12 +595,17 @@ public class TunnelController implements Logging {
* @since 0.9.17 * @since 0.9.17
*/ */
public void destroyTunnel() { public void destroyTunnel() {
synchronized (this) {
if (_state != TunnelState.RUNNING)
return;
changeState(TunnelState.DESTROYING);
}
// I2PTunnel removes the session in close(), // I2PTunnel removes the session in close(),
// so save the sessions to pass to release() and TCG // so save the sessions to pass to release() and TCG
Collection<I2PSession> sessions = getAllSessions(); Collection<I2PSession> sessions = getAllSessions();
_tunnel.runClose(new String[] { "destroy", "all" }, this); _tunnel.runClose(new String[] { "destroy", "all" }, this);
release(sessions); release(sessions);
_running = false; changeState(TunnelState.DESTROYED);
} }
public void restartTunnel() { public void restartTunnel() {
@@ -626,26 +657,29 @@ public class TunnelController implements Logging {
// tell i2ptunnel, who will tell the TunnelTask, who will tell the SocketManager // tell i2ptunnel, who will tell the TunnelTask, who will tell the SocketManager
setSessionOptions(); setSessionOptions();
if (_running) { synchronized (this) {
Collection<I2PSession> sessions = getAllSessions(); if (_state != TunnelState.RUNNING) {
if (sessions.isEmpty()) { if (_log.shouldLog(Log.DEBUG)) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Not running, not updating sessions");
_log.debug("Running but no sessions to update");
}
for (I2PSession s : sessions) {
// tell the router via the session
if (!s.isClosed()) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Session is open, updating: " + s);
s.updateOptions(_tunnel.getClientOptions());
} else {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Session is closed, not updating: " + s);
} }
return;
} }
} else { }
if (_log.shouldLog(Log.DEBUG)) { // Running, so check sessions
_log.debug("Not running, not updating sessions"); Collection<I2PSession> sessions = getAllSessions();
if (sessions.isEmpty()) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Running but no sessions to update");
}
for (I2PSession s : sessions) {
// tell the router via the session
if (!s.isClosed()) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Session is open, updating: " + s);
s.updateOptions(_tunnel.getClientOptions());
} else {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Session is closed, not updating: " + s);
} }
} }
} }
@@ -794,19 +828,26 @@ public class TunnelController implements Logging {
return null; return null;
} }
public boolean getIsRunning() { return _running; } public boolean getIsRunning() { return _state == TunnelState.RUNNING; }
public boolean getIsStarting() { return _starting; } public boolean getIsStarting() { return _state == TunnelState.START_ON_LOAD || _state == TunnelState.STARTING; }
/** if running but no open sessions, we are in standby */ /** if running but no open sessions, we are in standby */
public boolean getIsStandby() { public boolean getIsStandby() {
if (!_running) synchronized (this) {
return false; if (_state != TunnelState.RUNNING)
return false;
}
for (I2PSession sess : _tunnel.getSessions()) { for (I2PSession sess : _tunnel.getSessions()) {
if (!sess.isClosed()) if (!sess.isClosed())
return false; return false;
} }
return true; return true;
} }
private synchronized void changeState(TunnelState state) {
_state = state;
}
/** /**
* A text description of the tunnel. * A text description of the tunnel.
@@ -927,7 +968,7 @@ public class TunnelController implements Logging {
* *
*/ */
public void log(String s) { public void log(String s) {
synchronized (this) { synchronized (_messages) {
_messages.add(s); _messages.add(s);
while (_messages.size() > 10) while (_messages.size() > 10)
_messages.remove(0); _messages.remove(0);
@@ -943,7 +984,7 @@ public class TunnelController implements Logging {
*/ */
public List<String> clearMessages() { public List<String> clearMessages() {
List<String> rv = null; List<String> rv = null;
synchronized (this) { synchronized (_messages) {
rv = new ArrayList<String>(_messages); rv = new ArrayList<String>(_messages);
_messages.clear(); _messages.clear();
} }
@@ -955,6 +996,6 @@ public class TunnelController implements Logging {
*/ */
@Override @Override
public String toString() { public String toString() {
return "TC " + getType() + ' ' + getName() + " for " + _tunnel; return "TC " + getType() + ' ' + getName() + " for " + _tunnel + ' ' + _state;
} }
} }