Merge branch '273-service-exceptions' into 'master'

Services should throw exceptions for startup errors

Fixes #273

See merge request !134
This commit is contained in:
Torsten Grote
2016-04-06 15:51:03 +00:00
12 changed files with 87 additions and 85 deletions

View File

@@ -10,10 +10,10 @@ import android.support.v4.app.NotificationCompat;
import android.support.v4.app.TaskStackBuilder; import android.support.v4.app.TaskStackBuilder;
import org.briarproject.R; import org.briarproject.R;
import org.briarproject.android.contact.ConversationActivity;
import org.briarproject.android.forum.ForumActivity;
import org.briarproject.android.api.AndroidExecutor; import org.briarproject.android.api.AndroidExecutor;
import org.briarproject.android.api.AndroidNotificationManager; import org.briarproject.android.api.AndroidNotificationManager;
import org.briarproject.android.contact.ConversationActivity;
import org.briarproject.android.forum.ForumActivity;
import org.briarproject.api.db.DatabaseExecutor; import org.briarproject.api.db.DatabaseExecutor;
import org.briarproject.api.db.DbException; import org.briarproject.api.db.DbException;
import org.briarproject.api.event.Event; import org.briarproject.api.event.Event;
@@ -22,6 +22,7 @@ import org.briarproject.api.event.MessageValidatedEvent;
import org.briarproject.api.event.SettingsUpdatedEvent; import org.briarproject.api.event.SettingsUpdatedEvent;
import org.briarproject.api.forum.ForumManager; import org.briarproject.api.forum.ForumManager;
import org.briarproject.api.lifecycle.Service; import org.briarproject.api.lifecycle.Service;
import org.briarproject.api.lifecycle.ServiceException;
import org.briarproject.api.messaging.MessagingManager; import org.briarproject.api.messaging.MessagingManager;
import org.briarproject.api.settings.Settings; import org.briarproject.api.settings.Settings;
import org.briarproject.api.settings.SettingsManager; import org.briarproject.api.settings.SettingsManager;
@@ -31,7 +32,10 @@ import org.briarproject.util.StringUtils;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.inject.Inject; import javax.inject.Inject;
@@ -93,37 +97,30 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
} }
@Override @Override
public boolean start() { public void startService() throws ServiceException {
loadSettings(); try {
return true; settings = settingsManager.getSettings(SETTINGS_NAMESPACE);
} } catch (DbException e) {
throw new ServiceException(e);
private void loadSettings() { }
dbExecutor.execute(new Runnable() {
public void run() {
try {
settings = settingsManager.getSettings(SETTINGS_NAMESPACE);
} catch (DbException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
}
}
});
} }
@Override @Override
public boolean stop() { public void stopService() throws ServiceException {
clearNotifications(); Future<Void> f = androidExecutor.submit(new Callable<Void>() {
return true; public Void call() {
}
private void clearNotifications() {
androidExecutor.execute(new Runnable() {
public void run() {
clearPrivateMessageNotification(); clearPrivateMessageNotification();
clearForumPostNotification(); clearForumPostNotification();
return null;
} }
}); });
try {
f.get();
} catch (InterruptedException e) {
throw new ServiceException(e);
} catch (ExecutionException e) {
throw new ServiceException(e);
}
} }
private void clearPrivateMessageNotification() { private void clearPrivateMessageNotification() {
@@ -154,6 +151,19 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
} }
} }
private void loadSettings() {
dbExecutor.execute(new Runnable() {
public void run() {
try {
settings = settingsManager.getSettings(SETTINGS_NAMESPACE);
} catch (DbException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
}
}
});
}
public void showPrivateMessageNotification(final GroupId g) { public void showPrivateMessageNotification(final GroupId g) {
androidExecutor.execute(new Runnable() { androidExecutor.execute(new Runnable() {
public void run() { public void run() {

View File

@@ -19,7 +19,6 @@ import org.briarproject.api.sync.Offer;
import org.briarproject.api.sync.Request; import org.briarproject.api.sync.Request;
import org.briarproject.api.transport.TransportKeys; import org.briarproject.api.transport.TransportKeys;
import java.io.IOException;
import java.util.Collection; import java.util.Collection;
import java.util.Map; import java.util.Map;
@@ -37,7 +36,7 @@ public interface DatabaseComponent {
/** /**
* Waits for any open transactions to finish and closes the database. * Waits for any open transactions to finish and closes the database.
*/ */
void close() throws DbException, IOException; void close() throws DbException;
/** /**
* Starts a new transaction and returns an object representing it. * Starts a new transaction and returns an object representing it.

View File

@@ -3,14 +3,14 @@ package org.briarproject.api.lifecycle;
public interface Service { public interface Service {
/** /**
* Starts the service and returns true if it started successfully. * Starts the service.This method must not be called concurrently with
* This method must not be called concurrently with {@link #stop()}. * {@link #stopService()}.
*/ */
public boolean start(); void startService() throws ServiceException;
/** /**
* Stops the service and returns true if it stopped successfully. * Stops the service. This method must not be called concurrently with
* This method must not be called concurrently with {@link #start()}. * {@link #startService()}.
*/ */
public boolean stop(); void stopService() throws ServiceException;
} }

View File

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

View File

@@ -19,7 +19,6 @@ import org.briarproject.api.sync.MessageStatus;
import org.briarproject.api.sync.ValidationManager.Validity; import org.briarproject.api.sync.ValidationManager.Validity;
import org.briarproject.api.transport.TransportKeys; import org.briarproject.api.transport.TransportKeys;
import java.io.IOException;
import java.util.Collection; import java.util.Collection;
import java.util.Map; import java.util.Map;
@@ -41,7 +40,7 @@ interface Database<T> {
* Prevents new transactions from starting, waits for all current * Prevents new transactions from starting, waits for all current
* transactions to finish, and closes the database. * transactions to finish, and closes the database.
*/ */
void close() throws DbException, IOException; void close() throws DbException;
/** /**
* Starts a new transaction and returns an object representing it. * Starts a new transaction and returns an object representing it.

View File

@@ -50,7 +50,6 @@ import org.briarproject.api.sync.Request;
import org.briarproject.api.sync.ValidationManager.Validity; import org.briarproject.api.sync.ValidationManager.Validity;
import org.briarproject.api.transport.TransportKeys; import org.briarproject.api.transport.TransportKeys;
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;
@@ -101,9 +100,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
} catch (DbException e) { } catch (DbException e) {
if (LOG.isLoggable(WARNING)) if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e); LOG.log(WARNING, e.toString(), e);
} catch (IOException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
} }
} }
}; };
@@ -112,7 +108,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
return reopened; return reopened;
} }
public void close() throws DbException, IOException { public void close() throws DbException {
if (closed.getAndSet(true)) return; if (closed.getAndSet(true)) return;
shutdown.removeShutdownHook(shutdownHandle); shutdown.removeShutdownHook(shutdownHandle);
db.close(); db.close();

View File

@@ -8,8 +8,8 @@ import org.briarproject.api.event.EventBus;
import org.briarproject.api.event.ShutdownEvent; import org.briarproject.api.event.ShutdownEvent;
import org.briarproject.api.lifecycle.LifecycleManager; import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.lifecycle.Service; import org.briarproject.api.lifecycle.Service;
import org.briarproject.api.lifecycle.ServiceException;
import java.io.IOException;
import java.util.List; import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
@@ -101,16 +101,11 @@ class LifecycleManagerImpl implements LifecycleManager {
} }
for (Service s : services) { for (Service s : services) {
start = System.currentTimeMillis(); start = System.currentTimeMillis();
boolean started = s.start(); s.startService();
duration = System.currentTimeMillis() - start; duration = System.currentTimeMillis() - start;
if (!started) {
if (LOG.isLoggable(WARNING))
LOG.warning(s.getClass().getName() + " did not start");
return SERVICE_ERROR;
}
if (LOG.isLoggable(INFO)) { if (LOG.isLoggable(INFO)) {
String name = s.getClass().getName(); LOG.info("Starting " + s.getClass().getName()
LOG.info("Starting " + name + " took " + duration + " ms"); + " took " + duration + " ms");
} }
} }
startupLatch.countDown(); startupLatch.countDown();
@@ -118,6 +113,9 @@ class LifecycleManagerImpl implements LifecycleManager {
} catch (DbException e) { } catch (DbException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
return DB_ERROR; return DB_ERROR;
} catch (ServiceException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
return SERVICE_ERROR;
} finally { } finally {
startStopSemaphore.release(); startStopSemaphore.release();
} }
@@ -134,12 +132,9 @@ class LifecycleManagerImpl implements LifecycleManager {
LOG.info("Stopping services"); LOG.info("Stopping services");
eventBus.broadcast(new ShutdownEvent()); eventBus.broadcast(new ShutdownEvent());
for (Service s : services) { for (Service s : services) {
boolean stopped = s.stop(); s.stopService();
if (LOG.isLoggable(INFO)) { if (LOG.isLoggable(INFO))
String name = s.getClass().getName(); LOG.info("Service stopped: " + s.getClass().getName());
if (stopped) LOG.info("Service stopped: " + name);
else LOG.warning("Service failed to stop: " + name);
}
} }
for (ExecutorService e : executors) e.shutdownNow(); for (ExecutorService e : executors) e.shutdownNow();
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
@@ -149,7 +144,7 @@ class LifecycleManagerImpl implements LifecycleManager {
shutdownLatch.countDown(); shutdownLatch.countDown();
} catch (DbException e) { } catch (DbException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
} catch (IOException e) { } catch (ServiceException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
} finally { } finally {
startStopSemaphore.release(); startStopSemaphore.release();

View File

@@ -8,6 +8,7 @@ import org.briarproject.api.event.TransportDisabledEvent;
import org.briarproject.api.event.TransportEnabledEvent; import org.briarproject.api.event.TransportEnabledEvent;
import org.briarproject.api.lifecycle.IoExecutor; import org.briarproject.api.lifecycle.IoExecutor;
import org.briarproject.api.lifecycle.Service; import org.briarproject.api.lifecycle.Service;
import org.briarproject.api.lifecycle.ServiceException;
import org.briarproject.api.plugins.ConnectionManager; import org.briarproject.api.plugins.ConnectionManager;
import org.briarproject.api.plugins.Plugin; import org.briarproject.api.plugins.Plugin;
import org.briarproject.api.plugins.PluginCallback; import org.briarproject.api.plugins.PluginCallback;
@@ -83,7 +84,7 @@ class PluginManagerImpl implements PluginManager, Service {
} }
@Override @Override
public boolean start() { public void startService() throws ServiceException {
// Instantiate and start the simplex plugins // Instantiate and start the simplex plugins
LOG.info("Starting simplex plugins"); LOG.info("Starting simplex plugins");
Collection<SimplexPluginFactory> sFactories = Collection<SimplexPluginFactory> sFactories =
@@ -103,15 +104,12 @@ class PluginManagerImpl implements PluginManager, Service {
sLatch.await(); sLatch.await();
dLatch.await(); dLatch.await();
} catch (InterruptedException e) { } catch (InterruptedException e) {
LOG.warning("Interrupted while starting plugins"); throw new ServiceException(e);
Thread.currentThread().interrupt();
return false;
} }
return true;
} }
@Override @Override
public boolean stop() { public void stopService() throws ServiceException {
// Stop the poller // Stop the poller
LOG.info("Stopping poller"); LOG.info("Stopping poller");
poller.stop(); poller.stop();
@@ -131,11 +129,8 @@ class PluginManagerImpl implements PluginManager, Service {
try { try {
latch.await(); latch.await();
} catch (InterruptedException e) { } catch (InterruptedException e) {
LOG.warning("Interrupted while stopping plugins"); throw new ServiceException(e);
Thread.currentThread().interrupt();
return false;
} }
return true;
} }
public Plugin getPlugin(TransportId t) { public Plugin getPlugin(TransportId t) {

View File

@@ -57,14 +57,12 @@ class ValidationManagerImpl implements ValidationManager, Service,
} }
@Override @Override
public boolean start() { public void startService() {
for (ClientId c : validators.keySet()) getMessagesToValidate(c); for (ClientId c : validators.keySet()) getMessagesToValidate(c);
return true;
} }
@Override @Override
public boolean stop() { public void stopService() {
return true;
} }
@Override @Override

View File

@@ -14,6 +14,7 @@ import org.briarproject.api.event.ContactStatusChangedEvent;
import org.briarproject.api.event.Event; import org.briarproject.api.event.Event;
import org.briarproject.api.event.EventListener; import org.briarproject.api.event.EventListener;
import org.briarproject.api.lifecycle.Service; import org.briarproject.api.lifecycle.Service;
import org.briarproject.api.lifecycle.ServiceException;
import org.briarproject.api.plugins.PluginConfig; import org.briarproject.api.plugins.PluginConfig;
import org.briarproject.api.plugins.duplex.DuplexPluginFactory; import org.briarproject.api.plugins.duplex.DuplexPluginFactory;
import org.briarproject.api.plugins.simplex.SimplexPluginFactory; import org.briarproject.api.plugins.simplex.SimplexPluginFactory;
@@ -32,7 +33,6 @@ import java.util.logging.Logger;
import javax.inject.Inject; import javax.inject.Inject;
import static java.util.logging.Level.INFO; import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
class KeyManagerImpl implements KeyManager, Service, EventListener { class KeyManagerImpl implements KeyManager, Service, EventListener {
@@ -64,7 +64,7 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
} }
@Override @Override
public boolean start() { public void startService() throws ServiceException {
Map<TransportId, Integer> transports = Map<TransportId, Integer> transports =
new HashMap<TransportId, Integer>(); new HashMap<TransportId, Integer>();
for (SimplexPluginFactory f : pluginConfig.getSimplexFactories()) for (SimplexPluginFactory f : pluginConfig.getSimplexFactories())
@@ -89,15 +89,12 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
db.endTransaction(txn); db.endTransaction(txn);
} }
} catch (DbException e) { } catch (DbException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); throw new ServiceException(e);
return false;
} }
return true;
} }
@Override @Override
public boolean stop() { public void stopService() {
return true;
} }
public void addContact(Transaction txn, ContactId c, SecretKey master, public void addContact(Transaction txn, ContactId c, SecretKey master,

View File

@@ -23,8 +23,6 @@ import java.util.Arrays;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import static org.junit.Assert.assertTrue;
public class PluginManagerImplTest extends BriarTestCase { public class PluginManagerImplTest extends BriarTestCase {
@Test @Test
@@ -117,8 +115,8 @@ public class PluginManagerImplTest extends BriarTestCase {
transportPropertyManager, uiCallback); transportPropertyManager, uiCallback);
// Two plugins should be started and stopped // Two plugins should be started and stopped
assertTrue(p.start()); p.startService();
assertTrue(p.stop()); p.stopService();
context.assertIsSatisfied(); context.assertIsSatisfied();
} }

View File

@@ -112,7 +112,7 @@ public class ValidationManagerImplTest extends BriarTestCase {
cryptoExecutor); cryptoExecutor);
vm.registerMessageValidator(clientId, validator); vm.registerMessageValidator(clientId, validator);
vm.registerIncomingMessageHook(clientId, hook); vm.registerIncomingMessageHook(clientId, hook);
vm.start(); vm.startService();
context.assertIsSatisfied(); context.assertIsSatisfied();
} }
@@ -166,7 +166,7 @@ public class ValidationManagerImplTest extends BriarTestCase {
cryptoExecutor); cryptoExecutor);
vm.registerMessageValidator(clientId, validator); vm.registerMessageValidator(clientId, validator);
vm.registerIncomingMessageHook(clientId, hook); vm.registerIncomingMessageHook(clientId, hook);
vm.start(); vm.startService();
context.assertIsSatisfied(); context.assertIsSatisfied();
} }
@@ -223,7 +223,7 @@ public class ValidationManagerImplTest extends BriarTestCase {
cryptoExecutor); cryptoExecutor);
vm.registerMessageValidator(clientId, validator); vm.registerMessageValidator(clientId, validator);
vm.registerIncomingMessageHook(clientId, hook); vm.registerIncomingMessageHook(clientId, hook);
vm.start(); vm.startService();
context.assertIsSatisfied(); context.assertIsSatisfied();
} }