Plugins throw exceptions for startup errors

This commit is contained in:
Torsten Grote
2016-12-14 09:52:47 -02:00
parent 074f5c2faf
commit ffc9fdbb92
11 changed files with 106 additions and 79 deletions

View File

@@ -18,6 +18,7 @@ import org.briarproject.bramble.api.keyagreement.KeyAgreementListener;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff; import org.briarproject.bramble.api.plugin.Backoff;
import org.briarproject.bramble.api.plugin.PluginException;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin; import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback; import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback;
@@ -128,7 +129,7 @@ class DroidtoothPlugin implements DuplexPlugin {
} }
@Override @Override
public boolean start() throws IOException { public void start() throws PluginException {
if (used.getAndSet(true)) throw new IllegalStateException(); if (used.getAndSet(true)) throw new IllegalStateException();
// BluetoothAdapter.getDefaultAdapter() must be called on a thread // BluetoothAdapter.getDefaultAdapter() must be called on a thread
// with a message queue, so submit it to the AndroidExecutor // with a message queue, so submit it to the AndroidExecutor
@@ -142,13 +143,14 @@ class DroidtoothPlugin implements DuplexPlugin {
}).get(); }).get();
} catch (InterruptedException e) { } catch (InterruptedException e) {
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();
throw new IOException("Interrupted while getting BluetoothAdapter"); LOG.warning("Interrupted while getting BluetoothAdapter");
throw new PluginException(e);
} catch (ExecutionException e) { } catch (ExecutionException e) {
throw new IOException(e); throw new PluginException(e);
} }
if (adapter == null) { if (adapter == null) {
LOG.info("Bluetooth is not supported"); LOG.info("Bluetooth is not supported");
return false; throw new PluginException();
} }
running = true; running = true;
// Listen for changes to the Bluetooth state // Listen for changes to the Bluetooth state
@@ -170,7 +172,6 @@ class DroidtoothPlugin implements DuplexPlugin {
LOG.info("Not enabling Bluetooth"); LOG.info("Not enabling Bluetooth");
} }
} }
return true;
} }
private void bind() { private void bind() {

View File

@@ -39,14 +39,13 @@ class AndroidLanTcpPlugin extends LanTcpPlugin {
} }
@Override @Override
public boolean start() { public void start() {
if (used.getAndSet(true)) throw new IllegalStateException(); if (used.getAndSet(true)) throw new IllegalStateException();
running = true; running = true;
// Register to receive network status events // Register to receive network status events
networkStateReceiver = new NetworkStateReceiver(); networkStateReceiver = new NetworkStateReceiver();
IntentFilter filter = new IntentFilter(CONNECTIVITY_ACTION); IntentFilter filter = new IntentFilter(CONNECTIVITY_ACTION);
appContext.registerReceiver(networkStateReceiver, filter); appContext.registerReceiver(networkStateReceiver, filter);
return true;
} }
@Override @Override

View File

@@ -25,6 +25,7 @@ import org.briarproject.bramble.api.keyagreement.KeyAgreementListener;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff; import org.briarproject.bramble.api.plugin.Backoff;
import org.briarproject.bramble.api.plugin.PluginException;
import org.briarproject.bramble.api.plugin.TorConstants; import org.briarproject.bramble.api.plugin.TorConstants;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin; import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
@@ -162,14 +163,18 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
} }
@Override @Override
public boolean start() throws IOException { public void start() throws PluginException {
if (used.getAndSet(true)) throw new IllegalStateException(); if (used.getAndSet(true)) throw new IllegalStateException();
// Install or update the assets if necessary // Install or update the assets if necessary
if (!assetsAreUpToDate()) installAssets(); if (!assetsAreUpToDate()) installAssets();
LOG.info("Starting Tor"); LOG.info("Starting Tor");
// Watch for the auth cookie file being updated // Watch for the auth cookie file being updated
cookieFile.getParentFile().mkdirs(); try {
cookieFile.createNewFile(); cookieFile.getParentFile().mkdirs();
cookieFile.createNewFile();
} catch (IOException e) {
throw new PluginException(e);
}
CountDownLatch latch = new CountDownLatch(1); CountDownLatch latch = new CountDownLatch(1);
FileObserver obs = new WriteObserver(cookieFile, latch); FileObserver obs = new WriteObserver(cookieFile, latch);
obs.startWatching(); obs.startWatching();
@@ -182,8 +187,8 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
Process torProcess; Process torProcess;
try { try {
torProcess = Runtime.getRuntime().exec(cmd, env, torDirectory); torProcess = Runtime.getRuntime().exec(cmd, env, torDirectory);
} catch (SecurityException e) { } catch (SecurityException | IOException e) {
throw new IOException(e); throw new PluginException(e);
} }
// Log the process's standard output until it detaches // Log the process's standard output until it detaches
if (LOG.isLoggable(INFO)) { if (LOG.isLoggable(INFO)) {
@@ -197,35 +202,39 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
if (exit != 0) { if (exit != 0) {
if (LOG.isLoggable(WARNING)) if (LOG.isLoggable(WARNING))
LOG.warning("Tor exited with value " + exit); LOG.warning("Tor exited with value " + exit);
return false; throw new PluginException();
} }
// Wait for the auth cookie file to be created/updated // Wait for the auth cookie file to be created/updated
if (!latch.await(COOKIE_TIMEOUT, MILLISECONDS)) { if (!latch.await(COOKIE_TIMEOUT, MILLISECONDS)) {
LOG.warning("Auth cookie not created"); LOG.warning("Auth cookie not created");
if (LOG.isLoggable(INFO)) listFiles(torDirectory); if (LOG.isLoggable(INFO)) listFiles(torDirectory);
return false; throw new PluginException();
} }
} catch (InterruptedException e) { } catch (InterruptedException e) {
LOG.warning("Interrupted while starting Tor"); LOG.warning("Interrupted while starting Tor");
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();
return false; throw new PluginException();
} }
// Open a control connection and authenticate using the cookie file try {
controlSocket = new Socket("127.0.0.1", CONTROL_PORT); // Open a control connection and authenticate using the cookie file
controlConnection = new TorControlConnection(controlSocket); controlSocket = new Socket("127.0.0.1", CONTROL_PORT);
controlConnection.authenticate(read(cookieFile)); controlConnection = new TorControlConnection(controlSocket);
// Tell Tor to exit when the control connection is closed controlConnection.authenticate(read(cookieFile));
controlConnection.takeOwnership(); // Tell Tor to exit when the control connection is closed
controlConnection.resetConf(Collections.singletonList(OWNER)); controlConnection.takeOwnership();
running = true; controlConnection.resetConf(Collections.singletonList(OWNER));
// Register to receive events from the Tor process running = true;
controlConnection.setEventHandler(this); // Register to receive events from the Tor process
controlConnection.setEvents(Arrays.asList(EVENTS)); controlConnection.setEventHandler(this);
// Check whether Tor has already bootstrapped controlConnection.setEvents(Arrays.asList(EVENTS));
String phase = controlConnection.getInfo("status/bootstrap-phase"); // Check whether Tor has already bootstrapped
if (phase != null && phase.contains("PROGRESS=100")) { String phase = controlConnection.getInfo("status/bootstrap-phase");
LOG.info("Tor has already bootstrapped"); if (phase != null && phase.contains("PROGRESS=100")) {
connectionStatus.setBootstrapped(); LOG.info("Tor has already bootstrapped");
connectionStatus.setBootstrapped();
}
} catch (IOException e) {
throw new PluginException(e);
} }
// Register to receive network status events // Register to receive network status events
networkStateReceiver = new NetworkStateReceiver(); networkStateReceiver = new NetworkStateReceiver();
@@ -233,7 +242,6 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
appContext.registerReceiver(networkStateReceiver, filter); appContext.registerReceiver(networkStateReceiver, filter);
// Bind a server socket to receive incoming hidden service connections // Bind a server socket to receive incoming hidden service connections
bind(); bind();
return true;
} }
private boolean assetsAreUpToDate() { private boolean assetsAreUpToDate() {
@@ -246,7 +254,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
} }
} }
private void installAssets() throws IOException { private void installAssets() throws PluginException {
InputStream in = null; InputStream in = null;
OutputStream out = null; OutputStream out = null;
try { try {
@@ -269,7 +277,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
} catch (IOException e) { } catch (IOException e) {
tryToClose(in); tryToClose(in);
tryToClose(out); tryToClose(out);
throw e; throw new PluginException(e);
} }
} }
@@ -476,7 +484,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
} }
@Override @Override
public void stop() throws IOException { public void stop() throws PluginException {
running = false; running = false;
tryToClose(socket); tryToClose(socket);
if (networkStateReceiver != null) if (networkStateReceiver != null)

View File

@@ -3,7 +3,6 @@ package org.briarproject.bramble.api.plugin;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.io.IOException;
import java.util.Collection; import java.util.Collection;
@NotNullByDefault @NotNullByDefault
@@ -25,14 +24,14 @@ public interface Plugin {
int getMaxIdleTime(); int getMaxIdleTime();
/** /**
* Starts the plugin and returns true if it started successfully. * Starts the plugin.
*/ */
boolean start() throws IOException; void start() throws PluginException;
/** /**
* Stops the plugin. * Stops the plugin.
*/ */
void stop() throws IOException; void stop() throws PluginException;
/** /**
* Returns true if the plugin is running. * Returns true if the plugin is running.

View File

@@ -0,0 +1,15 @@
package org.briarproject.bramble.api.plugin;
/**
* An exception that indicates an error starting or stopping a {@link Plugin}.
*/
public class PluginException extends Exception {
public PluginException() {
super();
}
public PluginException(Throwable cause) {
super(cause);
}
}

View File

@@ -11,6 +11,7 @@ import org.briarproject.bramble.api.plugin.ConnectionManager;
import org.briarproject.bramble.api.plugin.Plugin; import org.briarproject.bramble.api.plugin.Plugin;
import org.briarproject.bramble.api.plugin.PluginCallback; import org.briarproject.bramble.api.plugin.PluginCallback;
import org.briarproject.bramble.api.plugin.PluginConfig; import org.briarproject.bramble.api.plugin.PluginConfig;
import org.briarproject.bramble.api.plugin.PluginException;
import org.briarproject.bramble.api.plugin.PluginManager; import org.briarproject.bramble.api.plugin.PluginManager;
import org.briarproject.bramble.api.plugin.TransportConnectionReader; import org.briarproject.bramble.api.plugin.TransportConnectionReader;
import org.briarproject.bramble.api.plugin.TransportConnectionWriter; import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
@@ -30,7 +31,6 @@ import org.briarproject.bramble.api.settings.Settings;
import org.briarproject.bramble.api.settings.SettingsManager; import org.briarproject.bramble.api.settings.SettingsManager;
import org.briarproject.bramble.api.ui.UiCallback; import org.briarproject.bramble.api.ui.UiCallback;
import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
@@ -193,24 +193,17 @@ class PluginManagerImpl implements PluginManager, Service {
@Override @Override
public void run() { public void run() {
try { try {
try { long start = System.currentTimeMillis();
long start = System.currentTimeMillis(); plugin.start();
boolean started = plugin.start(); long duration = System.currentTimeMillis() - start;
long duration = System.currentTimeMillis() - start; if (LOG.isLoggable(INFO)) {
if (started) { LOG.info("Starting plugin " + plugin.getId() + " took " +
if (LOG.isLoggable(INFO)) { duration + " ms");
LOG.info("Starting plugin " + plugin.getId() }
+ " took " + duration + " ms"); } catch (PluginException e) {
} if (LOG.isLoggable(WARNING)) {
} else { LOG.warning("Plugin " + plugin.getId() + " did not start");
if (LOG.isLoggable(WARNING)) { LOG.log(WARNING, e.toString(), e);
LOG.warning("Plugin" + plugin.getId()
+ " did not start");
}
}
} catch (IOException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
} }
} finally { } finally {
startLatch.countDown(); startLatch.countDown();
@@ -246,10 +239,13 @@ class PluginManagerImpl implements PluginManager, Service {
+ " took " + duration + " ms"); + " took " + duration + " ms");
} }
} catch (InterruptedException e) { } catch (InterruptedException e) {
LOG.warning("Interrupted while waiting for plugin to start"); LOG.warning("Interrupted while waiting for plugin to stop");
// This task runs on an executor, so don't reset the interrupt // This task runs on an executor, so don't reset the interrupt
} catch (IOException e) { } catch (PluginException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); if (LOG.isLoggable(WARNING)) {
LOG.warning("Plugin " + plugin.getId() + " did not stop");
LOG.log(WARNING, e.toString(), e);
}
} finally { } finally {
stopLatch.countDown(); stopLatch.countDown();
} }

View File

@@ -101,11 +101,10 @@ abstract class TcpPlugin implements DuplexPlugin {
} }
@Override @Override
public boolean start() { public void start() {
if (used.getAndSet(true)) throw new IllegalStateException(); if (used.getAndSet(true)) throw new IllegalStateException();
running = true; running = true;
bind(); bind();
return true;
} }
protected void bind() { protected void bind() {

View File

@@ -9,6 +9,7 @@ import org.briarproject.bramble.api.keyagreement.KeyAgreementListener;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff; import org.briarproject.bramble.api.plugin.Backoff;
import org.briarproject.bramble.api.plugin.PluginException;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin; import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback; import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback;
@@ -99,7 +100,7 @@ class BluetoothPlugin implements DuplexPlugin {
} }
@Override @Override
public boolean start() throws IOException { public void start() throws PluginException {
if (used.getAndSet(true)) throw new IllegalStateException(); if (used.getAndSet(true)) throw new IllegalStateException();
// Initialise the Bluetooth stack // Initialise the Bluetooth stack
try { try {
@@ -108,13 +109,14 @@ class BluetoothPlugin implements DuplexPlugin {
// On Linux the user may need to install libbluetooth-dev // On Linux the user may need to install libbluetooth-dev
if (OsUtils.isLinux()) if (OsUtils.isLinux())
callback.showMessage("BLUETOOTH_INSTALL_LIBS"); callback.showMessage("BLUETOOTH_INSTALL_LIBS");
return false; throw new PluginException(e);
} catch (BluetoothStateException e) {
throw new PluginException(e);
} }
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
LOG.info("Local address " + localDevice.getBluetoothAddress()); LOG.info("Local address " + localDevice.getBluetoothAddress());
running = true; running = true;
bind(); bind();
return true;
} }
private void bind() { private void bind() {

View File

@@ -2,6 +2,7 @@ package org.briarproject.bramble.plugin.file;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.PluginException;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginCallback; import org.briarproject.bramble.api.plugin.simplex.SimplexPluginCallback;
@@ -42,17 +43,24 @@ class RemovableDrivePlugin extends FilePlugin
} }
@Override @Override
public boolean start() throws IOException { public void start() throws PluginException {
if (used.getAndSet(true)) throw new IllegalStateException(); if (used.getAndSet(true)) throw new IllegalStateException();
running = true; running = true;
monitor.start(this); try {
return true; monitor.start(this);
} catch (IOException e) {
throw new PluginException(e);
}
} }
@Override @Override
public void stop() throws IOException { public void stop() throws PluginException {
running = false; running = false;
monitor.stop(); try {
monitor.stop();
} catch (IOException e) {
throw new PluginException(e);
}
} }
@Override @Override

View File

@@ -6,6 +6,7 @@ import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.keyagreement.KeyAgreementListener; import org.briarproject.bramble.api.keyagreement.KeyAgreementListener;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.plugin.PluginException;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.AbstractDuplexTransportConnection; import org.briarproject.bramble.api.plugin.duplex.AbstractDuplexTransportConnection;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin; import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
@@ -68,7 +69,7 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback {
} }
@Override @Override
public boolean start() { public void start() throws PluginException {
if (used.getAndSet(true)) throw new IllegalStateException(); if (used.getAndSet(true)) throw new IllegalStateException();
for (String portName : serialPortList.getPortNames()) { for (String portName : serialPortList.getPortNames()) {
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
@@ -79,12 +80,12 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback {
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
LOG.info("Initialised modem on " + portName); LOG.info("Initialised modem on " + portName);
running = true; running = true;
return true; return;
} catch (IOException e) { } catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
} }
} }
return false; throw new PluginException();
} }
@Override @Override

