Allow Plugin.start() to fail gracefully; removed unnecessary locking.

This commit is contained in:
akwizgran
2012-11-24 10:50:20 +00:00
parent 66c155e41c
commit d90b4f4bc5
8 changed files with 71 additions and 142 deletions

View File

@@ -14,8 +14,8 @@ public interface Plugin {
/** Returns a label for looking up the plugin's translated name. */ /** Returns a label for looking up the plugin's translated name. */
String getName(); String getName();
/** Starts the plugin. */ /** Starts the plugin and returns true if it started successfully. */
void start() throws IOException; boolean start() throws IOException;
/** Stops the plugin. */ /** Stops the plugin. */
void stop() throws IOException; void stop() throws IOException;

View File

@@ -53,8 +53,8 @@ class BluetoothPlugin implements DuplexPlugin {
private final Object discoveryLock = new Object(); private final Object discoveryLock = new Object();
private final ScheduledExecutorService scheduler; private final ScheduledExecutorService scheduler;
private boolean running = false; // Locking: this private volatile boolean running = false;
private StreamConnectionNotifier socket = null; // Locking: this private volatile StreamConnectionNotifier socket = null;
// Non-null if running has ever been true // Non-null if running has ever been true
private volatile LocalDevice localDevice = null; private volatile LocalDevice localDevice = null;
@@ -76,7 +76,7 @@ class BluetoothPlugin implements DuplexPlugin {
return "BLUETOOTH_PLUGIN_NAME"; return "BLUETOOTH_PLUGIN_NAME";
} }
public void start() throws IOException { public boolean start() throws IOException {
// Initialise the Bluetooth stack // Initialise the Bluetooth stack
try { try {
localDevice = LocalDevice.getLocalDevice(); localDevice = LocalDevice.getLocalDevice();
@@ -84,24 +84,21 @@ 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");
throw new IOException(e.toString()); return false;
} }
if(LOG.isLoggable(INFO)) if(LOG.isLoggable(INFO))
LOG.info("Local address " + localDevice.getBluetoothAddress()); LOG.info("Local address " + localDevice.getBluetoothAddress());
synchronized(this) { running = true;
running = true;
}
pluginExecutor.execute(new Runnable() { pluginExecutor.execute(new Runnable() {
public void run() { public void run() {
bind(); bind();
} }
}); });
return true;
} }
private void bind() { private void bind() {
synchronized(this) { if(!running) return;
if(!running) return;
}
// Advertise the Bluetooth address to contacts // Advertise the Bluetooth address to contacts
TransportProperties p = new TransportProperties(); TransportProperties p = new TransportProperties();
p.put("address", localDevice.getBluetoothAddress()); p.put("address", localDevice.getBluetoothAddress());
@@ -114,13 +111,11 @@ class BluetoothPlugin implements DuplexPlugin {
if(LOG.isLoggable(WARNING)) LOG.warning(e.toString()); if(LOG.isLoggable(WARNING)) LOG.warning(e.toString());
return; return;
} }
synchronized(this) { if(!running) {
if(!running) { tryToClose(scn);
tryToClose(scn); return;
return;
}
socket = scn;
} }
socket = scn;
acceptContactConnections(scn); acceptContactConnections(scn);
} }
@@ -155,20 +150,13 @@ class BluetoothPlugin implements DuplexPlugin {
BluetoothTransportConnection conn = BluetoothTransportConnection conn =
new BluetoothTransportConnection(s); new BluetoothTransportConnection(s);
callback.incomingConnectionCreated(conn); callback.incomingConnectionCreated(conn);
synchronized(this) { if(!running) return;
if(!running) return;
}
} }
} }
public void stop() { public void stop() {
synchronized(this) { running = false;
running = false; if(socket != null) tryToClose(socket);
if(socket != null) {
tryToClose(socket);
socket = null;
}
}
scheduler.shutdownNow(); scheduler.shutdownNow();
} }
@@ -181,9 +169,7 @@ class BluetoothPlugin implements DuplexPlugin {
} }
public void poll(final Collection<ContactId> connected) { public void poll(final Collection<ContactId> connected) {
synchronized(this) { if(!running) return;
if(!running) return;
}
// Try to connect to known devices in parallel // Try to connect to known devices in parallel
Map<ContactId, TransportProperties> remote = Map<ContactId, TransportProperties> remote =
callback.getRemoteProperties(); callback.getRemoteProperties();
@@ -195,9 +181,7 @@ class BluetoothPlugin implements DuplexPlugin {
if(address != null && uuid != null) { if(address != null && uuid != null) {
pluginExecutor.execute(new Runnable() { pluginExecutor.execute(new Runnable() {
public void run() { public void run() {
synchronized(BluetoothPlugin.this) { if(!running) return;
if(!running) return;
}
String url = makeUrl(address, uuid); String url = makeUrl(address, uuid);
DuplexTransportConnection conn = connect(url); DuplexTransportConnection conn = connect(url);
if(conn != null) if(conn != null)
@@ -219,9 +203,7 @@ class BluetoothPlugin implements DuplexPlugin {
} }
public DuplexTransportConnection createConnection(ContactId c) { public DuplexTransportConnection createConnection(ContactId c) {
synchronized(this) { if(!running) return null;
if(!running) return null;
}
TransportProperties p = callback.getRemoteProperties().get(c); TransportProperties p = callback.getRemoteProperties().get(c);
if(p == null) return null; if(p == null) return null;
String address = p.get("address"); String address = p.get("address");
@@ -237,9 +219,7 @@ class BluetoothPlugin implements DuplexPlugin {
public DuplexTransportConnection sendInvitation(PseudoRandom r, public DuplexTransportConnection sendInvitation(PseudoRandom r,
long timeout) { long timeout) {
synchronized(this) { if(!running) return null;
if(!running) return null;
}
// Use the same pseudo-random UUID as the contact // Use the same pseudo-random UUID as the contact
String uuid = generateUuid(r.nextBytes(16)); String uuid = generateUuid(r.nextBytes(16));
// Discover nearby devices and connect to any with the right UUID // Discover nearby devices and connect to any with the right UUID
@@ -265,9 +245,7 @@ class BluetoothPlugin implements DuplexPlugin {
return null; return null;
} }
} }
synchronized(this) { if(!running) return null;
if(!running) return null;
}
} }
if(url == null) return null; if(url == null) return null;
return connect(url); return connect(url);
@@ -280,9 +258,7 @@ class BluetoothPlugin implements DuplexPlugin {
public DuplexTransportConnection acceptInvitation(PseudoRandom r, public DuplexTransportConnection acceptInvitation(PseudoRandom r,
long timeout) { long timeout) {
synchronized(this) { if(!running) return null;
if(!running) return null;
}
// Use the same pseudo-random UUID as the contact // Use the same pseudo-random UUID as the contact
String uuid = generateUuid(r.nextBytes(16)); String uuid = generateUuid(r.nextBytes(16));
String url = makeUrl("localhost", uuid); String url = makeUrl("localhost", uuid);
@@ -296,11 +272,9 @@ class BluetoothPlugin implements DuplexPlugin {
if(LOG.isLoggable(WARNING)) LOG.warning(e.toString()); if(LOG.isLoggable(WARNING)) LOG.warning(e.toString());
return null; return null;
} }
synchronized(this) { if(!running) {
if(!running) { tryToClose(scn);
tryToClose(scn); return null;
return null;
}
} }
// Close the socket when the invitation times out // Close the socket when the invitation times out
Runnable close = new Runnable() { Runnable close = new Runnable() {
@@ -324,9 +298,7 @@ class BluetoothPlugin implements DuplexPlugin {
private void makeDeviceDiscoverable() { private void makeDeviceDiscoverable() {
// Try to make the device discoverable (requires root on Linux) // Try to make the device discoverable (requires root on Linux)
synchronized(this) { if(!running) return;
if(!running) return;
}
try { try {
localDevice.setDiscoverable(GIAC); localDevice.setDiscoverable(GIAC);
} catch(BluetoothStateException e) { } catch(BluetoothStateException e) {

View File

@@ -61,8 +61,8 @@ class DroidtoothPlugin implements DuplexPlugin {
private final DuplexPluginCallback callback; private final DuplexPluginCallback callback;
private final long pollingInterval; private final long pollingInterval;
private boolean running = false; // Locking: this private volatile boolean running = false;
private BluetoothServerSocket socket = null; // Locking: this private volatile BluetoothServerSocket socket = null;
// Non-null if running has ever been true // Non-null if running has ever been true
private volatile BluetoothAdapter adapter = null; private volatile BluetoothAdapter adapter = null;
@@ -86,7 +86,7 @@ class DroidtoothPlugin implements DuplexPlugin {
return "BLUETOOTH_PLUGIN_NAME"; return "BLUETOOTH_PLUGIN_NAME";
} }
public void start() throws IOException { public boolean start() throws IOException {
// 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
try { try {
@@ -100,21 +100,18 @@ class DroidtoothPlugin implements DuplexPlugin {
} catch(ExecutionException e) { } catch(ExecutionException e) {
throw new IOException(e.toString()); throw new IOException(e.toString());
} }
if(adapter == null) throw new IOException(); // Bluetooth not supported if(adapter == null) return false; // Bluetooth not supported
synchronized(this) { running = true;
running = true;
}
pluginExecutor.execute(new Runnable() { pluginExecutor.execute(new Runnable() {
public void run() { public void run() {
bind(); bind();
} }
}); });
return true;
} }
private void bind() { private void bind() {
synchronized(this) { if(!running) return;
if(!running) return;
}
if(!enableBluetooth()) { if(!enableBluetooth()) {
if(LOG.isLoggable(INFO)) LOG.info("Could not enable Bluetooth"); if(LOG.isLoggable(INFO)) LOG.info("Could not enable Bluetooth");
return; return;
@@ -133,20 +130,16 @@ class DroidtoothPlugin implements DuplexPlugin {
if(LOG.isLoggable(WARNING)) LOG.warning(e.toString()); if(LOG.isLoggable(WARNING)) LOG.warning(e.toString());
return; return;
} }
synchronized(this) { if(!running) {
if(!running) { tryToClose(ss);
tryToClose(ss); return;
return;
}
socket = ss;
} }
socket = ss;
acceptContactConnections(ss); acceptContactConnections(ss);
} }
private boolean enableBluetooth() { private boolean enableBluetooth() {
synchronized(this) { if(!running) return false;
if(!running) return false;
}
if(adapter.isEnabled()) return true; if(adapter.isEnabled()) return true;
// Try to enable the adapter and wait for the result // Try to enable the adapter and wait for the result
IntentFilter filter = new IntentFilter(ACTION_STATE_CHANGED); IntentFilter filter = new IntentFilter(ACTION_STATE_CHANGED);
@@ -190,20 +183,13 @@ class DroidtoothPlugin implements DuplexPlugin {
DroidtoothTransportConnection conn = DroidtoothTransportConnection conn =
new DroidtoothTransportConnection(s); new DroidtoothTransportConnection(s);
callback.incomingConnectionCreated(conn); callback.incomingConnectionCreated(conn);
synchronized(this) { if(!running) return;
if(!running) return;
}
} }
} }
public void stop() throws IOException { public void stop() throws IOException {
synchronized(this) { running = false;
running = false; if(socket != null) tryToClose(socket);
if(socket != null) {
tryToClose(socket);
socket = null;
}
}
} }
public boolean shouldPoll() { public boolean shouldPoll() {
@@ -215,9 +201,7 @@ class DroidtoothPlugin implements DuplexPlugin {
} }
public void poll(Collection<ContactId> connected) { public void poll(Collection<ContactId> connected) {
synchronized(this) { if(!running) return;
if(!running) return;
}
// Try to connect to known devices in parallel // Try to connect to known devices in parallel
Map<ContactId, TransportProperties> remote = Map<ContactId, TransportProperties> remote =
callback.getRemoteProperties(); callback.getRemoteProperties();
@@ -229,9 +213,7 @@ class DroidtoothPlugin implements DuplexPlugin {
if(address != null && uuid != null) { if(address != null && uuid != null) {
pluginExecutor.execute(new Runnable() { pluginExecutor.execute(new Runnable() {
public void run() { public void run() {
synchronized(DroidtoothPlugin.this) { if(!running) return;
if(!running) return;
}
DuplexTransportConnection conn = connect(address, uuid); DuplexTransportConnection conn = connect(address, uuid);
if(conn != null) if(conn != null)
callback.outgoingConnectionCreated(c, conn); callback.outgoingConnectionCreated(c, conn);
@@ -269,9 +251,7 @@ class DroidtoothPlugin implements DuplexPlugin {
} }
public DuplexTransportConnection createConnection(ContactId c) { public DuplexTransportConnection createConnection(ContactId c) {
synchronized(this) { if(!running) return null;
if(!running) return null;
}
TransportProperties p = callback.getRemoteProperties().get(c); TransportProperties p = callback.getRemoteProperties().get(c);
if(p == null) return null; if(p == null) return null;
String address = p.get("address"); String address = p.get("address");
@@ -286,9 +266,7 @@ class DroidtoothPlugin implements DuplexPlugin {
public DuplexTransportConnection sendInvitation(PseudoRandom r, public DuplexTransportConnection sendInvitation(PseudoRandom r,
long timeout) { long timeout) {
synchronized(this) { if(!running) return null;
if(!running) return null;
}
// Use the same pseudo-random UUID as the contact // Use the same pseudo-random UUID as the contact
String uuid = UUID.nameUUIDFromBytes(r.nextBytes(16)).toString(); String uuid = UUID.nameUUIDFromBytes(r.nextBytes(16)).toString();
// Register to receive Bluetooth discovery intents // Register to receive Bluetooth discovery intents
@@ -311,9 +289,7 @@ class DroidtoothPlugin implements DuplexPlugin {
public DuplexTransportConnection acceptInvitation(PseudoRandom r, public DuplexTransportConnection acceptInvitation(PseudoRandom r,
long timeout) { long timeout) {
synchronized(this) { if(!running) return null;
if(!running) return null;
}
// Use the same pseudo-random UUID as the contact // Use the same pseudo-random UUID as the contact
UUID uuid = UUID.nameUUIDFromBytes(r.nextBytes(16)); UUID uuid = UUID.nameUUIDFromBytes(r.nextBytes(16));
// Bind a new server socket to accept the invitation connection // Bind a new server socket to accept the invitation connection
@@ -394,9 +370,7 @@ class DroidtoothPlugin implements DuplexPlugin {
for(final String address : addresses) { for(final String address : addresses) {
pluginExecutor.execute(new Runnable() { pluginExecutor.execute(new Runnable() {
public void run() { public void run() {
synchronized(DroidtoothPlugin.this) { if(!running) return;
if(!running) return;
}
DuplexTransportConnection conn = connect(address, uuid); DuplexTransportConnection conn = connect(address, uuid);
if(conn != null) { if(conn != null) {
connection = conn; connection = conn;

View File

@@ -47,9 +47,10 @@ implements RemovableDriveMonitor.Callback {
return "REMOVABLE_DRIVE_PLUGIN_NAME"; return "REMOVABLE_DRIVE_PLUGIN_NAME";
} }
public void start() throws IOException { public boolean start() throws IOException {
running = true; running = true;
monitor.start(this); monitor.start(this);
return true;
} }
public void stop() throws IOException { public void stop() throws IOException {

View File

@@ -43,8 +43,9 @@ class ModemPlugin implements DuplexPlugin {
return "MODEM_PLUGIN_NAME"; return "MODEM_PLUGIN_NAME";
} }
public void start() throws IOException { public boolean start() throws IOException {
// FIXME // FIXME
return false;
} }
public void stop() throws IOException { public void stop() throws IOException {

View File

@@ -110,9 +110,7 @@ class LanTcpPlugin extends TcpPlugin {
public DuplexTransportConnection sendInvitation(PseudoRandom r, public DuplexTransportConnection sendInvitation(PseudoRandom r,
long timeout) { long timeout) {
synchronized(this) { if(!running) return null;
if(!running) return null;
}
// Use the invitation code to choose the group address and port // Use the invitation code to choose the group address and port
InetSocketAddress mcast = chooseMulticastGroup(r); InetSocketAddress mcast = chooseMulticastGroup(r);
// Bind a multicast socket for receiving packets // Bind a multicast socket for receiving packets
@@ -157,9 +155,7 @@ class LanTcpPlugin extends TcpPlugin {
break; break;
} }
now = System.currentTimeMillis(); now = System.currentTimeMillis();
synchronized(this) { if(!running) return null;
if(!running) return null;
}
} }
if(LOG.isLoggable(INFO)) if(LOG.isLoggable(INFO))
LOG.info("Timeout while sending invitation"); LOG.info("Timeout while sending invitation");
@@ -242,9 +238,7 @@ class LanTcpPlugin extends TcpPlugin {
public DuplexTransportConnection acceptInvitation(PseudoRandom r, public DuplexTransportConnection acceptInvitation(PseudoRandom r,
long timeout) { long timeout) {
synchronized(this) { if(!running) return null;
if(!running) return null;
}
// Use the invitation code to choose the group address and port // Use the invitation code to choose the group address and port
InetSocketAddress mcast = chooseMulticastGroup(r); InetSocketAddress mcast = chooseMulticastGroup(r);
// Bind a TCP socket for receiving connections // Bind a TCP socket for receiving connections
@@ -299,9 +293,7 @@ class LanTcpPlugin extends TcpPlugin {
interval += 1000; interval += 1000;
} }
} }
synchronized(this) { if(!running) return null;
if(!running) return null;
}
} }
if(LOG.isLoggable(INFO)) if(LOG.isLoggable(INFO))
LOG.info("Timeout while accepting invitation"); LOG.info("Timeout while accepting invitation");

View File

@@ -32,8 +32,8 @@ abstract class TcpPlugin implements DuplexPlugin {
protected final DuplexPluginCallback callback; protected final DuplexPluginCallback callback;
protected final long pollingInterval; protected final long pollingInterval;
protected boolean running = false; // Locking: this protected volatile boolean running = false;
private ServerSocket socket = null; // Locking: this private volatile ServerSocket socket = null;
/** /**
* Returns zero or more socket addresses on which the plugin should listen, * Returns zero or more socket addresses on which the plugin should listen,
@@ -48,15 +48,14 @@ abstract class TcpPlugin implements DuplexPlugin {
this.pollingInterval = pollingInterval; this.pollingInterval = pollingInterval;
} }
public void start() throws IOException { public boolean start() throws IOException {
synchronized(this) { running = true;
running = true;
}
pluginExecutor.execute(new Runnable() { pluginExecutor.execute(new Runnable() {
public void run() { public void run() {
bind(); bind();
} }
}); });
return true;
} }
private void bind() { private void bind() {
@@ -83,13 +82,11 @@ abstract class TcpPlugin implements DuplexPlugin {
if(LOG.isLoggable(INFO)) LOG.info("Could not bind server socket"); if(LOG.isLoggable(INFO)) LOG.info("Could not bind server socket");
return; return;
} }
synchronized(this) { if(!running) {
if(!running) { tryToClose(ss);
tryToClose(ss); return;
return;
}
socket = ss;
} }
socket = ss;
if(LOG.isLoggable(INFO)) { if(LOG.isLoggable(INFO)) {
String addr = ss.getInetAddress().getHostAddress(); String addr = ss.getInetAddress().getHostAddress();
int port = ss.getLocalPort(); int port = ss.getLocalPort();
@@ -129,18 +126,13 @@ abstract class TcpPlugin implements DuplexPlugin {
} }
TcpTransportConnection conn = new TcpTransportConnection(s); TcpTransportConnection conn = new TcpTransportConnection(s);
callback.incomingConnectionCreated(conn); callback.incomingConnectionCreated(conn);
synchronized(this) { if(!running) return;
if(!running) return;
}
} }
} }
public synchronized void stop() throws IOException { public void stop() throws IOException {
running = false; running = false;
if(socket != null) { if(socket != null) tryToClose(socket);
tryToClose(socket);
socket = null;
}
} }
public boolean shouldPoll() { public boolean shouldPoll() {
@@ -152,9 +144,7 @@ abstract class TcpPlugin implements DuplexPlugin {
} }
public void poll(Collection<ContactId> connected) { public void poll(Collection<ContactId> connected) {
synchronized(this) { if(!running) return;
if(!running) return;
}
Map<ContactId, TransportProperties> remote = Map<ContactId, TransportProperties> remote =
callback.getRemoteProperties(); callback.getRemoteProperties();
for(final ContactId c : remote.keySet()) { for(final ContactId c : remote.keySet()) {
@@ -173,9 +163,7 @@ abstract class TcpPlugin implements DuplexPlugin {
} }
public DuplexTransportConnection createConnection(ContactId c) { public DuplexTransportConnection createConnection(ContactId c) {
synchronized(this) { if(!running) return null;
if(!running) return null;
}
SocketAddress addr = getRemoteSocketAddress(c); SocketAddress addr = getRemoteSocketAddress(c);
Socket s = new Socket(); Socket s = new Socket();
if(addr == null || s == null) return null; if(addr == null || s == null) return null;

View File

@@ -66,7 +66,7 @@ class TorPlugin implements DuplexPlugin {
return "TOR_PLUGIN_NAME"; return "TOR_PLUGIN_NAME";
} }
public void start() throws IOException { public boolean start() throws IOException {
synchronized(this) { synchronized(this) {
running = true; running = true;
} }
@@ -75,6 +75,7 @@ class TorPlugin implements DuplexPlugin {
bind(); bind();
} }
}); });
return true;
} }
private void bind() { private void bind() {