Merge branch 'poller-refactoring' into 'master'

Poller refactoring, replace Timer with ScheduledExecutorService

* Replace Timer with ScheduledExecutorService (closes #258)
* Move automatic connection logic from PluginManager to Poller
* Reschedule polling when connections are opened or closed, making the poller more responsive to reductions in the polling interval


See merge request !180
This commit is contained in:
akwizgran
2016-05-11 14:45:50 +00:00
30 changed files with 876 additions and 648 deletions

View File

@@ -42,6 +42,7 @@ import org.briarproject.introduction.MessageSender;
import org.briarproject.lifecycle.LifecycleModule; import org.briarproject.lifecycle.LifecycleModule;
import org.briarproject.properties.PropertiesModule; import org.briarproject.properties.PropertiesModule;
import org.briarproject.sync.SyncModule; import org.briarproject.sync.SyncModule;
import org.briarproject.system.SystemModule;
import org.briarproject.transport.TransportModule; import org.briarproject.transport.TransportModule;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
@@ -944,6 +945,7 @@ public class IntroductionIntegrationTest extends BriarTestCase {
this.accept = accept; this.accept = accept;
} }
@Override
public void eventOccurred(Event e) { public void eventOccurred(Event e) {
if (e instanceof MessageValidatedEvent) { if (e instanceof MessageValidatedEvent) {
MessageValidatedEvent event = (MessageValidatedEvent) e; MessageValidatedEvent event = (MessageValidatedEvent) e;
@@ -1010,6 +1012,7 @@ public class IntroductionIntegrationTest extends BriarTestCase {
public volatile boolean response2Received = false; public volatile boolean response2Received = false;
public volatile boolean aborted = false; public volatile boolean aborted = false;
@Override
public void eventOccurred(Event e) { public void eventOccurred(Event e) {
if (e instanceof MessageValidatedEvent) { if (e instanceof MessageValidatedEvent) {
MessageValidatedEvent event = (MessageValidatedEvent) e; MessageValidatedEvent event = (MessageValidatedEvent) e;
@@ -1050,7 +1053,7 @@ public class IntroductionIntegrationTest extends BriarTestCase {
component.inject(new ContactModule.EagerSingletons()); component.inject(new ContactModule.EagerSingletons());
component.inject(new TransportModule.EagerSingletons()); component.inject(new TransportModule.EagerSingletons());
component.inject(new SyncModule.EagerSingletons()); component.inject(new SyncModule.EagerSingletons());
component.inject(new SystemModule.EagerSingletons());
component.inject(new PropertiesModule.EagerSingletons()); component.inject(new PropertiesModule.EagerSingletons());
} }
} }

View File

@@ -21,6 +21,7 @@ import org.briarproject.introduction.MessageSender;
import org.briarproject.lifecycle.LifecycleModule; import org.briarproject.lifecycle.LifecycleModule;
import org.briarproject.properties.PropertiesModule; import org.briarproject.properties.PropertiesModule;
import org.briarproject.sync.SyncModule; import org.briarproject.sync.SyncModule;
import org.briarproject.system.SystemModule;
import org.briarproject.transport.TransportModule; import org.briarproject.transport.TransportModule;
import javax.inject.Singleton; import javax.inject.Singleton;
@@ -29,9 +30,9 @@ import dagger.Component;
@Singleton @Singleton
@Component(modules = { @Component(modules = {
TestSystemModule.class,
TestDatabaseModule.class, TestDatabaseModule.class,
TestPluginsModule.class, TestPluginsModule.class,
TestSeedProviderModule.class,
LifecycleModule.class, LifecycleModule.class,
IntroductionModule.class, IntroductionModule.class,
DatabaseModule.class, DatabaseModule.class,
@@ -42,6 +43,7 @@ import dagger.Component;
TransportModule.class, TransportModule.class,
ClientsModule.class, ClientsModule.class,
SyncModule.class, SyncModule.class,
SystemModule.class,
DataModule.class, DataModule.class,
PropertiesModule.class PropertiesModule.class
}) })
@@ -61,6 +63,8 @@ public interface IntroductionIntegrationTestComponent {
void inject(SyncModule.EagerSingletons init); void inject(SyncModule.EagerSingletons init);
void inject(SystemModule.EagerSingletons init);
void inject(TransportModule.EagerSingletons init); void inject(TransportModule.EagerSingletons init);
LifecycleManager getLifecycleManager(); LifecycleManager getLifecycleManager();
@@ -84,5 +88,4 @@ public interface IntroductionIntegrationTestComponent {
MessageSender getMessageSender(); MessageSender getMessageSender();
IntroductionGroupFactory getIntroductionGroupFactory(); IntroductionGroupFactory getIntroductionGroupFactory();
} }

View File

@@ -13,6 +13,7 @@ import org.briarproject.api.messaging.PrivateMessage;
import org.briarproject.api.messaging.PrivateMessageFactory; import org.briarproject.api.messaging.PrivateMessageFactory;
import org.briarproject.api.sync.GroupId; import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.MessageId; import org.briarproject.api.sync.MessageId;
import org.briarproject.system.SystemModule;
import org.junit.Test; import org.junit.Test;
import javax.inject.Inject; import javax.inject.Inject;
@@ -39,6 +40,7 @@ public class MessageSizeIntegrationTest extends BriarTestCase {
MessageSizeIntegrationTestComponent component = MessageSizeIntegrationTestComponent component =
DaggerMessageSizeIntegrationTestComponent.builder().build(); DaggerMessageSizeIntegrationTestComponent.builder().build();
component.inject(this); component.inject(this);
component.inject(new SystemModule.EagerSingletons());
} }
@Test @Test

View File

@@ -9,6 +9,7 @@ import org.briarproject.forum.ForumModule;
import org.briarproject.identity.IdentityModule; import org.briarproject.identity.IdentityModule;
import org.briarproject.messaging.MessagingModule; import org.briarproject.messaging.MessagingModule;
import org.briarproject.sync.SyncModule; import org.briarproject.sync.SyncModule;
import org.briarproject.system.SystemModule;
import javax.inject.Singleton; import javax.inject.Singleton;
@@ -18,7 +19,7 @@ import dagger.Component;
@Component(modules = { @Component(modules = {
TestDatabaseModule.class, TestDatabaseModule.class,
TestLifecycleModule.class, TestLifecycleModule.class,
TestSystemModule.class, TestSeedProviderModule.class,
ClientsModule.class, ClientsModule.class,
CryptoModule.class, CryptoModule.class,
DataModule.class, DataModule.class,
@@ -27,8 +28,12 @@ import dagger.Component;
ForumModule.class, ForumModule.class,
IdentityModule.class, IdentityModule.class,
MessagingModule.class, MessagingModule.class,
SyncModule.class SyncModule.class,
SystemModule.class
}) })
public interface MessageSizeIntegrationTestComponent { public interface MessageSizeIntegrationTestComponent {
void inject(MessageSizeIntegrationTest testCase); void inject(MessageSizeIntegrationTest testCase);
void inject(SystemModule.EagerSingletons init);
} }

View File

@@ -21,6 +21,7 @@ import org.briarproject.api.transport.KeyManager;
import org.briarproject.api.transport.StreamContext; import org.briarproject.api.transport.StreamContext;
import org.briarproject.api.transport.StreamReaderFactory; import org.briarproject.api.transport.StreamReaderFactory;
import org.briarproject.api.transport.StreamWriterFactory; import org.briarproject.api.transport.StreamWriterFactory;
import org.briarproject.system.SystemModule;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@@ -57,8 +58,10 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
assertTrue(testDir.mkdirs()); assertTrue(testDir.mkdirs());
alice = DaggerSimplexMessagingIntegrationTestComponent.builder() alice = DaggerSimplexMessagingIntegrationTestComponent.builder()
.testDatabaseModule(new TestDatabaseModule(aliceDir)).build(); .testDatabaseModule(new TestDatabaseModule(aliceDir)).build();
alice.inject(new SystemModule.EagerSingletons());
bob = DaggerSimplexMessagingIntegrationTestComponent.builder() bob = DaggerSimplexMessagingIntegrationTestComponent.builder()
.testDatabaseModule(new TestDatabaseModule(bobDir)).build(); .testDatabaseModule(new TestDatabaseModule(bobDir)).build();
bob.inject(new SystemModule.EagerSingletons());
} }
@Test @Test
@@ -183,6 +186,7 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
private volatile boolean messageAdded = false; private volatile boolean messageAdded = false;
@Override
public void eventOccurred(Event e) { public void eventOccurred(Event e) {
if (e instanceof MessageAddedEvent) messageAdded = true; if (e instanceof MessageAddedEvent) messageAdded = true;
} }

View File

@@ -21,6 +21,7 @@ import org.briarproject.lifecycle.LifecycleModule;
import org.briarproject.messaging.MessagingModule; import org.briarproject.messaging.MessagingModule;
import org.briarproject.plugins.PluginsModule; import org.briarproject.plugins.PluginsModule;
import org.briarproject.sync.SyncModule; import org.briarproject.sync.SyncModule;
import org.briarproject.system.SystemModule;
import org.briarproject.transport.TransportModule; import org.briarproject.transport.TransportModule;
import javax.inject.Singleton; import javax.inject.Singleton;
@@ -31,7 +32,7 @@ import dagger.Component;
@Component(modules = { @Component(modules = {
TestDatabaseModule.class, TestDatabaseModule.class,
TestPluginsModule.class, TestPluginsModule.class,
TestSystemModule.class, TestSeedProviderModule.class,
ClientsModule.class, ClientsModule.class,
ContactModule.class, ContactModule.class,
CryptoModule.class, CryptoModule.class,
@@ -43,11 +44,12 @@ import dagger.Component;
MessagingModule.class, MessagingModule.class,
PluginsModule.class, PluginsModule.class,
SyncModule.class, SyncModule.class,
SystemModule.class,
TransportModule.class TransportModule.class
}) })
public interface SimplexMessagingIntegrationTestComponent { public interface SimplexMessagingIntegrationTestComponent {
void inject(SimplexMessagingIntegrationTest testCase); void inject(SystemModule.EagerSingletons init);
LifecycleManager getLifecycleManager(); LifecycleManager getLifecycleManager();

View File

@@ -10,7 +10,7 @@ import dagger.Component;
@Singleton @Singleton
@Component(modules = { @Component(modules = {
TestSystemModule.class, TestSeedProviderModule.class,
CryptoModule.class, CryptoModule.class,
SyncModule.class, SyncModule.class,
TransportModule.class TransportModule.class

View File

@@ -15,6 +15,7 @@ import dagger.Provides;
public class AndroidSystemModule { public class AndroidSystemModule {
@Provides @Provides
@Singleton
public SeedProvider provideSeedProvider(Application app) { public SeedProvider provideSeedProvider(Application app) {
return new AndroidSeedProvider(app); return new AndroidSeedProvider(app);
} }

View File

@@ -2,24 +2,39 @@ package org.briarproject.api.plugins;
import org.briarproject.api.TransportId; import org.briarproject.api.TransportId;
import org.briarproject.api.plugins.duplex.DuplexPlugin; import org.briarproject.api.plugins.duplex.DuplexPlugin;
import org.briarproject.api.plugins.simplex.SimplexPlugin;
import java.util.Collection; import java.util.Collection;
/** /**
* Responsible for starting transport plugins at startup, stopping them at * Responsible for starting transport plugins at startup and stopping them at
* shutdown, and providing access to plugins for exchanging invitations. * shutdown.
*/ */
public interface PluginManager { public interface PluginManager {
/** /**
* Returns the plugin for the given transport, or null if no such plugin * Returns the plugin for the given transport, or null if no such plugin
* is running. * has been created.
*/ */
Plugin getPlugin(TransportId t); Plugin getPlugin(TransportId t);
/** Returns any running duplex plugins that support invitations. */ /**
* Returns any simplex plugins that have been created.
*/
Collection<SimplexPlugin> getSimplexPlugins();
/**
* Returns any duplex plugins that have been created.
*/
Collection<DuplexPlugin> getDuplexPlugins();
/**
* Returns any duplex plugins that support invitations.
*/
Collection<DuplexPlugin> getInvitationPlugins(); Collection<DuplexPlugin> getInvitationPlugins();
/** Returns any running duplex plugins that support key agreement. */ /**
* Returns any duplex plugins that support key agreement.
*/
Collection<DuplexPlugin> getKeyAgreementPlugins(); Collection<DuplexPlugin> getKeyAgreementPlugins();
} }

View File

@@ -1,27 +0,0 @@
package org.briarproject.api.system;
import java.util.TimerTask;
/**
* A wrapper around a {@link java.util.Timer} that allows it to be replaced for
* testing.
*/
public interface Timer {
/** @see {@link java.util.Timer#cancel()} */
void cancel();
/** @see {@link java.util.Timer#purge()} */
int purge();
/** @see {@link java.util.Timer#schedule(TimerTask, long)} */
void schedule(TimerTask task, long delay);
/** @see {@link java.util.Timer#schedule(TimerTask, long, long)} */
void schedule(TimerTask task, long delay, long period);
/**
* @see {@link java.util.Timer#scheduleAtFixedRate(TimerTask, long, long)}
*/
void scheduleAtFixedRate(TimerTask task, long delay, long period);
}

View File

@@ -10,6 +10,7 @@ import org.briarproject.messaging.MessagingModule;
import org.briarproject.plugins.PluginsModule; import org.briarproject.plugins.PluginsModule;
import org.briarproject.properties.PropertiesModule; import org.briarproject.properties.PropertiesModule;
import org.briarproject.sync.SyncModule; import org.briarproject.sync.SyncModule;
import org.briarproject.system.SystemModule;
import org.briarproject.transport.TransportModule; import org.briarproject.transport.TransportModule;
public interface CoreEagerSingletons { public interface CoreEagerSingletons {
@@ -34,5 +35,7 @@ public interface CoreEagerSingletons {
void inject(SyncModule.EagerSingletons init); void inject(SyncModule.EagerSingletons init);
void inject(SystemModule.EagerSingletons init);
void inject(TransportModule.EagerSingletons init); void inject(TransportModule.EagerSingletons init);
} }

View File

@@ -61,6 +61,7 @@ public class CoreModule {
c.inject(new PluginsModule.EagerSingletons()); c.inject(new PluginsModule.EagerSingletons());
c.inject(new PropertiesModule.EagerSingletons()); c.inject(new PropertiesModule.EagerSingletons());
c.inject(new SyncModule.EagerSingletons()); c.inject(new SyncModule.EagerSingletons());
c.inject(new SystemModule.EagerSingletons());
c.inject(new TransportModule.EagerSingletons()); c.inject(new TransportModule.EagerSingletons());
c.inject(new IntroductionModule.EagerSingletons()); c.inject(new IntroductionModule.EagerSingletons());
} }

View File

@@ -50,24 +50,27 @@ class LifecycleManagerImpl implements LifecycleManager {
executors = new CopyOnWriteArrayList<ExecutorService>(); executors = new CopyOnWriteArrayList<ExecutorService>();
} }
@Override
public void registerService(Service s) { public void registerService(Service s) {
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
LOG.info("Registering service " + s.getClass().getName()); LOG.info("Registering service " + s.getClass().getName());
services.add(s); services.add(s);
} }
@Override
public void registerClient(Client c) { public void registerClient(Client c) {
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
LOG.info("Registering client " + c.getClass().getName()); LOG.info("Registering client " + c.getClass().getName());
clients.add(c); clients.add(c);
} }
@Override
public void registerForShutdown(ExecutorService e) { public void registerForShutdown(ExecutorService e) {
if (LOG.isLoggable(INFO)) LOG.info("Registering executor");
LOG.info("Registering executor " + e.getClass().getName());
executors.add(e); executors.add(e);
} }
@Override
public StartResult startServices() { public StartResult startServices() {
if (!startStopSemaphore.tryAcquire()) { if (!startStopSemaphore.tryAcquire()) {
LOG.info("Already starting or stopping"); LOG.info("Already starting or stopping");
@@ -91,7 +94,7 @@ class LifecycleManagerImpl implements LifecycleManager {
c.createLocalState(txn); c.createLocalState(txn);
duration = System.currentTimeMillis() - start; duration = System.currentTimeMillis() - start;
if (LOG.isLoggable(INFO)) { if (LOG.isLoggable(INFO)) {
LOG.info("Starting " + c.getClass().getName() LOG.info("Starting client " + c.getClass().getName()
+ " took " + duration + " ms"); + " took " + duration + " ms");
} }
} }
@@ -104,7 +107,7 @@ class LifecycleManagerImpl implements LifecycleManager {
s.startService(); s.startService();
duration = System.currentTimeMillis() - start; duration = System.currentTimeMillis() - start;
if (LOG.isLoggable(INFO)) { if (LOG.isLoggable(INFO)) {
LOG.info("Starting " + s.getClass().getName() LOG.info("Starting service " + s.getClass().getName()
+ " took " + duration + " ms"); + " took " + duration + " ms");
} }
} }
@@ -121,6 +124,7 @@ class LifecycleManagerImpl implements LifecycleManager {
} }
} }
@Override
public void stopServices() { public void stopServices() {
try { try {
startStopSemaphore.acquire(); startStopSemaphore.acquire();
@@ -132,15 +136,22 @@ 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) {
long start = System.currentTimeMillis();
s.stopService(); s.stopService();
if (LOG.isLoggable(INFO)) long duration = System.currentTimeMillis() - start;
LOG.info("Service stopped: " + s.getClass().getName()); if (LOG.isLoggable(INFO)) {
LOG.info("Stopping service " + s.getClass().getName()
+ " took " + duration + " ms");
}
} }
for (ExecutorService e : executors) e.shutdownNow(); for (ExecutorService e : executors) e.shutdownNow();
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
LOG.info(executors.size() + " executors shut down"); LOG.info(executors.size() + " executors shut down");
long start = System.currentTimeMillis();
db.close(); db.close();
LOG.info("Database closed"); long duration = System.currentTimeMillis() - start;
if (LOG.isLoggable(INFO))
LOG.info("Closing database took " + duration + " ms");
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);
@@ -151,14 +162,17 @@ class LifecycleManagerImpl implements LifecycleManager {
} }
} }
@Override
public void waitForDatabase() throws InterruptedException { public void waitForDatabase() throws InterruptedException {
dbLatch.await(); dbLatch.await();
} }
@Override
public void waitForStartup() throws InterruptedException { public void waitForStartup() throws InterruptedException {
startupLatch.await(); startupLatch.await();
} }
@Override
public void waitForShutdown() throws InterruptedException { public void waitForShutdown() throws InterruptedException {
shutdownLatch.await(); shutdownLatch.await();
} }

View File

@@ -3,18 +3,13 @@ package org.briarproject.plugins;
import org.briarproject.api.TransportId; import org.briarproject.api.TransportId;
import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactId;
import org.briarproject.api.db.DbException; import org.briarproject.api.db.DbException;
import org.briarproject.api.event.ConnectionClosedEvent;
import org.briarproject.api.event.ContactStatusChangedEvent;
import org.briarproject.api.event.Event;
import org.briarproject.api.event.EventBus; import org.briarproject.api.event.EventBus;
import org.briarproject.api.event.EventListener;
import org.briarproject.api.event.TransportDisabledEvent; 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.lifecycle.ServiceException;
import org.briarproject.api.plugins.ConnectionManager; import org.briarproject.api.plugins.ConnectionManager;
import org.briarproject.api.plugins.ConnectionRegistry;
import org.briarproject.api.plugins.Plugin; import org.briarproject.api.plugins.Plugin;
import org.briarproject.api.plugins.PluginCallback; import org.briarproject.api.plugins.PluginCallback;
import org.briarproject.api.plugins.PluginConfig; import org.briarproject.api.plugins.PluginConfig;
@@ -51,7 +46,7 @@ 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; import static java.util.logging.Level.WARNING;
class PluginManagerImpl implements PluginManager, Service, EventListener { class PluginManagerImpl implements PluginManager, Service {
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(PluginManagerImpl.class.getName()); Logger.getLogger(PluginManagerImpl.class.getName());
@@ -59,9 +54,7 @@ class PluginManagerImpl implements PluginManager, Service, EventListener {
private final Executor ioExecutor; private final Executor ioExecutor;
private final EventBus eventBus; private final EventBus eventBus;
private final PluginConfig pluginConfig; private final PluginConfig pluginConfig;
private final Poller poller;
private final ConnectionManager connectionManager; private final ConnectionManager connectionManager;
private final ConnectionRegistry connectionRegistry;
private final SettingsManager settingsManager; private final SettingsManager settingsManager;
private final TransportPropertyManager transportPropertyManager; private final TransportPropertyManager transportPropertyManager;
private final UiCallback uiCallback; private final UiCallback uiCallback;
@@ -71,18 +64,14 @@ class PluginManagerImpl implements PluginManager, Service, EventListener {
@Inject @Inject
PluginManagerImpl(@IoExecutor Executor ioExecutor, EventBus eventBus, PluginManagerImpl(@IoExecutor Executor ioExecutor, EventBus eventBus,
PluginConfig pluginConfig, Poller poller, PluginConfig pluginConfig, ConnectionManager connectionManager,
ConnectionManager connectionManager,
ConnectionRegistry connectionRegistry,
SettingsManager settingsManager, SettingsManager settingsManager,
TransportPropertyManager transportPropertyManager, TransportPropertyManager transportPropertyManager,
UiCallback uiCallback) { UiCallback uiCallback) {
this.ioExecutor = ioExecutor; this.ioExecutor = ioExecutor;
this.eventBus = eventBus; this.eventBus = eventBus;
this.pluginConfig = pluginConfig; this.pluginConfig = pluginConfig;
this.poller = poller;
this.connectionManager = connectionManager; this.connectionManager = connectionManager;
this.connectionRegistry = connectionRegistry;
this.settingsManager = settingsManager; this.settingsManager = settingsManager;
this.transportPropertyManager = transportPropertyManager; this.transportPropertyManager = transportPropertyManager;
this.uiCallback = uiCallback; this.uiCallback = uiCallback;
@@ -93,39 +82,53 @@ class PluginManagerImpl implements PluginManager, Service, EventListener {
@Override @Override
public void startService() throws ServiceException { public void startService() throws ServiceException {
Collection<SimplexPluginFactory> simplexFactories =
pluginConfig.getSimplexFactories();
Collection<DuplexPluginFactory> duplexFactories =
pluginConfig.getDuplexFactories();
int numPlugins = simplexFactories.size() + duplexFactories.size();
CountDownLatch latch = new CountDownLatch(numPlugins);
// 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 = for (SimplexPluginFactory f : simplexFactories) {
pluginConfig.getSimplexFactories(); TransportId t = f.getId();
final CountDownLatch sLatch = new CountDownLatch(sFactories.size()); SimplexPlugin s = f.createPlugin(new SimplexCallback(t));
for (SimplexPluginFactory factory : sFactories) if (s == null) {
ioExecutor.execute(new SimplexPluginStarter(factory, sLatch)); if (LOG.isLoggable(WARNING))
LOG.warning("Could not create plugin for " + t);
latch.countDown();
} else {
plugins.put(t, s);
simplexPlugins.add(s);
ioExecutor.execute(new PluginStarter(s, latch));
}
}
// Instantiate and start the duplex plugins // Instantiate and start the duplex plugins
LOG.info("Starting duplex plugins"); LOG.info("Starting duplex plugins");
Collection<DuplexPluginFactory> dFactories = for (DuplexPluginFactory f : duplexFactories) {
pluginConfig.getDuplexFactories(); TransportId t = f.getId();
final CountDownLatch dLatch = new CountDownLatch(dFactories.size()); DuplexPlugin d = f.createPlugin(new DuplexCallback(t));
for (DuplexPluginFactory factory : dFactories) if (d == null) {
ioExecutor.execute(new DuplexPluginStarter(factory, dLatch)); if (LOG.isLoggable(WARNING))
// Wait for the plugins to start LOG.warning("Could not create plugin for " + t);
latch.countDown();
} else {
plugins.put(t, d);
duplexPlugins.add(d);
ioExecutor.execute(new PluginStarter(d, latch));
}
}
// Wait for all the plugins to start
try { try {
sLatch.await(); latch.await();
dLatch.await();
} catch (InterruptedException e) { } catch (InterruptedException e) {
throw new ServiceException(e); throw new ServiceException(e);
} }
// Listen for events
eventBus.addListener(this);
} }
@Override @Override
public void stopService() throws ServiceException { public void stopService() throws ServiceException {
// Stop listening for events CountDownLatch latch = new CountDownLatch(plugins.size());
eventBus.removeListener(this);
// Stop the poller
LOG.info("Stopping poller");
poller.stop();
final CountDownLatch latch = new CountDownLatch(plugins.size());
// Stop the simplex plugins // Stop the simplex plugins
LOG.info("Stopping simplex plugins"); LOG.info("Stopping simplex plugins");
for (SimplexPlugin plugin : simplexPlugins) for (SimplexPlugin plugin : simplexPlugins)
@@ -147,6 +150,16 @@ class PluginManagerImpl implements PluginManager, Service, EventListener {
return plugins.get(t); return plugins.get(t);
} }
@Override
public Collection<SimplexPlugin> getSimplexPlugins() {
return Collections.unmodifiableList(simplexPlugins);
}
@Override
public Collection<DuplexPlugin> getDuplexPlugins() {
return Collections.unmodifiableList(duplexPlugins);
}
@Override @Override
public Collection<DuplexPlugin> getInvitationPlugins() { public Collection<DuplexPlugin> getInvitationPlugins() {
List<DuplexPlugin> supported = new ArrayList<DuplexPlugin>(); List<DuplexPlugin> supported = new ArrayList<DuplexPlugin>();
@@ -163,158 +176,32 @@ class PluginManagerImpl implements PluginManager, Service, EventListener {
return Collections.unmodifiableList(supported); return Collections.unmodifiableList(supported);
} }
@Override private class PluginStarter implements Runnable {
public void eventOccurred(Event e) {
if (e instanceof ContactStatusChangedEvent) {
ContactStatusChangedEvent c = (ContactStatusChangedEvent) e;
if (c.isActive()) {
// Connect to the newly activated contact
connectToContact(c.getContactId());
}
} else if (e instanceof ConnectionClosedEvent) {
ConnectionClosedEvent c = (ConnectionClosedEvent) e;
if (!c.isIncoming()) {
// Connect to the disconnected contact
connectToContact(c.getContactId(), c.getTransportId());
}
}
}
private void connectToContact(ContactId c) { private final Plugin plugin;
for (SimplexPlugin s : simplexPlugins)
if (s.shouldPoll()) connectToContact(c, s);
for (DuplexPlugin d : duplexPlugins)
if (d.shouldPoll()) connectToContact(c, d);
}
private void connectToContact(ContactId c, TransportId t) {
Plugin p = plugins.get(t);
if (p instanceof SimplexPlugin && p.shouldPoll())
connectToContact(c, (SimplexPlugin) p);
else if (p instanceof DuplexPlugin && p.shouldPoll())
connectToContact(c, (DuplexPlugin) p);
}
private void connectToContact(final ContactId c, final SimplexPlugin p) {
ioExecutor.execute(new Runnable() {
@Override
public void run() {
TransportId t = p.getId();
if (!connectionRegistry.isConnected(c, t)) {
TransportConnectionWriter w = p.createWriter(c);
if (w != null)
connectionManager.manageOutgoingConnection(c, t, w);
}
}
});
}
private void connectToContact(final ContactId c, final DuplexPlugin p) {
ioExecutor.execute(new Runnable() {
@Override
public void run() {
TransportId t = p.getId();
if (!connectionRegistry.isConnected(c, t)) {
DuplexTransportConnection d = p.createConnection(c);
if (d != null)
connectionManager.manageOutgoingConnection(c, t, d);
}
}
});
}
private class SimplexPluginStarter implements Runnable {
private final SimplexPluginFactory factory;
private final CountDownLatch latch; private final CountDownLatch latch;
private SimplexPluginStarter(SimplexPluginFactory factory, private PluginStarter(Plugin plugin, CountDownLatch latch) {
CountDownLatch latch) { this.plugin = plugin;
this.factory = factory;
this.latch = latch; this.latch = latch;
} }
@Override @Override
public void run() { public void run() {
try { try {
TransportId id = factory.getId();
SimplexCallback callback = new SimplexCallback(id);
SimplexPlugin plugin = factory.createPlugin(callback);
if (plugin == null) {
if (LOG.isLoggable(INFO)) {
String name = factory.getClass().getSimpleName();
LOG.info(name + " did not create a plugin");
}
return;
}
try { try {
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
boolean started = plugin.start(); boolean started = plugin.start();
long duration = System.currentTimeMillis() - start; long duration = System.currentTimeMillis() - start;
if (started) { if (started) {
plugins.put(id, plugin);
simplexPlugins.add(plugin);
if (LOG.isLoggable(INFO)) { if (LOG.isLoggable(INFO)) {
String name = plugin.getClass().getSimpleName(); LOG.info("Starting plugin " + plugin.getId()
LOG.info("Starting " + name + " took " + + " took " + duration + " ms");
duration + " ms");
} }
} else { } else {
if (LOG.isLoggable(WARNING)) { if (LOG.isLoggable(WARNING)) {
String name = plugin.getClass().getSimpleName(); LOG.warning("Plugin" + plugin.getId()
LOG.warning(name + " did not start"); + " did not start");
}
}
} catch (IOException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
}
} finally {
latch.countDown();
}
}
}
private class DuplexPluginStarter implements Runnable {
private final DuplexPluginFactory factory;
private final CountDownLatch latch;
private DuplexPluginStarter(DuplexPluginFactory factory,
CountDownLatch latch) {
this.factory = factory;
this.latch = latch;
}
@Override
public void run() {
try {
TransportId id = factory.getId();
DuplexCallback callback = new DuplexCallback(id);
DuplexPlugin plugin = factory.createPlugin(callback);
if (plugin == null) {
if (LOG.isLoggable(INFO)) {
String name = factory.getClass().getSimpleName();
LOG.info(name + " did not create a plugin");
}
return;
}
try {
long start = System.currentTimeMillis();
boolean started = plugin.start();
long duration = System.currentTimeMillis() - start;
if (started) {
plugins.put(id, plugin);
duplexPlugins.add(plugin);
if (LOG.isLoggable(INFO)) {
String name = plugin.getClass().getSimpleName();
LOG.info("Starting " + name + " took " +
duration + " ms");
}
} else {
if (LOG.isLoggable(WARNING)) {
String name = plugin.getClass().getSimpleName();
LOG.warning(name + " did not start");
} }
} }
} catch (IOException e) { } catch (IOException e) {
@@ -344,8 +231,8 @@ class PluginManagerImpl implements PluginManager, Service, EventListener {
plugin.stop(); plugin.stop();
long duration = System.currentTimeMillis() - start; long duration = System.currentTimeMillis() - start;
if (LOG.isLoggable(INFO)) { if (LOG.isLoggable(INFO)) {
String name = plugin.getClass().getSimpleName(); LOG.info("Stopping plugin " + plugin.getId()
LOG.info("Stopping " + name + " took " + duration + " ms"); + " took " + duration + " ms");
} }
} 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);
@@ -431,8 +318,6 @@ class PluginManagerImpl implements PluginManager, Service, EventListener {
@Override @Override
public void transportEnabled() { public void transportEnabled() {
eventBus.broadcast(new TransportEnabledEvent(id)); eventBus.broadcast(new TransportEnabledEvent(id));
Plugin p = plugins.get(id);
if (p != null) poller.pollNow(p);
} }
@Override @Override

View File

@@ -7,14 +7,11 @@ import org.briarproject.api.plugins.BackoffFactory;
import org.briarproject.api.plugins.ConnectionManager; import org.briarproject.api.plugins.ConnectionManager;
import org.briarproject.api.plugins.ConnectionRegistry; import org.briarproject.api.plugins.ConnectionRegistry;
import org.briarproject.api.plugins.PluginManager; import org.briarproject.api.plugins.PluginManager;
import org.briarproject.api.sync.SyncSessionFactory; import org.briarproject.api.system.Clock;
import org.briarproject.api.system.Timer;
import org.briarproject.api.transport.KeyManager;
import org.briarproject.api.transport.StreamReaderFactory;
import org.briarproject.api.transport.StreamWriterFactory;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
@@ -28,6 +25,8 @@ public class PluginsModule {
public static class EagerSingletons { public static class EagerSingletons {
@Inject @Inject
PluginManager pluginManager; PluginManager pluginManager;
@Inject
Poller poller;
} }
@Provides @Provides
@@ -36,33 +35,35 @@ public class PluginsModule {
} }
@Provides @Provides
@Singleton
Poller providePoller(@IoExecutor Executor ioExecutor, Poller providePoller(@IoExecutor Executor ioExecutor,
ConnectionRegistry connectionRegistry, SecureRandom random, ScheduledExecutorService scheduler,
Timer timer) { ConnectionManager connectionManager,
return new PollerImpl(ioExecutor, connectionRegistry, random, timer); ConnectionRegistry connectionRegistry, PluginManager pluginManager,
SecureRandom random, Clock clock, EventBus eventBus) {
Poller poller = new Poller(ioExecutor, scheduler, connectionManager,
connectionRegistry, pluginManager, random, clock);
eventBus.addListener(poller);
return poller;
} }
@Provides @Provides
@Singleton
ConnectionManager provideConnectionManager( ConnectionManager provideConnectionManager(
@IoExecutor Executor ioExecutor, KeyManager keyManager, ConnectionManagerImpl connectionManager) {
StreamReaderFactory streamReaderFactory, return connectionManager;
StreamWriterFactory streamWriterFactory,
SyncSessionFactory syncSessionFactory,
ConnectionRegistry connectionRegistry) {
return new ConnectionManagerImpl(ioExecutor, keyManager,
streamReaderFactory, streamWriterFactory, syncSessionFactory,
connectionRegistry);
} }
@Provides @Provides
@Singleton @Singleton
ConnectionRegistry provideConnectionRegistry(EventBus eventBus) { ConnectionRegistry provideConnectionRegistry(
return new ConnectionRegistryImpl(eventBus); ConnectionRegistryImpl connectionRegistry) {
return connectionRegistry;
} }
@Provides @Provides
@Singleton @Singleton
PluginManager getPluginManager(LifecycleManager lifecycleManager, PluginManager providePluginManager(LifecycleManager lifecycleManager,
PluginManagerImpl pluginManager) { PluginManagerImpl pluginManager) {
lifecycleManager.registerService(pluginManager); lifecycleManager.registerService(pluginManager);
return pluginManager; return pluginManager;

View File

@@ -1,12 +1,203 @@
package org.briarproject.plugins; package org.briarproject.plugins;
import org.briarproject.api.TransportId;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.event.ConnectionClosedEvent;
import org.briarproject.api.event.ConnectionOpenedEvent;
import org.briarproject.api.event.ContactStatusChangedEvent;
import org.briarproject.api.event.Event;
import org.briarproject.api.event.EventListener;
import org.briarproject.api.event.TransportEnabledEvent;
import org.briarproject.api.lifecycle.IoExecutor;
import org.briarproject.api.plugins.ConnectionManager;
import org.briarproject.api.plugins.ConnectionRegistry;
import org.briarproject.api.plugins.Plugin; import org.briarproject.api.plugins.Plugin;
import org.briarproject.api.plugins.PluginManager;
import org.briarproject.api.plugins.TransportConnectionWriter;
import org.briarproject.api.plugins.duplex.DuplexPlugin;
import org.briarproject.api.plugins.duplex.DuplexTransportConnection;
import org.briarproject.api.plugins.simplex.SimplexPlugin;
import org.briarproject.api.system.Clock;
interface Poller { import java.security.SecureRandom;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Logger;
/** Tells the poller to poll the given plugin immediately. */ import javax.inject.Inject;
void pollNow(Plugin p);
/** Stops the poller. */ import static java.util.concurrent.TimeUnit.MILLISECONDS;
void stop(); import static java.util.logging.Level.INFO;
class Poller implements EventListener {
private static final Logger LOG = Logger.getLogger(Poller.class.getName());
private final Executor ioExecutor;
private final ScheduledExecutorService scheduler;
private final ConnectionManager connectionManager;
private final ConnectionRegistry connectionRegistry;
private final PluginManager pluginManager;
private final SecureRandom random;
private final Clock clock;
private final Lock lock;
private final Map<TransportId, PollTask> tasks; // Locking: lock
@Inject
Poller(@IoExecutor Executor ioExecutor, ScheduledExecutorService scheduler,
ConnectionManager connectionManager,
ConnectionRegistry connectionRegistry, PluginManager pluginManager,
SecureRandom random, Clock clock) {
this.ioExecutor = ioExecutor;
this.scheduler = scheduler;
this.connectionManager = connectionManager;
this.connectionRegistry = connectionRegistry;
this.pluginManager = pluginManager;
this.random = random;
this.clock = clock;
lock = new ReentrantLock();
tasks = new HashMap<TransportId, PollTask>();
}
@Override
public void eventOccurred(Event e) {
if (e instanceof ContactStatusChangedEvent) {
ContactStatusChangedEvent c = (ContactStatusChangedEvent) e;
if (c.isActive()) {
// Connect to the newly activated contact
connectToContact(c.getContactId());
}
} else if (e instanceof ConnectionClosedEvent) {
ConnectionClosedEvent c = (ConnectionClosedEvent) e;
// Reschedule polling, the polling interval may have decreased
reschedule(c.getTransportId());
if (!c.isIncoming()) {
// Connect to the disconnected contact
connectToContact(c.getContactId(), c.getTransportId());
}
} else if (e instanceof ConnectionOpenedEvent) {
ConnectionOpenedEvent c = (ConnectionOpenedEvent) e;
// Reschedule polling, the polling interval may have decreased
reschedule(c.getTransportId());
} else if (e instanceof TransportEnabledEvent) {
TransportEnabledEvent t = (TransportEnabledEvent) e;
// Poll the newly enabled transport
pollNow(t.getTransportId());
}
}
private void connectToContact(ContactId c) {
for (SimplexPlugin s : pluginManager.getSimplexPlugins())
if (s.shouldPoll()) connectToContact(c, s);
for (DuplexPlugin d : pluginManager.getDuplexPlugins())
if (d.shouldPoll()) connectToContact(c, d);
}
private void connectToContact(ContactId c, TransportId t) {
Plugin p = pluginManager.getPlugin(t);
if (p instanceof SimplexPlugin && p.shouldPoll())
connectToContact(c, (SimplexPlugin) p);
else if (p instanceof DuplexPlugin && p.shouldPoll())
connectToContact(c, (DuplexPlugin) p);
}
private void connectToContact(final ContactId c, final SimplexPlugin p) {
ioExecutor.execute(new Runnable() {
@Override
public void run() {
TransportId t = p.getId();
if (!connectionRegistry.isConnected(c, t)) {
TransportConnectionWriter w = p.createWriter(c);
if (w != null)
connectionManager.manageOutgoingConnection(c, t, w);
}
}
});
}
private void connectToContact(final ContactId c, final DuplexPlugin p) {
ioExecutor.execute(new Runnable() {
@Override
public void run() {
TransportId t = p.getId();
if (!connectionRegistry.isConnected(c, t)) {
DuplexTransportConnection d = p.createConnection(c);
if (d != null)
connectionManager.manageOutgoingConnection(c, t, d);
}
}
});
}
private void reschedule(TransportId t) {
Plugin p = pluginManager.getPlugin(t);
if (p.shouldPoll()) schedule(p, p.getPollingInterval(), false);
}
private void pollNow(TransportId t) {
Plugin p = pluginManager.getPlugin(t);
// Randomise next polling interval
if (p.shouldPoll()) schedule(p, 0, true);
}
private void schedule(Plugin p, int delay, boolean randomiseNext) {
// Replace any later scheduled task for this plugin
long due = clock.currentTimeMillis() + delay;
TransportId t = p.getId();
lock.lock();
try {
PollTask scheduled = tasks.get(t);
if (scheduled == null || due < scheduled.due) {
PollTask task = new PollTask(p, due, randomiseNext);
tasks.put(t, task);
scheduler.schedule(task, delay, MILLISECONDS);
}
} finally {
lock.unlock();
}
}
private void poll(final Plugin p) {
ioExecutor.execute(new Runnable() {
@Override
public void run() {
TransportId t = p.getId();
if (LOG.isLoggable(INFO)) LOG.info("Polling plugin " + t);
p.poll(connectionRegistry.getConnectedContacts(t));
}
});
}
private class PollTask implements Runnable {
private final Plugin plugin;
private final long due;
private final boolean randomiseNext;
private PollTask(Plugin plugin, long due, boolean randomiseNext) {
this.plugin = plugin;
this.due = due;
this.randomiseNext = randomiseNext;
}
@Override
public void run() {
lock.lock();
try {
TransportId t = plugin.getId();
if (tasks.get(t) != this) return; // Replaced by another task
tasks.remove(t);
} finally {
lock.unlock();
}
int delay = plugin.getPollingInterval();
if (randomiseNext) delay = (int) (delay * random.nextDouble());
schedule(plugin, delay, false);
poll(plugin);
}
}
} }

View File

@@ -1,92 +0,0 @@
package org.briarproject.plugins;
import org.briarproject.api.TransportId;
import org.briarproject.api.lifecycle.IoExecutor;
import org.briarproject.api.plugins.ConnectionRegistry;
import org.briarproject.api.plugins.Plugin;
import org.briarproject.api.system.Timer;
import java.security.SecureRandom;
import java.util.Map;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
import javax.inject.Inject;
import static java.util.logging.Level.INFO;
class PollerImpl implements Poller {
private static final Logger LOG =
Logger.getLogger(PollerImpl.class.getName());
private final Executor ioExecutor;
private final ConnectionRegistry connectionRegistry;
private final SecureRandom random;
private final Timer timer;
private final Map<TransportId, PollTask> tasks;
@Inject
PollerImpl(@IoExecutor Executor ioExecutor,
ConnectionRegistry connectionRegistry, SecureRandom random,
Timer timer) {
this.ioExecutor = ioExecutor;
this.connectionRegistry = connectionRegistry;
this.random = random;
this.timer = timer;
tasks = new ConcurrentHashMap<TransportId, PollTask>();
}
@Override
public void stop() {
timer.cancel();
}
@Override
public void pollNow(Plugin p) {
// Randomise next polling interval
if (p.shouldPoll()) schedule(p, 0, true);
}
private void schedule(Plugin p, int interval, boolean randomiseNext) {
// Replace any previously scheduled task for this plugin
PollTask task = new PollTask(p, randomiseNext);
PollTask replaced = tasks.put(p.getId(), task);
if (replaced != null) replaced.cancel();
timer.schedule(task, interval);
}
private void poll(final Plugin p) {
ioExecutor.execute(new Runnable() {
@Override
public void run() {
if (LOG.isLoggable(INFO))
LOG.info("Polling " + p.getClass().getSimpleName());
p.poll(connectionRegistry.getConnectedContacts(p.getId()));
}
});
}
private class PollTask extends TimerTask {
private final Plugin plugin;
private final boolean randomiseNext;
private PollTask(Plugin plugin, boolean randomiseNext) {
this.plugin = plugin;
this.randomiseNext = randomiseNext;
}
@Override
public void run() {
tasks.remove(plugin.getId());
int interval = plugin.getPollingInterval();
if (randomiseNext)
interval = (int) (interval * random.nextDouble());
schedule(plugin, interval, false);
poll(plugin);
}
}
}

View File

@@ -1,23 +1,41 @@
package org.briarproject.system; package org.briarproject.system;
import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.system.Clock; import org.briarproject.api.system.Clock;
import org.briarproject.api.system.LocationUtils;
import org.briarproject.api.system.SeedProvider; import java.util.concurrent.Executors;
import org.briarproject.api.system.Timer; import java.util.concurrent.ScheduledExecutorService;
import javax.inject.Inject;
import javax.inject.Singleton;
import dagger.Module; import dagger.Module;
import dagger.Provides; import dagger.Provides;
@Module @Module
public class SystemModule { public class SystemModule {
public static class EagerSingletons {
@Inject
ScheduledExecutorService scheduledExecutorService;
}
private final ScheduledExecutorService scheduler;
public SystemModule() {
scheduler = Executors.newSingleThreadScheduledExecutor();
}
@Provides @Provides
Clock provideClock() { Clock provideClock() {
return new SystemClock(); return new SystemClock();
} }
@Provides @Provides
Timer provideTimer() { @Singleton
return new SystemTimer(); ScheduledExecutorService provideScheduledExecutorService(
LifecycleManager lifecycleManager) {
lifecycleManager.registerForShutdown(scheduler);
return scheduler;
} }
} }

View File

@@ -1,31 +0,0 @@
package org.briarproject.system;
import java.util.TimerTask;
import org.briarproject.api.system.Timer;
/** Default timer implementation. */
public class SystemTimer implements Timer {
private final java.util.Timer timer = new java.util.Timer(true);
public void cancel() {
timer.cancel();
}
public int purge() {
return timer.purge();
}
public void schedule(TimerTask task, long delay) {
timer.schedule(task, delay);
}
public void schedule(TimerTask task, long delay, long period) {
timer.schedule(task, delay, period);
}
public void scheduleAtFixedRate(TimerTask task, long delay, long period) {
timer.scheduleAtFixedRate(task, delay, period);
}
}

View File

@@ -19,7 +19,6 @@ 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;
import org.briarproject.api.system.Clock; import org.briarproject.api.system.Clock;
import org.briarproject.api.system.Timer;
import org.briarproject.api.transport.KeyManager; import org.briarproject.api.transport.KeyManager;
import org.briarproject.api.transport.StreamContext; import org.briarproject.api.transport.StreamContext;
@@ -28,6 +27,7 @@ import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.inject.Inject; import javax.inject.Inject;
@@ -42,21 +42,22 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
private final DatabaseComponent db; private final DatabaseComponent db;
private final CryptoComponent crypto; private final CryptoComponent crypto;
private final Executor dbExecutor; private final Executor dbExecutor;
private final ScheduledExecutorService scheduler;
private final PluginConfig pluginConfig; private final PluginConfig pluginConfig;
private final Timer timer;
private final Clock clock; private final Clock clock;
private final Map<ContactId, Boolean> activeContacts; private final Map<ContactId, Boolean> activeContacts;
private final ConcurrentHashMap<TransportId, TransportKeyManager> managers; private final ConcurrentHashMap<TransportId, TransportKeyManager> managers;
@Inject @Inject
KeyManagerImpl(DatabaseComponent db, CryptoComponent crypto, KeyManagerImpl(DatabaseComponent db, CryptoComponent crypto,
@DatabaseExecutor Executor dbExecutor, PluginConfig pluginConfig, @DatabaseExecutor Executor dbExecutor,
Timer timer, Clock clock) { ScheduledExecutorService scheduler, PluginConfig pluginConfig,
Clock clock) {
this.db = db; this.db = db;
this.crypto = crypto; this.crypto = crypto;
this.dbExecutor = dbExecutor; this.dbExecutor = dbExecutor;
this.scheduler = scheduler;
this.pluginConfig = pluginConfig; this.pluginConfig = pluginConfig;
this.timer = timer;
this.clock = clock; this.clock = clock;
// Use a ConcurrentHashMap as a thread-safe set // Use a ConcurrentHashMap as a thread-safe set
activeContacts = new ConcurrentHashMap<ContactId, Boolean>(); activeContacts = new ConcurrentHashMap<ContactId, Boolean>();
@@ -80,7 +81,8 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
db.addTransport(txn, e.getKey(), e.getValue()); db.addTransport(txn, e.getKey(), e.getValue());
for (Entry<TransportId, Integer> e : transports.entrySet()) { for (Entry<TransportId, Integer> e : transports.entrySet()) {
TransportKeyManager m = new TransportKeyManager(db, crypto, TransportKeyManager m = new TransportKeyManager(db, crypto,
timer, clock, e.getKey(), e.getValue()); dbExecutor, scheduler, clock, e.getKey(),
e.getValue());
managers.put(e.getKey(), m); managers.put(e.getKey(), m);
m.start(txn); m.start(txn);
} }
@@ -97,12 +99,14 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
public void stopService() { public void stopService() {
} }
@Override
public void addContact(Transaction txn, ContactId c, SecretKey master, public void addContact(Transaction txn, ContactId c, SecretKey master,
long timestamp, boolean alice) throws DbException { long timestamp, boolean alice) throws DbException {
for (TransportKeyManager m : managers.values()) for (TransportKeyManager m : managers.values())
m.addContact(txn, c, master, timestamp, alice); m.addContact(txn, c, master, timestamp, alice);
} }
@Override
public StreamContext getStreamContext(ContactId c, TransportId t) public StreamContext getStreamContext(ContactId c, TransportId t)
throws DbException { throws DbException {
// Don't allow outgoing streams to inactive contacts // Don't allow outgoing streams to inactive contacts
@@ -123,6 +127,7 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
return ctx; return ctx;
} }
@Override
public StreamContext getStreamContext(TransportId t, byte[] tag) public StreamContext getStreamContext(TransportId t, byte[] tag)
throws DbException { throws DbException {
TransportKeyManager m = managers.get(t); TransportKeyManager m = managers.get(t);
@@ -141,6 +146,7 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
return ctx; return ctx;
} }
@Override
public void eventOccurred(Event e) { public void eventOccurred(Event e) {
if (e instanceof ContactRemovedEvent) { if (e instanceof ContactRemovedEvent) {
removeContact(((ContactRemovedEvent) e).getContactId()); removeContact(((ContactRemovedEvent) e).getContactId());
@@ -154,6 +160,7 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
private void removeContact(final ContactId c) { private void removeContact(final ContactId c) {
activeContacts.remove(c); activeContacts.remove(c);
dbExecutor.execute(new Runnable() { dbExecutor.execute(new Runnable() {
@Override
public void run() { public void run() {
for (TransportKeyManager m : managers.values()) for (TransportKeyManager m : managers.values())
m.removeContact(c); m.removeContact(c);

View File

@@ -9,7 +9,6 @@ import org.briarproject.api.db.DatabaseComponent;
import org.briarproject.api.db.DbException; import org.briarproject.api.db.DbException;
import org.briarproject.api.db.Transaction; import org.briarproject.api.db.Transaction;
import org.briarproject.api.system.Clock; import org.briarproject.api.system.Clock;
import org.briarproject.api.system.Timer;
import org.briarproject.api.transport.StreamContext; import org.briarproject.api.transport.StreamContext;
import org.briarproject.api.transport.TransportKeys; import org.briarproject.api.transport.TransportKeys;
import org.briarproject.transport.ReorderingWindow.Change; import org.briarproject.transport.ReorderingWindow.Change;
@@ -18,10 +17,12 @@ import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.TimerTask; import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Logger; import java.util.logging.Logger;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static org.briarproject.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE; import static org.briarproject.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE;
import static org.briarproject.api.transport.TransportConstants.TAG_LENGTH; import static org.briarproject.api.transport.TransportConstants.TAG_LENGTH;
@@ -34,7 +35,8 @@ class TransportKeyManager {
private final DatabaseComponent db; private final DatabaseComponent db;
private final CryptoComponent crypto; private final CryptoComponent crypto;
private final Timer timer; private final Executor dbExecutor;
private final ScheduledExecutorService scheduler;
private final Clock clock; private final Clock clock;
private final TransportId transportId; private final TransportId transportId;
private final long rotationPeriodLength; private final long rotationPeriodLength;
@@ -46,11 +48,12 @@ class TransportKeyManager {
private final Map<ContactId, MutableTransportKeys> keys; private final Map<ContactId, MutableTransportKeys> keys;
TransportKeyManager(DatabaseComponent db, CryptoComponent crypto, TransportKeyManager(DatabaseComponent db, CryptoComponent crypto,
Timer timer, Clock clock, TransportId transportId, Executor dbExecutor, ScheduledExecutorService scheduler,
long maxLatency) { Clock clock, TransportId transportId, long maxLatency) {
this.db = db; this.db = db;
this.crypto = crypto; this.crypto = crypto;
this.timer = timer; this.dbExecutor = dbExecutor;
this.scheduler = scheduler;
this.clock = clock; this.clock = clock;
this.transportId = transportId; this.transportId = transportId;
rotationPeriodLength = maxLatency + MAX_CLOCK_DIFFERENCE; rotationPeriodLength = maxLatency + MAX_CLOCK_DIFFERENCE;
@@ -122,7 +125,19 @@ class TransportKeyManager {
} }
private void scheduleKeyRotation(long now) { private void scheduleKeyRotation(long now) {
TimerTask task = new TimerTask() { Runnable task = new Runnable() {
@Override
public void run() {
rotateKeys();
}
};
long delay = rotationPeriodLength - now % rotationPeriodLength;
scheduler.schedule(task, delay, MILLISECONDS);
}
private void rotateKeys() {
dbExecutor.execute(new Runnable() {
@Override
public void run() { public void run() {
try { try {
Transaction txn = db.startTransaction(false); Transaction txn = db.startTransaction(false);
@@ -137,9 +152,7 @@ class TransportKeyManager {
LOG.log(WARNING, e.toString(), e); LOG.log(WARNING, e.toString(), e);
} }
} }
}; });
long delay = rotationPeriodLength - now % rotationPeriodLength;
timer.schedule(task, delay);
} }
void addContact(Transaction txn, ContactId c, SecretKey master, void addContact(Transaction txn, ContactId c, SecretKey master,

View File

@@ -18,7 +18,8 @@ import dagger.Provides;
public class TransportModule { public class TransportModule {
public static class EagerSingletons { public static class EagerSingletons {
@Inject KeyManager keyManager; @Inject
KeyManager keyManager;
} }
@Provides @Provides
@@ -35,7 +36,7 @@ public class TransportModule {
@Provides @Provides
@Singleton @Singleton
KeyManager getKeyManager(LifecycleManager lifecycleManager, KeyManager provideKeyManager(LifecycleManager lifecycleManager,
EventBus eventBus, KeyManagerImpl keyManager) { EventBus eventBus, KeyManagerImpl keyManager) {
lifecycleManager.registerService(keyManager); lifecycleManager.registerService(keyManager);
eventBus.addListener(keyManager); eventBus.addListener(keyManager);

View File

@@ -0,0 +1,19 @@
package org.briarproject.system;
import org.briarproject.api.system.SeedProvider;
import org.briarproject.util.OsUtils;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
@Module
public class DesktopSeedProviderModule {
@Provides
@Singleton
SeedProvider provideSeedProvider() {
return OsUtils.isLinux() ? new LinuxSeedProvider() : null;
}
}

View File

@@ -1,31 +0,0 @@
package org.briarproject.system;
import org.briarproject.api.system.Clock;
import org.briarproject.api.system.SeedProvider;
import org.briarproject.api.system.Timer;
import org.briarproject.util.OsUtils;
import dagger.Module;
import dagger.Provides;
@Module
public class DesktopSystemModule {
@Provides
Clock provideClock() {
return new SystemClock();
}
@Provides
Timer provideTimer() {
return new SystemTimer();
}
@Provides
SeedProvider provideSeedProvider() {
if (OsUtils.isLinux()) {
return new LinuxSeedProvider();
}
return null;
}
}

View File

@@ -0,0 +1,20 @@
package org.briarproject;
import org.hamcrest.Description;
import org.jmock.api.Action;
import org.jmock.api.Invocation;
public class RunAction implements Action {
@Override
public Object invoke(Invocation invocation) throws Throwable {
Runnable task = (Runnable) invocation.getParameter(0);
task.run();
return null;
}
@Override
public void describeTo(Description description) {
description.appendText("runs a runnable");
}
}

View File

@@ -0,0 +1,18 @@
package org.briarproject;
import org.briarproject.api.system.SeedProvider;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
@Module
public class TestSeedProviderModule {
@Provides
@Singleton
SeedProvider provideSeedProvider() {
return new TestSeedProvider();
}
}

View File

@@ -1,29 +0,0 @@
package org.briarproject;
import org.briarproject.api.system.Clock;
import org.briarproject.api.system.SeedProvider;
import org.briarproject.api.system.Timer;
import org.briarproject.system.SystemClock;
import org.briarproject.system.SystemTimer;
import dagger.Module;
import dagger.Provides;
@Module
public class TestSystemModule {
@Provides
Clock provideClock() {
return new SystemClock();
}
@Provides
Timer provideSystemTimer() {
return new SystemTimer();
}
@Provides
SeedProvider provideSeedProvider() {
return new TestSeedProvider();
}
}

View File

@@ -1,20 +1,13 @@
package org.briarproject.plugins; package org.briarproject.plugins;
import org.briarproject.BriarTestCase; import org.briarproject.BriarTestCase;
import org.briarproject.ImmediateExecutor;
import org.briarproject.api.TransportId; import org.briarproject.api.TransportId;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.event.ContactStatusChangedEvent;
import org.briarproject.api.event.EventBus; import org.briarproject.api.event.EventBus;
import org.briarproject.api.event.EventListener;
import org.briarproject.api.plugins.ConnectionManager; import org.briarproject.api.plugins.ConnectionManager;
import org.briarproject.api.plugins.ConnectionRegistry;
import org.briarproject.api.plugins.PluginConfig; import org.briarproject.api.plugins.PluginConfig;
import org.briarproject.api.plugins.TransportConnectionWriter;
import org.briarproject.api.plugins.duplex.DuplexPlugin; import org.briarproject.api.plugins.duplex.DuplexPlugin;
import org.briarproject.api.plugins.duplex.DuplexPluginCallback; import org.briarproject.api.plugins.duplex.DuplexPluginCallback;
import org.briarproject.api.plugins.duplex.DuplexPluginFactory; import org.briarproject.api.plugins.duplex.DuplexPluginFactory;
import org.briarproject.api.plugins.duplex.DuplexTransportConnection;
import org.briarproject.api.plugins.simplex.SimplexPlugin; import org.briarproject.api.plugins.simplex.SimplexPlugin;
import org.briarproject.api.plugins.simplex.SimplexPluginCallback; import org.briarproject.api.plugins.simplex.SimplexPluginCallback;
import org.briarproject.api.plugins.simplex.SimplexPluginFactory; import org.briarproject.api.plugins.simplex.SimplexPluginFactory;
@@ -40,11 +33,8 @@ public class PluginManagerImplTest extends BriarTestCase {
final Executor ioExecutor = Executors.newSingleThreadExecutor(); final Executor ioExecutor = Executors.newSingleThreadExecutor();
final EventBus eventBus = context.mock(EventBus.class); final EventBus eventBus = context.mock(EventBus.class);
final PluginConfig pluginConfig = context.mock(PluginConfig.class); final PluginConfig pluginConfig = context.mock(PluginConfig.class);
final Poller poller = context.mock(Poller.class);
final ConnectionManager connectionManager = final ConnectionManager connectionManager =
context.mock(ConnectionManager.class); context.mock(ConnectionManager.class);
final ConnectionRegistry connectionRegistry =
context.mock(ConnectionRegistry.class);
final SettingsManager settingsManager = final SettingsManager settingsManager =
context.mock(SettingsManager.class); context.mock(SettingsManager.class);
final TransportPropertyManager transportPropertyManager = final TransportPropertyManager transportPropertyManager =
@@ -72,6 +62,12 @@ public class PluginManagerImplTest extends BriarTestCase {
final TransportId duplexFailId = new TransportId("duplex1"); final TransportId duplexFailId = new TransportId("duplex1");
context.checking(new Expectations() {{ context.checking(new Expectations() {{
allowing(simplexPlugin).getId();
will(returnValue(simplexId));
allowing(simplexFailPlugin).getId();
will(returnValue(simplexFailId));
allowing(duplexPlugin).getId();
will(returnValue(duplexId));
// start() // start()
// First simplex plugin // First simplex plugin
oneOf(pluginConfig).getSimplexFactories(); oneOf(pluginConfig).getSimplexFactories();
@@ -108,21 +104,16 @@ public class PluginManagerImplTest extends BriarTestCase {
oneOf(duplexFailFactory).createPlugin(with(any( oneOf(duplexFailFactory).createPlugin(with(any(
DuplexPluginCallback.class))); DuplexPluginCallback.class)));
will(returnValue(null)); // Failed to create a plugin will(returnValue(null)); // Failed to create a plugin
// Start listening for events
oneOf(eventBus).addListener(with(any(EventListener.class)));
// stop() // stop()
// Stop listening for events
oneOf(eventBus).removeListener(with(any(EventListener.class)));
// Stop the poller
oneOf(poller).stop();
// Stop the plugins // Stop the plugins
oneOf(simplexPlugin).stop(); oneOf(simplexPlugin).stop();
oneOf(simplexFailPlugin).stop();
oneOf(duplexPlugin).stop(); oneOf(duplexPlugin).stop();
}}); }});
PluginManagerImpl p = new PluginManagerImpl(ioExecutor, eventBus, PluginManagerImpl p = new PluginManagerImpl(ioExecutor, eventBus,
pluginConfig, poller, connectionManager, connectionRegistry, pluginConfig, connectionManager, settingsManager,
settingsManager, transportPropertyManager, uiCallback); transportPropertyManager, uiCallback);
// Two plugins should be started and stopped // Two plugins should be started and stopped
p.startService(); p.startService();
@@ -130,141 +121,4 @@ public class PluginManagerImplTest extends BriarTestCase {
context.assertIsSatisfied(); context.assertIsSatisfied();
} }
@Test
public void testConnectToNewContact() throws Exception {
Mockery context = new Mockery();
final Executor ioExecutor = new ImmediateExecutor();
final EventBus eventBus = context.mock(EventBus.class);
final PluginConfig pluginConfig = context.mock(PluginConfig.class);
final Poller poller = context.mock(Poller.class);
final ConnectionManager connectionManager =
context.mock(ConnectionManager.class);
final ConnectionRegistry connectionRegistry =
context.mock(ConnectionRegistry.class);
final SettingsManager settingsManager =
context.mock(SettingsManager.class);
final TransportPropertyManager transportPropertyManager =
context.mock(TransportPropertyManager.class);
final UiCallback uiCallback = context.mock(UiCallback.class);
final TransportConnectionWriter transportConnectionWriter =
context.mock(TransportConnectionWriter.class);
final DuplexTransportConnection duplexTransportConnection =
context.mock(DuplexTransportConnection.class);
final ContactId contactId = new ContactId(234);
// Two simplex plugins: one supports polling, the other doesn't
final SimplexPluginFactory simplexFactory =
context.mock(SimplexPluginFactory.class);
final SimplexPlugin simplexPlugin = context.mock(SimplexPlugin.class);
final TransportId simplexId = new TransportId("simplex");
final SimplexPluginFactory simplexFactory1 =
context.mock(SimplexPluginFactory.class, "simplexFactory1");
final SimplexPlugin simplexPlugin1 =
context.mock(SimplexPlugin.class, "simplexPlugin1");
final TransportId simplexId1 = new TransportId("simplex1");
// Two duplex plugins: one supports polling, the other doesn't
final DuplexPluginFactory duplexFactory =
context.mock(DuplexPluginFactory.class);
final DuplexPlugin duplexPlugin = context.mock(DuplexPlugin.class);
final TransportId duplexId = new TransportId("duplex");
final DuplexPluginFactory duplexFactory1 =
context.mock(DuplexPluginFactory.class, "duplexFactory1");
final DuplexPlugin duplexPlugin1 =
context.mock(DuplexPlugin.class, "duplexPlugin1");
final TransportId duplexId1 = new TransportId("duplex1");
context.checking(new Expectations() {{
// start()
// First simplex plugin
oneOf(pluginConfig).getSimplexFactories();
will(returnValue(Arrays.asList(simplexFactory, simplexFactory1)));
oneOf(simplexFactory).getId();
will(returnValue(simplexId));
oneOf(simplexFactory).createPlugin(with(any(
SimplexPluginCallback.class)));
will(returnValue(simplexPlugin)); // Created
oneOf(simplexPlugin).start();
will(returnValue(true)); // Started
// Second simplex plugin
oneOf(simplexFactory1).getId();
will(returnValue(simplexId1));
oneOf(simplexFactory1).createPlugin(with(any(
SimplexPluginCallback.class)));
will(returnValue(simplexPlugin1)); // Created
oneOf(simplexPlugin1).start();
will(returnValue(true)); // Started
// First duplex plugin
oneOf(pluginConfig).getDuplexFactories();
will(returnValue(Arrays.asList(duplexFactory, duplexFactory1)));
oneOf(duplexFactory).getId();
will(returnValue(duplexId));
oneOf(duplexFactory).createPlugin(with(any(
DuplexPluginCallback.class)));
will(returnValue(duplexPlugin)); // Created
oneOf(duplexPlugin).start();
will(returnValue(true)); // Started
// Second duplex plugin
oneOf(duplexFactory1).getId();
will(returnValue(duplexId1));
oneOf(duplexFactory1).createPlugin(with(any(
DuplexPluginCallback.class)));
will(returnValue(duplexPlugin1)); // Created
oneOf(duplexPlugin1).start();
will(returnValue(true)); // Started
// Start listening for events
oneOf(eventBus).addListener(with(any(EventListener.class)));
// eventOccurred()
// First simplex plugin
oneOf(simplexPlugin).shouldPoll();
will(returnValue(true));
oneOf(simplexPlugin).getId();
will(returnValue(simplexId));
oneOf(connectionRegistry).isConnected(contactId, simplexId);
will(returnValue(false));
oneOf(simplexPlugin).createWriter(contactId);
will(returnValue(transportConnectionWriter));
oneOf(connectionManager).manageOutgoingConnection(contactId,
simplexId, transportConnectionWriter);
// Second simplex plugin
oneOf(simplexPlugin1).shouldPoll();
will(returnValue(false));
// First duplex plugin
oneOf(duplexPlugin).shouldPoll();
will(returnValue(true));
oneOf(duplexPlugin).getId();
will(returnValue(duplexId));
oneOf(connectionRegistry).isConnected(contactId, duplexId);
will(returnValue(false));
oneOf(duplexPlugin).createConnection(contactId);
will(returnValue(duplexTransportConnection));
oneOf(connectionManager).manageOutgoingConnection(contactId,
duplexId, duplexTransportConnection);
// Second duplex plugin
oneOf(duplexPlugin1).shouldPoll();
will(returnValue(false));
// stop()
// Stop listening for events
oneOf(eventBus).removeListener(with(any(EventListener.class)));
// Stop the poller
oneOf(poller).stop();
// Stop the plugins
oneOf(simplexPlugin).stop();
oneOf(simplexPlugin1).stop();
oneOf(duplexPlugin).stop();
oneOf(duplexPlugin1).stop();
}});
PluginManagerImpl p = new PluginManagerImpl(ioExecutor, eventBus,
pluginConfig, poller, connectionManager, connectionRegistry,
settingsManager, transportPropertyManager, uiCallback);
p.startService();
p.eventOccurred(new ContactStatusChangedEvent(contactId, true));
p.stopService();
context.assertIsSatisfied();
}
} }

View File

@@ -0,0 +1,354 @@
package org.briarproject.plugins;
import org.briarproject.BriarTestCase;
import org.briarproject.ImmediateExecutor;
import org.briarproject.RunAction;
import org.briarproject.api.TransportId;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.event.ConnectionClosedEvent;
import org.briarproject.api.event.ConnectionOpenedEvent;
import org.briarproject.api.event.ContactStatusChangedEvent;
import org.briarproject.api.event.TransportEnabledEvent;
import org.briarproject.api.plugins.ConnectionManager;
import org.briarproject.api.plugins.ConnectionRegistry;
import org.briarproject.api.plugins.Plugin;
import org.briarproject.api.plugins.PluginManager;
import org.briarproject.api.plugins.TransportConnectionWriter;
import org.briarproject.api.plugins.duplex.DuplexPlugin;
import org.briarproject.api.plugins.duplex.DuplexTransportConnection;
import org.briarproject.api.plugins.simplex.SimplexPlugin;
import org.briarproject.api.system.Clock;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.jmock.lib.legacy.ClassImposteriser;
import org.junit.Test;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
public class PollerTest extends BriarTestCase {
private final ContactId contactId = new ContactId(234);
private final int pollingInterval = 60 * 1000;
private final long now = System.currentTimeMillis();
@Test
public void testConnectOnContactStatusChanged() throws Exception {
Mockery context = new Mockery();
context.setImposteriser(ClassImposteriser.INSTANCE);
final Executor ioExecutor = new ImmediateExecutor();
final ScheduledExecutorService scheduler =
context.mock(ScheduledExecutorService.class);
final ConnectionManager connectionManager =
context.mock(ConnectionManager.class);
final ConnectionRegistry connectionRegistry =
context.mock(ConnectionRegistry.class);
final PluginManager pluginManager = context.mock(PluginManager.class);
final SecureRandom random = context.mock(SecureRandom.class);
final Clock clock = context.mock(Clock.class);
// Two simplex plugins: one supports polling, the other doesn't
final SimplexPlugin simplexPlugin = context.mock(SimplexPlugin.class);
final SimplexPlugin simplexPlugin1 =
context.mock(SimplexPlugin.class, "simplexPlugin1");
final TransportId simplexId1 = new TransportId("simplex1");
final List<SimplexPlugin> simplexPlugins = Arrays.asList(simplexPlugin,
simplexPlugin1);
final TransportConnectionWriter simplexWriter =
context.mock(TransportConnectionWriter.class);
// Two duplex plugins: one supports polling, the other doesn't
final DuplexPlugin duplexPlugin = context.mock(DuplexPlugin.class);
final TransportId duplexId = new TransportId("duplex");
final DuplexPlugin duplexPlugin1 =
context.mock(DuplexPlugin.class, "duplexPlugin1");
final List<DuplexPlugin> duplexPlugins = Arrays.asList(duplexPlugin,
duplexPlugin1);
final DuplexTransportConnection duplexConnection =
context.mock(DuplexTransportConnection.class);
context.checking(new Expectations() {{
// Get the simplex plugins
oneOf(pluginManager).getSimplexPlugins();
will(returnValue(simplexPlugins));
// The first plugin doesn't support polling
oneOf(simplexPlugin).shouldPoll();
will(returnValue(false));
// The second plugin supports polling
oneOf(simplexPlugin1).shouldPoll();
will(returnValue(true));
// Check whether the contact is already connected
oneOf(simplexPlugin1).getId();
will(returnValue(simplexId1));
oneOf(connectionRegistry).isConnected(contactId, simplexId1);
will(returnValue(false));
// Connect to the contact
oneOf(simplexPlugin1).createWriter(contactId);
will(returnValue(simplexWriter));
// Pass the connection to the connection manager
oneOf(connectionManager).manageOutgoingConnection(contactId,
simplexId1, simplexWriter);
// Get the duplex plugins
oneOf(pluginManager).getDuplexPlugins();
will(returnValue(duplexPlugins));
// The first plugin supports polling
oneOf(duplexPlugin).shouldPoll();
will(returnValue(true));
// Check whether the contact is already connected
oneOf(duplexPlugin).getId();
will(returnValue(duplexId));
oneOf(connectionRegistry).isConnected(contactId, duplexId);
will(returnValue(false));
// Connect to the contact
oneOf(duplexPlugin).createConnection(contactId);
will(returnValue(duplexConnection));
// Pass the connection to the connection manager
oneOf(connectionManager).manageOutgoingConnection(contactId,
duplexId, duplexConnection);
// The second plugin doesn't support polling
oneOf(duplexPlugin1).shouldPoll();
will(returnValue(false));
}});
Poller p = new Poller(ioExecutor, scheduler, connectionManager,
connectionRegistry, pluginManager, random, clock);
p.eventOccurred(new ContactStatusChangedEvent(contactId, true));
context.assertIsSatisfied();
}
@Test
public void testRescheduleAndReconnectOnConnectionClosed()
throws Exception {
Mockery context = new Mockery();
context.setImposteriser(ClassImposteriser.INSTANCE);
final Executor ioExecutor = new ImmediateExecutor();
final ScheduledExecutorService scheduler =
context.mock(ScheduledExecutorService.class);
final ConnectionManager connectionManager =
context.mock(ConnectionManager.class);
final ConnectionRegistry connectionRegistry =
context.mock(ConnectionRegistry.class);
final PluginManager pluginManager = context.mock(PluginManager.class);
final SecureRandom random = context.mock(SecureRandom.class);
final Clock clock = context.mock(Clock.class);
final DuplexPlugin plugin = context.mock(DuplexPlugin.class);
final TransportId transportId = new TransportId("id");
final DuplexTransportConnection duplexConnection =
context.mock(DuplexTransportConnection.class);
context.checking(new Expectations() {{
allowing(plugin).getId();
will(returnValue(transportId));
// reschedule()
// Get the plugin
oneOf(pluginManager).getPlugin(transportId);
will(returnValue(plugin));
// The plugin supports polling
oneOf(plugin).shouldPoll();
will(returnValue(true));
// Get the plugin
oneOf(pluginManager).getPlugin(transportId);
will(returnValue(plugin));
// The plugin supports polling
oneOf(plugin).shouldPoll();
will(returnValue(true));
// Schedule the next poll
oneOf(plugin).getPollingInterval();
will(returnValue(pollingInterval));
oneOf(clock).currentTimeMillis();
will(returnValue(now));
oneOf(scheduler).schedule(with(any(Runnable.class)),
with((long) pollingInterval), with(MILLISECONDS));
// connectToContact()
// Check whether the contact is already connected
oneOf(connectionRegistry).isConnected(contactId, transportId);
will(returnValue(false));
// Connect to the contact
oneOf(plugin).createConnection(contactId);
will(returnValue(duplexConnection));
// Pass the connection to the connection manager
oneOf(connectionManager).manageOutgoingConnection(contactId,
transportId, duplexConnection);
}});
Poller p = new Poller(ioExecutor, scheduler, connectionManager,
connectionRegistry, pluginManager, random, clock);
p.eventOccurred(new ConnectionClosedEvent(contactId, transportId,
false));
context.assertIsSatisfied();
}
@Test
public void testRescheduleOnConnectionOpened() throws Exception {
Mockery context = new Mockery();
context.setImposteriser(ClassImposteriser.INSTANCE);
final Executor ioExecutor = new ImmediateExecutor();
final ScheduledExecutorService scheduler =
context.mock(ScheduledExecutorService.class);
final ConnectionManager connectionManager =
context.mock(ConnectionManager.class);
final ConnectionRegistry connectionRegistry =
context.mock(ConnectionRegistry.class);
final PluginManager pluginManager = context.mock(PluginManager.class);
final SecureRandom random = context.mock(SecureRandom.class);
final Clock clock = context.mock(Clock.class);
final DuplexPlugin plugin = context.mock(DuplexPlugin.class);
final TransportId transportId = new TransportId("id");
context.checking(new Expectations() {{
allowing(plugin).getId();
will(returnValue(transportId));
// Get the plugin
oneOf(pluginManager).getPlugin(transportId);
will(returnValue(plugin));
// The plugin supports polling
oneOf(plugin).shouldPoll();
will(returnValue(true));
// Schedule the next poll
oneOf(plugin).getPollingInterval();
will(returnValue(pollingInterval));
oneOf(clock).currentTimeMillis();
will(returnValue(now));
oneOf(scheduler).schedule(with(any(Runnable.class)),
with((long) pollingInterval), with(MILLISECONDS));
}});
Poller p = new Poller(ioExecutor, scheduler, connectionManager,
connectionRegistry, pluginManager, random, clock);
p.eventOccurred(new ConnectionOpenedEvent(contactId, transportId,
false));
context.assertIsSatisfied();
}
@Test
public void testRescheduleDoesNotReplaceEarlierTask() throws Exception {
Mockery context = new Mockery();
context.setImposteriser(ClassImposteriser.INSTANCE);
final Executor ioExecutor = new ImmediateExecutor();
final ScheduledExecutorService scheduler =
context.mock(ScheduledExecutorService.class);
final ConnectionManager connectionManager =
context.mock(ConnectionManager.class);
final ConnectionRegistry connectionRegistry =
context.mock(ConnectionRegistry.class);
final PluginManager pluginManager = context.mock(PluginManager.class);
final SecureRandom random = context.mock(SecureRandom.class);
final Clock clock = context.mock(Clock.class);
final DuplexPlugin plugin = context.mock(DuplexPlugin.class);
final TransportId transportId = new TransportId("id");
context.checking(new Expectations() {{
allowing(plugin).getId();
will(returnValue(transportId));
// First event
// Get the plugin
oneOf(pluginManager).getPlugin(transportId);
will(returnValue(plugin));
// The plugin supports polling
oneOf(plugin).shouldPoll();
will(returnValue(true));
// Schedule the next poll
oneOf(plugin).getPollingInterval();
will(returnValue(pollingInterval));
oneOf(clock).currentTimeMillis();
will(returnValue(now));
oneOf(scheduler).schedule(with(any(Runnable.class)),
with((long) pollingInterval), with(MILLISECONDS));
// Second event
// Get the plugin
oneOf(pluginManager).getPlugin(transportId);
will(returnValue(plugin));
// The plugin supports polling
oneOf(plugin).shouldPoll();
will(returnValue(true));
// Don't replace the previously scheduled task, due earlier
oneOf(plugin).getPollingInterval();
will(returnValue(pollingInterval));
oneOf(clock).currentTimeMillis();
will(returnValue(now + 1));
}});
Poller p = new Poller(ioExecutor, scheduler, connectionManager,
connectionRegistry, pluginManager, random, clock);
p.eventOccurred(new ConnectionOpenedEvent(contactId, transportId,
false));
p.eventOccurred(new ConnectionOpenedEvent(contactId, transportId,
false));
context.assertIsSatisfied();
}
@Test
public void testPollOnTransportEnabled() throws Exception {
Mockery context = new Mockery();
context.setImposteriser(ClassImposteriser.INSTANCE);
final Executor ioExecutor = new ImmediateExecutor();
final ScheduledExecutorService scheduler =
context.mock(ScheduledExecutorService.class);
final ConnectionManager connectionManager =
context.mock(ConnectionManager.class);
final ConnectionRegistry connectionRegistry =
context.mock(ConnectionRegistry.class);
final PluginManager pluginManager = context.mock(PluginManager.class);
final SecureRandom random = context.mock(SecureRandom.class);
final Clock clock = context.mock(Clock.class);
final Plugin plugin = context.mock(Plugin.class);
final TransportId transportId = new TransportId("id");
final List<ContactId> connected = Collections.singletonList(contactId);
context.checking(new Expectations() {{
allowing(plugin).getId();
will(returnValue(transportId));
// Get the plugin
oneOf(pluginManager).getPlugin(transportId);
will(returnValue(plugin));
// The plugin supports polling
oneOf(plugin).shouldPoll();
will(returnValue(true));
// Schedule a polling task immediately
oneOf(clock).currentTimeMillis();
will(returnValue(now));
oneOf(scheduler).schedule(with(any(Runnable.class)), with(0L),
with(MILLISECONDS));
will(new RunAction());
// Running the polling task schedules the next polling task
oneOf(plugin).getPollingInterval();
will(returnValue(pollingInterval));
oneOf(random).nextDouble();
will(returnValue(0.5));
oneOf(clock).currentTimeMillis();
will(returnValue(now));
oneOf(scheduler).schedule(with(any(Runnable.class)),
with((long) (pollingInterval * 0.5)), with(MILLISECONDS));
// Poll the plugin
oneOf(connectionRegistry).getConnectedContacts(transportId);
will(returnValue(connected));
oneOf(plugin).poll(connected);
}});
Poller p = new Poller(ioExecutor, scheduler, connectionManager,
connectionRegistry, pluginManager, random, clock);
p.eventOccurred(new TransportEnabledEvent(transportId));
context.assertIsSatisfied();
}
}

View File

@@ -1,6 +1,7 @@
package org.briarproject.transport; package org.briarproject.transport;
import org.briarproject.BriarTestCase; import org.briarproject.BriarTestCase;
import org.briarproject.RunAction;
import org.briarproject.TestUtils; import org.briarproject.TestUtils;
import org.briarproject.api.TransportId; import org.briarproject.api.TransportId;
import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactId;
@@ -9,7 +10,6 @@ import org.briarproject.api.crypto.SecretKey;
import org.briarproject.api.db.DatabaseComponent; import org.briarproject.api.db.DatabaseComponent;
import org.briarproject.api.db.Transaction; import org.briarproject.api.db.Transaction;
import org.briarproject.api.system.Clock; import org.briarproject.api.system.Clock;
import org.briarproject.api.system.Timer;
import org.briarproject.api.transport.IncomingKeys; import org.briarproject.api.transport.IncomingKeys;
import org.briarproject.api.transport.OutgoingKeys; import org.briarproject.api.transport.OutgoingKeys;
import org.briarproject.api.transport.StreamContext; import org.briarproject.api.transport.StreamContext;
@@ -28,8 +28,10 @@ import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Random; import java.util.Random;
import java.util.TimerTask; import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.briarproject.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE; import static org.briarproject.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE;
import static org.briarproject.api.transport.TransportConstants.REORDERING_WINDOW_SIZE; import static org.briarproject.api.transport.TransportConstants.REORDERING_WINDOW_SIZE;
import static org.briarproject.api.transport.TransportConstants.TAG_LENGTH; import static org.briarproject.api.transport.TransportConstants.TAG_LENGTH;
@@ -56,11 +58,12 @@ public class TransportKeyManagerTest extends BriarTestCase {
Mockery context = new Mockery(); Mockery context = new Mockery();
final DatabaseComponent db = context.mock(DatabaseComponent.class); final DatabaseComponent db = context.mock(DatabaseComponent.class);
final CryptoComponent crypto = context.mock(CryptoComponent.class); final CryptoComponent crypto = context.mock(CryptoComponent.class);
final Timer timer = context.mock(Timer.class); final Executor dbExecutor = context.mock(Executor.class);
final ScheduledExecutorService scheduler =
context.mock(ScheduledExecutorService.class);
final Clock clock = context.mock(Clock.class); final Clock clock = context.mock(Clock.class);
final Map<ContactId, TransportKeys> loaded = final Map<ContactId, TransportKeys> loaded = new LinkedHashMap<>();
new LinkedHashMap<ContactId, TransportKeys>();
final TransportKeys shouldRotate = createTransportKeys(900, 0); final TransportKeys shouldRotate = createTransportKeys(900, 0);
final TransportKeys shouldNotRotate = createTransportKeys(1000, 0); final TransportKeys shouldNotRotate = createTransportKeys(1000, 0);
loaded.put(contactId, shouldRotate); loaded.put(contactId, shouldRotate);
@@ -90,12 +93,12 @@ public class TransportKeyManagerTest extends BriarTestCase {
oneOf(db).updateTransportKeys(txn, oneOf(db).updateTransportKeys(txn,
Collections.singletonMap(contactId, rotated)); Collections.singletonMap(contactId, rotated));
// Schedule key rotation at the start of the next rotation period // Schedule key rotation at the start of the next rotation period
oneOf(timer).schedule(with(any(TimerTask.class)), oneOf(scheduler).schedule(with(any(Runnable.class)),
with(rotationPeriodLength - 1)); with(rotationPeriodLength - 1), with(MILLISECONDS));
}}); }});
TransportKeyManager transportKeyManager = new TransportKeyManager(db, TransportKeyManager transportKeyManager = new TransportKeyManager(db,
crypto, timer, clock, transportId, maxLatency); crypto, dbExecutor, scheduler, clock, transportId, maxLatency);
transportKeyManager.start(txn); transportKeyManager.start(txn);
context.assertIsSatisfied(); context.assertIsSatisfied();
@@ -106,7 +109,9 @@ public class TransportKeyManagerTest extends BriarTestCase {
Mockery context = new Mockery(); Mockery context = new Mockery();
final DatabaseComponent db = context.mock(DatabaseComponent.class); final DatabaseComponent db = context.mock(DatabaseComponent.class);
final CryptoComponent crypto = context.mock(CryptoComponent.class); final CryptoComponent crypto = context.mock(CryptoComponent.class);
final Timer timer = context.mock(Timer.class); final Executor dbExecutor = context.mock(Executor.class);
final ScheduledExecutorService scheduler =
context.mock(ScheduledExecutorService.class);
final Clock clock = context.mock(Clock.class); final Clock clock = context.mock(Clock.class);
final boolean alice = true; final boolean alice = true;
@@ -135,7 +140,7 @@ public class TransportKeyManagerTest extends BriarTestCase {
}}); }});
TransportKeyManager transportKeyManager = new TransportKeyManager(db, TransportKeyManager transportKeyManager = new TransportKeyManager(db,
crypto, timer, clock, transportId, maxLatency); crypto, dbExecutor, scheduler, clock, transportId, maxLatency);
// The timestamp is 1 ms before the start of rotation period 1000 // The timestamp is 1 ms before the start of rotation period 1000
long timestamp = rotationPeriodLength * 1000 - 1; long timestamp = rotationPeriodLength * 1000 - 1;
transportKeyManager.addContact(txn, contactId, masterKey, timestamp, transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
@@ -150,13 +155,15 @@ public class TransportKeyManagerTest extends BriarTestCase {
Mockery context = new Mockery(); Mockery context = new Mockery();
final DatabaseComponent db = context.mock(DatabaseComponent.class); final DatabaseComponent db = context.mock(DatabaseComponent.class);
final CryptoComponent crypto = context.mock(CryptoComponent.class); final CryptoComponent crypto = context.mock(CryptoComponent.class);
final Timer timer = context.mock(Timer.class); final Executor dbExecutor = context.mock(Executor.class);
final ScheduledExecutorService scheduler =
context.mock(ScheduledExecutorService.class);
final Clock clock = context.mock(Clock.class); final Clock clock = context.mock(Clock.class);
final Transaction txn = new Transaction(null, false); final Transaction txn = new Transaction(null, false);
TransportKeyManager transportKeyManager = new TransportKeyManager(db, TransportKeyManager transportKeyManager = new TransportKeyManager(db,
crypto, timer, clock, transportId, maxLatency); crypto, dbExecutor, scheduler, clock, transportId, maxLatency);
assertNull(transportKeyManager.getStreamContext(txn, contactId)); assertNull(transportKeyManager.getStreamContext(txn, contactId));
context.assertIsSatisfied(); context.assertIsSatisfied();
@@ -168,7 +175,9 @@ public class TransportKeyManagerTest extends BriarTestCase {
Mockery context = new Mockery(); Mockery context = new Mockery();
final DatabaseComponent db = context.mock(DatabaseComponent.class); final DatabaseComponent db = context.mock(DatabaseComponent.class);
final CryptoComponent crypto = context.mock(CryptoComponent.class); final CryptoComponent crypto = context.mock(CryptoComponent.class);
final Timer timer = context.mock(Timer.class); final Executor dbExecutor = context.mock(Executor.class);
final ScheduledExecutorService scheduler =
context.mock(ScheduledExecutorService.class);
final Clock clock = context.mock(Clock.class); final Clock clock = context.mock(Clock.class);
final boolean alice = true; final boolean alice = true;
@@ -198,7 +207,7 @@ public class TransportKeyManagerTest extends BriarTestCase {
}}); }});
TransportKeyManager transportKeyManager = new TransportKeyManager(db, TransportKeyManager transportKeyManager = new TransportKeyManager(db,
crypto, timer, clock, transportId, maxLatency); crypto, dbExecutor, scheduler, clock, transportId, maxLatency);
// The timestamp is at the start of rotation period 1000 // The timestamp is at the start of rotation period 1000
long timestamp = rotationPeriodLength * 1000; long timestamp = rotationPeriodLength * 1000;
transportKeyManager.addContact(txn, contactId, masterKey, timestamp, transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
@@ -213,7 +222,9 @@ public class TransportKeyManagerTest extends BriarTestCase {
Mockery context = new Mockery(); Mockery context = new Mockery();
final DatabaseComponent db = context.mock(DatabaseComponent.class); final DatabaseComponent db = context.mock(DatabaseComponent.class);
final CryptoComponent crypto = context.mock(CryptoComponent.class); final CryptoComponent crypto = context.mock(CryptoComponent.class);
final Timer timer = context.mock(Timer.class); final Executor dbExecutor = context.mock(Executor.class);
final ScheduledExecutorService scheduler =
context.mock(ScheduledExecutorService.class);
final Clock clock = context.mock(Clock.class); final Clock clock = context.mock(Clock.class);
final boolean alice = true; final boolean alice = true;
@@ -245,7 +256,7 @@ public class TransportKeyManagerTest extends BriarTestCase {
}}); }});
TransportKeyManager transportKeyManager = new TransportKeyManager(db, TransportKeyManager transportKeyManager = new TransportKeyManager(db,
crypto, timer, clock, transportId, maxLatency); crypto, dbExecutor, scheduler, clock, transportId, maxLatency);
// The timestamp is at the start of rotation period 1000 // The timestamp is at the start of rotation period 1000
long timestamp = rotationPeriodLength * 1000; long timestamp = rotationPeriodLength * 1000;
transportKeyManager.addContact(txn, contactId, masterKey, timestamp, transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
@@ -271,7 +282,9 @@ public class TransportKeyManagerTest extends BriarTestCase {
Mockery context = new Mockery(); Mockery context = new Mockery();
final DatabaseComponent db = context.mock(DatabaseComponent.class); final DatabaseComponent db = context.mock(DatabaseComponent.class);
final CryptoComponent crypto = context.mock(CryptoComponent.class); final CryptoComponent crypto = context.mock(CryptoComponent.class);
final Timer timer = context.mock(Timer.class); final Executor dbExecutor = context.mock(Executor.class);
final ScheduledExecutorService scheduler =
context.mock(ScheduledExecutorService.class);
final Clock clock = context.mock(Clock.class); final Clock clock = context.mock(Clock.class);
final boolean alice = true; final boolean alice = true;
@@ -299,7 +312,7 @@ public class TransportKeyManagerTest extends BriarTestCase {
}}); }});
TransportKeyManager transportKeyManager = new TransportKeyManager(db, TransportKeyManager transportKeyManager = new TransportKeyManager(db,
crypto, timer, clock, transportId, maxLatency); crypto, dbExecutor, scheduler, clock, transportId, maxLatency);
// The timestamp is at the start of rotation period 1000 // The timestamp is at the start of rotation period 1000
long timestamp = rotationPeriodLength * 1000; long timestamp = rotationPeriodLength * 1000;
transportKeyManager.addContact(txn, contactId, masterKey, timestamp, transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
@@ -315,13 +328,15 @@ public class TransportKeyManagerTest extends BriarTestCase {
Mockery context = new Mockery(); Mockery context = new Mockery();
final DatabaseComponent db = context.mock(DatabaseComponent.class); final DatabaseComponent db = context.mock(DatabaseComponent.class);
final CryptoComponent crypto = context.mock(CryptoComponent.class); final CryptoComponent crypto = context.mock(CryptoComponent.class);
final Timer timer = context.mock(Timer.class); final Executor dbExecutor = context.mock(Executor.class);
final ScheduledExecutorService scheduler =
context.mock(ScheduledExecutorService.class);
final Clock clock = context.mock(Clock.class); final Clock clock = context.mock(Clock.class);
final boolean alice = true; final boolean alice = true;
final TransportKeys transportKeys = createTransportKeys(1000, 0); final TransportKeys transportKeys = createTransportKeys(1000, 0);
// Keep a copy of the tags // Keep a copy of the tags
final List<byte[]> tags = new ArrayList<byte[]>(); final List<byte[]> tags = new ArrayList<>();
final Transaction txn = new Transaction(null, false); final Transaction txn = new Transaction(null, false);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
@@ -352,7 +367,7 @@ public class TransportKeyManagerTest extends BriarTestCase {
}}); }});
TransportKeyManager transportKeyManager = new TransportKeyManager(db, TransportKeyManager transportKeyManager = new TransportKeyManager(db,
crypto, timer, clock, transportId, maxLatency); crypto, dbExecutor, scheduler, clock, transportId, maxLatency);
// The timestamp is at the start of rotation period 1000 // The timestamp is at the start of rotation period 1000
long timestamp = rotationPeriodLength * 1000; long timestamp = rotationPeriodLength * 1000;
transportKeyManager.addContact(txn, contactId, masterKey, timestamp, transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
@@ -381,7 +396,9 @@ public class TransportKeyManagerTest extends BriarTestCase {
Mockery context = new Mockery(); Mockery context = new Mockery();
final DatabaseComponent db = context.mock(DatabaseComponent.class); final DatabaseComponent db = context.mock(DatabaseComponent.class);
final CryptoComponent crypto = context.mock(CryptoComponent.class); final CryptoComponent crypto = context.mock(CryptoComponent.class);
final Timer timer = context.mock(Timer.class); final Executor dbExecutor = context.mock(Executor.class);
final ScheduledExecutorService scheduler =
context.mock(ScheduledExecutorService.class);
final Clock clock = context.mock(Clock.class); final Clock clock = context.mock(Clock.class);
final TransportKeys transportKeys = createTransportKeys(1000, 0); final TransportKeys transportKeys = createTransportKeys(1000, 0);
@@ -408,9 +425,11 @@ public class TransportKeyManagerTest extends BriarTestCase {
will(new EncodeTagAction()); will(new EncodeTagAction());
} }
// Schedule key rotation at the start of the next rotation period // Schedule key rotation at the start of the next rotation period
oneOf(timer).schedule(with(any(TimerTask.class)), oneOf(scheduler).schedule(with(any(Runnable.class)),
with(rotationPeriodLength)); with(rotationPeriodLength), with(MILLISECONDS));
will(new RunTimerTaskAction()); will(new RunAction());
oneOf(dbExecutor).execute(with(any(Runnable.class)));
will(new RunAction());
// Start a transaction for key rotation // Start a transaction for key rotation
oneOf(db).startTransaction(false); oneOf(db).startTransaction(false);
will(returnValue(txn1)); will(returnValue(txn1));
@@ -431,14 +450,14 @@ public class TransportKeyManagerTest extends BriarTestCase {
oneOf(db).updateTransportKeys(txn1, oneOf(db).updateTransportKeys(txn1,
Collections.singletonMap(contactId, rotated)); Collections.singletonMap(contactId, rotated));
// Schedule key rotation at the start of the next rotation period // Schedule key rotation at the start of the next rotation period
oneOf(timer).schedule(with(any(TimerTask.class)), oneOf(scheduler).schedule(with(any(Runnable.class)),
with(rotationPeriodLength)); with(rotationPeriodLength), with(MILLISECONDS));
// Commit the key rotation transaction // Commit the key rotation transaction
oneOf(db).endTransaction(txn1); oneOf(db).endTransaction(txn1);
}}); }});
TransportKeyManager transportKeyManager = new TransportKeyManager(db, TransportKeyManager transportKeyManager = new TransportKeyManager(db,
crypto, timer, clock, transportId, maxLatency); crypto, dbExecutor, scheduler, clock, transportId, maxLatency);
transportKeyManager.start(txn); transportKeyManager.start(txn);
assertTrue(txn1.isComplete()); assertTrue(txn1.isComplete());
@@ -483,19 +502,4 @@ public class TransportKeyManagerTest extends BriarTestCase {
description.appendText("encodes a tag"); description.appendText("encodes a tag");
} }
} }
private static class RunTimerTaskAction implements Action {
@Override
public Object invoke(Invocation invocation) throws Throwable {
TimerTask task = (TimerTask) invocation.getParameter(0);
task.run();
return null;
}
@Override
public void describeTo(Description description) {
description.appendText("schedules a timer task");
}
}
} }