View File

@@ -14,7 +14,6 @@ import java.util.Map;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
public class ModemPluginTest extends BrambleTestCase { public class ModemPluginTest extends BrambleTestCase {
@@ -49,7 +48,7 @@ public class ModemPluginTest extends BrambleTestCase {
oneOf(modem).start(); oneOf(modem).start();
will(returnValue(true)); will(returnValue(true));
}}); }});
assertTrue(plugin.start()); plugin.start();
context.assertIsSatisfied(); context.assertIsSatisfied();
} }
@@ -88,7 +87,7 @@ public class ModemPluginTest extends BrambleTestCase {
oneOf(modem).dial(NUMBER); oneOf(modem).dial(NUMBER);
will(returnValue(true)); will(returnValue(true));
}}); }});
assertTrue(plugin.start()); plugin.start();
// A connection should be returned // A connection should be returned
assertNotNull(plugin.createConnection(contactId)); assertNotNull(plugin.createConnection(contactId));
context.assertIsSatisfied(); context.assertIsSatisfied();
@@ -129,7 +128,7 @@ public class ModemPluginTest extends BrambleTestCase {
oneOf(modem).dial(NUMBER); oneOf(modem).dial(NUMBER);
will(returnValue(false)); will(returnValue(false));
}}); }});
assertTrue(plugin.start()); plugin.start();
// No connection should be returned // No connection should be returned
assertNull(plugin.createConnection(contactId)); assertNull(plugin.createConnection(contactId));
context.assertIsSatisfied(); context.assertIsSatisfied();
@@ -177,7 +176,7 @@ public class ModemPluginTest extends BrambleTestCase {
oneOf(modem).start(); oneOf(modem).start();
will(returnValue(true)); will(returnValue(true));
}}); }});
assertTrue(plugin.start()); plugin.start();
// No connection should be returned // No connection should be returned
assertNull(plugin.createConnection(contactId)); assertNull(plugin.createConnection(contactId));
context.assertIsSatisfied(); context.assertIsSatisfied();