diff --git a/briar-android/src/org/briarproject/android/BriarService.java b/briar-android/src/org/briarproject/android/BriarService.java index ea17bfa2f5363e25e7e5ce70ca2ca7e9e55ecc73..2e606f42eb79e6fd1b3ff21668120f7a8143c1cb 100644 --- a/briar-android/src/org/briarproject/android/BriarService.java +++ b/briar-android/src/org/briarproject/android/BriarService.java @@ -4,6 +4,8 @@ import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP; import static java.util.logging.Level.WARNING; +import static org.briarproject.api.lifecycle.LifecycleManager.StartResult.ALREADY_RUNNING; +import static org.briarproject.api.lifecycle.LifecycleManager.StartResult.SUCCESS; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; @@ -24,6 +26,7 @@ import org.briarproject.api.event.Event; import org.briarproject.api.event.EventListener; import org.briarproject.api.event.MessageAddedEvent; import org.briarproject.api.lifecycle.LifecycleManager; +import org.briarproject.api.lifecycle.LifecycleManager.StartResult; import org.briarproject.api.messaging.GroupId; import roboguice.service.RoboService; @@ -87,11 +90,16 @@ public class BriarService extends RoboService implements EventListener { new Thread() { @Override public void run() { - if(lifecycleManager.startServices()) { + StartResult result = lifecycleManager.startServices(); + if(result == SUCCESS) { db.addListener(BriarService.this); started = true; + } else if(result == ALREADY_RUNNING) { + LOG.info("Already running"); + stopSelf(); } else { - LOG.info("Startup failed"); + if(LOG.isLoggable(WARNING)) + LOG.warning("Startup failed: " + result); showStartupFailureNotification(); stopSelf(); } diff --git a/briar-api/src/org/briarproject/api/lifecycle/LifecycleManager.java b/briar-api/src/org/briarproject/api/lifecycle/LifecycleManager.java index 1df494ddbc7f9490c99a9c7e584588ac8d8b97ec..5f75237c25e080ed40cdfc0614cdb4107d345fe9 100644 --- a/briar-api/src/org/briarproject/api/lifecycle/LifecycleManager.java +++ b/briar-api/src/org/briarproject/api/lifecycle/LifecycleManager.java @@ -2,8 +2,17 @@ package org.briarproject.api.lifecycle; import java.util.concurrent.ExecutorService; +/** + * Manages the lifecycle of the app, starting and stopping {@link Service + * Services}, shutting down {@link java.util.concurrent.ExecutorService + * ExecutorServices}, and opening and closing the {@link + * org.briarproject.api.db.DatabaseComponent DatabaseComponent}. + */ public interface LifecycleManager { + /** The result of calling {@link LifecycleManager#startServices()}. */ + enum StartResult { ALREADY_RUNNING, DB_ERROR, SERVICE_ERROR, SUCCESS } + /** Registers a {@link Service} to be started and stopped. */ public void register(Service s); @@ -14,27 +23,37 @@ public interface LifecycleManager { public void registerForShutdown(ExecutorService e); /** - * Starts any registered {@link Service}s and returns true if all services - * started successfully. + * Starts any registered {@link Service Services} and opens the {@link + * org.briarproject.api.db.DatabaseComponent DatabaseComponent}. */ - public boolean startServices(); + public StartResult startServices(); /** - * Stops any registered {@link Service}s and shuts down any registered - * {@link java.util.concurrent.ExecutorService ExecutorService}s. + * Stops any registered {@link Service Services}, shuts down any + * registered {@link java.util.concurrent.ExecutorService ExecutorServices}, + * and closes the {@link org.briarproject.api.db.DatabaseComponent + * DatabaseComponent}. */ public void stopServices(); - /** Waits for the database to be opened before returning. */ + /** + * Waits for the {@link org.briarproject.api.db.DatabaseComponent + * DatabaseComponent} to be opened before returning. + */ public void waitForDatabase() throws InterruptedException; - /** Waits for all registered {@link Service}s to start before returning. */ + /** + * Waits for the {@link org.briarproject.api.db.DatabaseComponent + * DatabaseComponent} to be opened and all registered {@link Service + * Services} to start before returning. + */ public void waitForStartup() throws InterruptedException; /** - * Waits for all registered {@link Service}s to stop and all registered - * {@link java.util.concurrent.ExecutorService ExecutorService}s to shut - * down before returning. + * Waits for all registered {@link Service Services} to stop, all + * registered {@link java.util.concurrent.ExecutorService ExecutorServices} + * to shut down, and the {@link org.briarproject.api.db.DatabaseComponent + * DatabaseComponent} to be closed before returning. */ public void waitForShutdown() throws InterruptedException; } \ No newline at end of file diff --git a/briar-core/src/org/briarproject/lifecycle/LifecycleManagerImpl.java b/briar-core/src/org/briarproject/lifecycle/LifecycleManagerImpl.java index 15d0375fbd0fcf35f0e1c847f27d71ab2f1618d4..e6ff2c9780cb1ed18666dbb6f1f9e81ff6895324 100644 --- a/briar-core/src/org/briarproject/lifecycle/LifecycleManagerImpl.java +++ b/briar-core/src/org/briarproject/lifecycle/LifecycleManagerImpl.java @@ -2,12 +2,17 @@ package org.briarproject.lifecycle; import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; +import static org.briarproject.api.lifecycle.LifecycleManager.StartResult.ALREADY_RUNNING; +import static org.briarproject.api.lifecycle.LifecycleManager.StartResult.DB_ERROR; +import static org.briarproject.api.lifecycle.LifecycleManager.StartResult.SERVICE_ERROR; +import static org.briarproject.api.lifecycle.LifecycleManager.StartResult.SUCCESS; import java.io.IOException; import java.util.Collection; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; +import java.util.concurrent.Semaphore; import java.util.logging.Logger; import javax.inject.Inject; @@ -27,6 +32,7 @@ class LifecycleManagerImpl implements LifecycleManager { private final DatabaseComponent db; private final Collection<Service> services; private final Collection<ExecutorService> executors; + private final Semaphore startStopSemaphore = new Semaphore(1); private final CountDownLatch dbLatch = new CountDownLatch(1); private final CountDownLatch startupLatch = new CountDownLatch(1); private final CountDownLatch shutdownLatch = new CountDownLatch(1); @@ -51,12 +57,16 @@ class LifecycleManagerImpl implements LifecycleManager { executors.add(e); } - public boolean startServices() { + public StartResult startServices() { + if(!startStopSemaphore.tryAcquire()) { + LOG.info("Already starting or stopping"); + return ALREADY_RUNNING; + } try { - LOG.info("Starting"); - long start = clock.currentTimeMillis(); + LOG.info("Starting services"); + long now = clock.currentTimeMillis(); boolean reopened = db.open(); - long duration = clock.currentTimeMillis() - start; + long duration = clock.currentTimeMillis() - now; if(LOG.isLoggable(INFO)) { if(reopened) LOG.info("Reopening database took " + duration + " ms"); @@ -64,15 +74,15 @@ class LifecycleManagerImpl implements LifecycleManager { } dbLatch.countDown(); for(Service s : services) { - start = clock.currentTimeMillis(); + now = clock.currentTimeMillis(); boolean started = s.start(); - duration = clock.currentTimeMillis() - start; + duration = clock.currentTimeMillis() - now; if(!started) { if(LOG.isLoggable(WARNING)) { String name = s.getClass().getName(); LOG.warning(name + " did not start"); } - return false; + return SERVICE_ERROR; } if(LOG.isLoggable(INFO)) { String name = s.getClass().getName(); @@ -80,19 +90,27 @@ class LifecycleManagerImpl implements LifecycleManager { } } startupLatch.countDown(); - return true; + return SUCCESS; } catch(DbException e) { if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - return false; + return DB_ERROR; } catch(IOException e) { if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - return false; + return DB_ERROR; + } finally { + startStopSemaphore.release(); } } public void stopServices() { try { - LOG.info("Shutting down"); + startStopSemaphore.acquire(); + } catch(InterruptedException e) { + LOG.warning("Interrupted while waiting to stop services"); + return; + } + try { + LOG.info("Stopping services"); for(Service s : services) { boolean stopped = s.stop(); if(LOG.isLoggable(INFO)) { @@ -111,6 +129,8 @@ class LifecycleManagerImpl implements LifecycleManager { if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); } catch(IOException e) { if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); + } finally { + startStopSemaphore.release(); } } diff --git a/briar-tests/src/org/briarproject/TestLifecycleModule.java b/briar-tests/src/org/briarproject/TestLifecycleModule.java index 5f91d0ba6b58d58936043d8a52d5ef1fdf7be0cb..becccd4523a35d4834682595750ccf81b05a698b 100644 --- a/briar-tests/src/org/briarproject/TestLifecycleModule.java +++ b/briar-tests/src/org/briarproject/TestLifecycleModule.java @@ -17,7 +17,7 @@ public class TestLifecycleModule extends AbstractModule { public void registerForShutdown(ExecutorService e) {} - public boolean startServices() { return true; } + public StartResult startServices() { return StartResult.SUCCESS; } public void stopServices() {}