Merge branch '272-transaction-isolation' into 'master'

Transaction isolation. #272

When client transactions were implemented the DB's read-write lock was removed, exposing H2's transaction isolation semantics. The default isolation level is "read committed", which allows concurrent transactions to overwrite each other's updates. This was the cause of #272. Changing H2's isolation level to "serialisable" would have caused other problems.

The solution is to reintroduce the DB's read-write lock. The lock is acquired when starting a transaction and released when committing or rolling back a transaction. (We already use try/finally blocks to ensure every transaction is committed or rolled back.) Read-only transactions can share the lock. To avoid deadlock, transactions must not be started while holding other locks.

This patch adapts the key manager to the new locking rules. The rest of the code was already compliant. Transports are now added to the DB during the startup phase, which allows TransportAddedEvent and TransportRemovedEvent to be deleted.

Fixes #269, fixes #272.

See merge request !124
This commit is contained in:
akwizgran
2016-03-30 09:40:33 +00:00
62 changed files with 1133 additions and 823 deletions

View File

@@ -1,6 +1,7 @@
package org.briarproject.sync; package org.briarproject.sync;
import org.briarproject.TestDatabaseModule; import org.briarproject.TestDatabaseModule;
import org.briarproject.TestPluginsModule;
import org.briarproject.TestSystemModule; import org.briarproject.TestSystemModule;
import org.briarproject.api.contact.ContactManager; import org.briarproject.api.contact.ContactManager;
import org.briarproject.api.db.DatabaseComponent; import org.briarproject.api.db.DatabaseComponent;
@@ -23,6 +24,7 @@ import org.briarproject.event.EventModule;
import org.briarproject.identity.IdentityModule; import org.briarproject.identity.IdentityModule;
import org.briarproject.lifecycle.LifecycleModule; import org.briarproject.lifecycle.LifecycleModule;
import org.briarproject.messaging.MessagingModule; import org.briarproject.messaging.MessagingModule;
import org.briarproject.plugins.PluginsModule;
import org.briarproject.transport.TransportModule; import org.briarproject.transport.TransportModule;
import javax.inject.Singleton; import javax.inject.Singleton;
@@ -30,23 +32,37 @@ import javax.inject.Singleton;
import dagger.Component; import dagger.Component;
@Singleton @Singleton
@Component(modules = {TestDatabaseModule.class, TestSystemModule.class, @Component(modules = {TestDatabaseModule.class, TestPluginsModule.class,
LifecycleModule.class, ContactModule.class, CryptoModule.class, TestSystemModule.class, LifecycleModule.class, ContactModule.class,
DatabaseModule.class, EventModule.class, SyncModule.class, CryptoModule.class, DatabaseModule.class, EventModule.class,
DataModule.class, TransportModule.class, IdentityModule.class, SyncModule.class, DataModule.class, TransportModule.class,
MessagingModule.class, ClientsModule.class}) IdentityModule.class, MessagingModule.class, ClientsModule.class,
PluginsModule.class})
public interface SimplexMessagingComponent { public interface SimplexMessagingComponent {
void inject(SimplexMessagingIntegrationTest testCase); void inject(SimplexMessagingIntegrationTest testCase);
LifecycleManager getLifeCycleManager();
LifecycleManager getLifecycleManager();
DatabaseComponent getDatabaseComponent(); DatabaseComponent getDatabaseComponent();
IdentityManager getIdentityManager(); IdentityManager getIdentityManager();
ContactManager getContactManager(); ContactManager getContactManager();
MessagingManager getMessagingManager(); MessagingManager getMessagingManager();
KeyManager getKeyManager(); KeyManager getKeyManager();
PrivateMessageFactory getPrivateMessageFactory(); PrivateMessageFactory getPrivateMessageFactory();
PacketWriterFactory getPacketWriterFactory(); PacketWriterFactory getPacketWriterFactory();
EventBus getEventBus(); EventBus getEventBus();
StreamWriterFactory getStreamWriterFactory(); StreamWriterFactory getStreamWriterFactory();
StreamReaderFactory getStreamReaderFactory(); StreamReaderFactory getStreamReaderFactory();
PacketReaderFactory getPacketReaderFactory(); PacketReaderFactory getPacketReaderFactory();
} }

View File

@@ -4,12 +4,10 @@ import org.briarproject.BriarTestCase;
import org.briarproject.ImmediateExecutor; import org.briarproject.ImmediateExecutor;
import org.briarproject.TestDatabaseModule; import org.briarproject.TestDatabaseModule;
import org.briarproject.TestUtils; import org.briarproject.TestUtils;
import org.briarproject.api.TransportId;
import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactId;
import org.briarproject.api.contact.ContactManager; import org.briarproject.api.contact.ContactManager;
import org.briarproject.api.crypto.SecretKey; 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.event.Event; 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.EventListener;
@@ -36,27 +34,26 @@ import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
import static org.briarproject.api.transport.TransportConstants.TAG_LENGTH;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
public class SimplexMessagingIntegrationTest extends BriarTestCase { import static org.briarproject.TestPluginsModule.MAX_LATENCY;
import static org.briarproject.TestPluginsModule.TRANSPORT_ID;
import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
import static org.briarproject.api.transport.TransportConstants.TAG_LENGTH;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
private static final int MAX_LATENCY = 2 * 60 * 1000; // 2 minutes public class SimplexMessagingIntegrationTest extends BriarTestCase {
private final File testDir = TestUtils.getTestDirectory(); private final File testDir = TestUtils.getTestDirectory();
private final File aliceDir = new File(testDir, "alice"); private final File aliceDir = new File(testDir, "alice");
private final File bobDir = new File(testDir, "bob"); private final File bobDir = new File(testDir, "bob");
private final TransportId transportId = new TransportId("id");
private final SecretKey master = TestUtils.createSecretKey(); private final SecretKey master = TestUtils.createSecretKey();
private final long timestamp = System.currentTimeMillis(); private final long timestamp = System.currentTimeMillis();
private final AuthorId aliceId = new AuthorId(TestUtils.getRandomId()); private final AuthorId aliceId = new AuthorId(TestUtils.getRandomId());
@@ -80,7 +77,7 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
private byte[] write() throws Exception { private byte[] write() throws Exception {
// Instantiate Alice's services // Instantiate Alice's services
LifecycleManager lifecycleManager = alice.getLifeCycleManager(); LifecycleManager lifecycleManager = alice.getLifecycleManager();
DatabaseComponent db = alice.getDatabaseComponent(); DatabaseComponent db = alice.getDatabaseComponent();
IdentityManager identityManager = alice.getIdentityManager(); IdentityManager identityManager = alice.getIdentityManager();
ContactManager contactManager = alice.getContactManager(); ContactManager contactManager = alice.getContactManager();
@@ -97,14 +94,6 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
// Start the lifecycle manager // Start the lifecycle manager
lifecycleManager.startServices(); lifecycleManager.startServices();
lifecycleManager.waitForStartup(); lifecycleManager.waitForStartup();
// Add a transport
Transaction txn = db.startTransaction();
try {
db.addTransport(txn, transportId, MAX_LATENCY);
txn.setComplete();
} finally {
db.endTransaction(txn);
}
// Add an identity for Alice // Add an identity for Alice
LocalAuthor aliceAuthor = new LocalAuthor(aliceId, "Alice", LocalAuthor aliceAuthor = new LocalAuthor(aliceId, "Alice",
new byte[MAX_PUBLIC_KEY_LENGTH], new byte[123], timestamp); new byte[MAX_PUBLIC_KEY_LENGTH], new byte[123], timestamp);
@@ -122,7 +111,8 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
groupId, timestamp, null, "text/plain", body); groupId, timestamp, null, "text/plain", body);
messagingManager.addLocalMessage(message); messagingManager.addLocalMessage(message);
// Get a stream context // Get a stream context
StreamContext ctx = keyManager.getStreamContext(contactId, transportId); StreamContext ctx = keyManager.getStreamContext(contactId,
TRANSPORT_ID);
assertNotNull(ctx); assertNotNull(ctx);
// Create a stream writer // Create a stream writer
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
@@ -132,8 +122,8 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
PacketWriter packetWriter = packetWriterFactory.createPacketWriter( PacketWriter packetWriter = packetWriterFactory.createPacketWriter(
streamWriter); streamWriter);
SyncSession session = new SimplexOutgoingSession(db, SyncSession session = new SimplexOutgoingSession(db,
new ImmediateExecutor(), eventBus, contactId, transportId, new ImmediateExecutor(), eventBus, contactId, MAX_LATENCY,
MAX_LATENCY, packetWriter); packetWriter);
// Write whatever needs to be written // Write whatever needs to be written
session.run(); session.run();
streamWriter.close(); streamWriter.close();
@@ -148,7 +138,7 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
private void read(byte[] stream) throws Exception { private void read(byte[] stream) throws Exception {
// Instantiate Bob's services // Instantiate Bob's services
LifecycleManager lifecycleManager = bob.getLifeCycleManager(); LifecycleManager lifecycleManager = bob.getLifecycleManager();
DatabaseComponent db = bob.getDatabaseComponent(); DatabaseComponent db = bob.getDatabaseComponent();
IdentityManager identityManager = bob.getIdentityManager(); IdentityManager identityManager = bob.getIdentityManager();
ContactManager contactManager = bob.getContactManager(); ContactManager contactManager = bob.getContactManager();
@@ -162,14 +152,6 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
// Start the lifecyle manager // Start the lifecyle manager
lifecycleManager.startServices(); lifecycleManager.startServices();
lifecycleManager.waitForStartup(); lifecycleManager.waitForStartup();
// Add a transport
Transaction txn = db.startTransaction();
try {
db.addTransport(txn, transportId, MAX_LATENCY);
txn.setComplete();
} finally {
db.endTransaction(txn);
}
// Add an identity for Bob // Add an identity for Bob
LocalAuthor bobAuthor = new LocalAuthor(bobId, "Bob", LocalAuthor bobAuthor = new LocalAuthor(bobId, "Bob",
new byte[MAX_PUBLIC_KEY_LENGTH], new byte[123], timestamp); new byte[MAX_PUBLIC_KEY_LENGTH], new byte[123], timestamp);
@@ -188,7 +170,7 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
byte[] tag = new byte[TAG_LENGTH]; byte[] tag = new byte[TAG_LENGTH];
int read = in.read(tag); int read = in.read(tag);
assertEquals(tag.length, read); assertEquals(tag.length, read);
StreamContext ctx = keyManager.getStreamContext(transportId, tag); StreamContext ctx = keyManager.getStreamContext(TRANSPORT_ID, tag);
assertNotNull(ctx); assertNotNull(ctx);
// Create a stream reader // Create a stream reader
InputStream streamReader = streamReaderFactory.createStreamReader( InputStream streamReader = streamReaderFactory.createStreamReader(
@@ -197,7 +179,7 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
PacketReader packetReader = packetReaderFactory.createPacketReader( PacketReader packetReader = packetReaderFactory.createPacketReader(
streamReader); streamReader);
SyncSession session = new IncomingSession(db, new ImmediateExecutor(), SyncSession session = new IncomingSession(db, new ImmediateExecutor(),
eventBus, contactId, transportId, packetReader); eventBus, contactId, packetReader);
// No messages should have been added yet // No messages should have been added yet
assertFalse(listener.messageAdded); assertFalse(listener.messageAdded);
// Read whatever needs to be read // Read whatever needs to be read

View File

@@ -7,9 +7,8 @@ import org.briarproject.android.api.AndroidExecutor;
import org.briarproject.api.event.EventBus; import org.briarproject.api.event.EventBus;
import org.briarproject.api.lifecycle.IoExecutor; import org.briarproject.api.lifecycle.IoExecutor;
import org.briarproject.api.plugins.BackoffFactory; import org.briarproject.api.plugins.BackoffFactory;
import org.briarproject.api.plugins.duplex.DuplexPluginConfig; 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.SimplexPluginConfig;
import org.briarproject.api.plugins.simplex.SimplexPluginFactory; import org.briarproject.api.plugins.simplex.SimplexPluginFactory;
import org.briarproject.api.system.LocationUtils; import org.briarproject.api.system.LocationUtils;
import org.briarproject.plugins.droidtooth.DroidtoothPluginFactory; import org.briarproject.plugins.droidtooth.DroidtoothPluginFactory;
@@ -29,17 +28,8 @@ import dagger.Provides;
public class AndroidPluginsModule { public class AndroidPluginsModule {
@Provides @Provides
SimplexPluginConfig provideSimplexPluginConfig() { public PluginConfig providePluginConfig(@IoExecutor Executor ioExecutor,
return new SimplexPluginConfig() { AndroidExecutor androidExecutor,
public Collection<SimplexPluginFactory> getFactories() {
return Collections.emptyList();
}
};
}
@Provides
public DuplexPluginConfig provideDuplexPluginConfig(
@IoExecutor Executor ioExecutor, AndroidExecutor androidExecutor,
SecureRandom random, BackoffFactory backoffFactory, Application app, SecureRandom random, BackoffFactory backoffFactory, Application app,
LocationUtils locationUtils, EventBus eventBus) { LocationUtils locationUtils, EventBus eventBus) {
Context appContext = app.getApplicationContext(); Context appContext = app.getApplicationContext();
@@ -49,13 +39,19 @@ public class AndroidPluginsModule {
locationUtils, eventBus); locationUtils, eventBus);
DuplexPluginFactory lan = new AndroidLanTcpPluginFactory(ioExecutor, DuplexPluginFactory lan = new AndroidLanTcpPluginFactory(ioExecutor,
backoffFactory, appContext); backoffFactory, appContext);
final Collection<DuplexPluginFactory> factories = final Collection<DuplexPluginFactory> duplex =
Arrays.asList(bluetooth, tor, lan); Arrays.asList(bluetooth, tor, lan);
return new DuplexPluginConfig() { return new PluginConfig() {
public Collection<DuplexPluginFactory> getFactories() {
return factories; @Override
public Collection<DuplexPluginFactory> getDuplexFactories() {
return duplex;
}
@Override
public Collection<SimplexPluginFactory> getSimplexFactories() {
return Collections.emptyList();
} }
}; };
} }
} }

View File

@@ -2,8 +2,8 @@ package org.briarproject.plugins.droidtooth;
import android.content.Context; import android.content.Context;
import org.briarproject.api.TransportId;
import org.briarproject.android.api.AndroidExecutor; import org.briarproject.android.api.AndroidExecutor;
import org.briarproject.api.TransportId;
import org.briarproject.api.plugins.Backoff; import org.briarproject.api.plugins.Backoff;
import org.briarproject.api.plugins.BackoffFactory; import org.briarproject.api.plugins.BackoffFactory;
import org.briarproject.api.plugins.duplex.DuplexPlugin; import org.briarproject.api.plugins.duplex.DuplexPlugin;
@@ -40,6 +40,10 @@ public class DroidtoothPluginFactory implements DuplexPluginFactory {
return DroidtoothPlugin.ID; return DroidtoothPlugin.ID;
} }
public int getMaxLatency() {
return MAX_LATENCY;
}
public DuplexPlugin createPlugin(DuplexPluginCallback callback) { public DuplexPlugin createPlugin(DuplexPluginCallback callback) {
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL, Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE); MAX_POLLING_INTERVAL, BACKOFF_BASE);

View File

@@ -1,16 +1,16 @@
package org.briarproject.plugins.droidtooth; package org.briarproject.plugins.droidtooth;
import java.io.IOException; import android.bluetooth.BluetoothSocket;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.concurrent.atomic.AtomicBoolean;
import org.briarproject.api.plugins.Plugin; import org.briarproject.api.plugins.Plugin;
import org.briarproject.api.plugins.TransportConnectionReader; import org.briarproject.api.plugins.TransportConnectionReader;
import org.briarproject.api.plugins.TransportConnectionWriter; import org.briarproject.api.plugins.TransportConnectionWriter;
import org.briarproject.api.plugins.duplex.DuplexTransportConnection; import org.briarproject.api.plugins.duplex.DuplexTransportConnection;
import android.bluetooth.BluetoothSocket; import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.concurrent.atomic.AtomicBoolean;
class DroidtoothTransportConnection implements DuplexTransportConnection { class DroidtoothTransportConnection implements DuplexTransportConnection {
@@ -39,10 +39,6 @@ class DroidtoothTransportConnection implements DuplexTransportConnection {
private class Reader implements TransportConnectionReader { private class Reader implements TransportConnectionReader {
public long getMaxLatency() {
return plugin.getMaxLatency();
}
public InputStream getInputStream() throws IOException { public InputStream getInputStream() throws IOException {
return socket.getInputStream(); return socket.getInputStream();
} }

View File

@@ -34,6 +34,10 @@ public class AndroidLanTcpPluginFactory implements DuplexPluginFactory {
return LanTcpPlugin.ID; return LanTcpPlugin.ID;
} }
public int getMaxLatency() {
return MAX_LATENCY;
}
public DuplexPlugin createPlugin(DuplexPluginCallback callback) { public DuplexPlugin createPlugin(DuplexPluginCallback callback) {
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL, Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE); MAX_POLLING_INTERVAL, BACKOFF_BASE);

View File

@@ -44,6 +44,10 @@ public class TorPluginFactory implements DuplexPluginFactory {
return TorPlugin.ID; return TorPlugin.ID;
} }
public int getMaxLatency() {
return MAX_LATENCY;
}
public DuplexPlugin createPlugin(DuplexPluginCallback callback) { public DuplexPlugin createPlugin(DuplexPluginCallback callback) {
// Check that we have a Tor binary for this architecture // Check that we have a Tor binary for this architecture

View File

@@ -1,16 +1,16 @@
package org.briarproject.plugins.tor; package org.briarproject.plugins.tor;
import org.briarproject.api.plugins.Plugin;
import org.briarproject.api.plugins.TransportConnectionReader;
import org.briarproject.api.plugins.TransportConnectionWriter;
import org.briarproject.api.plugins.duplex.DuplexTransportConnection;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.net.Socket; import java.net.Socket;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import org.briarproject.api.plugins.Plugin;
import org.briarproject.api.plugins.TransportConnectionReader;
import org.briarproject.api.plugins.TransportConnectionWriter;
import org.briarproject.api.plugins.duplex.DuplexTransportConnection;
class TorTransportConnection implements DuplexTransportConnection { class TorTransportConnection implements DuplexTransportConnection {
private final Plugin plugin; private final Plugin plugin;
@@ -38,10 +38,6 @@ class TorTransportConnection implements DuplexTransportConnection {
private class Reader implements TransportConnectionReader { private class Reader implements TransportConnectionReader {
public long getMaxLatency() {
return plugin.getMaxLatency();
}
public InputStream getInputStream() throws IOException { public InputStream getInputStream() throws IOException {
return socket.getInputStream(); return socket.getInputStream();
} }

View File

@@ -26,10 +26,6 @@ import java.util.Map;
/** /**
* Encapsulates the database implementation and exposes high-level operations * Encapsulates the database implementation and exposes high-level operations
* to other components. * to other components.
* <p/>
* This interface's methods are blocking, but they do not call out into other
* components except to broadcast {@link org.briarproject.api.event.Event
* Events}, so they can safely be called while holding locks.
*/ */
public interface DatabaseComponent { public interface DatabaseComponent {
@@ -45,8 +41,12 @@ public interface DatabaseComponent {
/** /**
* Starts a new transaction and returns an object representing it. * Starts a new transaction and returns an object representing it.
* <p/>
* This method acquires locks, so it must not be called while holding a
* lock.
* @param readOnly true if the transaction will only be used for reading.
*/ */
Transaction startTransaction() throws DbException; Transaction startTransaction(boolean readOnly) throws DbException;
/** /**
* Ends a transaction. If the transaction is marked as complete, the * Ends a transaction. If the transaction is marked as complete, the
@@ -142,70 +142,97 @@ public interface DatabaseComponent {
/** /**
* Returns the contact with the given ID. * Returns the contact with the given ID.
* <p/>
* Read-only.
*/ */
Contact getContact(Transaction txn, ContactId c) throws DbException; Contact getContact(Transaction txn, ContactId c) throws DbException;
/** /**
* Returns all contacts. * Returns all contacts.
* <p/>
* Read-only.
*/ */
Collection<Contact> getContacts(Transaction txn) throws DbException; Collection<Contact> getContacts(Transaction txn) throws DbException;
/** /**
* Returns all contacts associated with the given local pseudonym. * Returns all contacts associated with the given local pseudonym.
* <p/>
* Read-only.
*/ */
Collection<ContactId> getContacts(Transaction txn, AuthorId a) Collection<ContactId> getContacts(Transaction txn, AuthorId a)
throws DbException; throws DbException;
/** /**
* Returns the unique ID for this device. * Returns the unique ID for this device.
* <p/>
* Read-only.
*/ */
DeviceId getDeviceId(Transaction txn) throws DbException; DeviceId getDeviceId(Transaction txn) throws DbException;
/** /**
* Returns the group with the given ID. * Returns the group with the given ID.
* <p/>
* Read-only.
*/ */
Group getGroup(Transaction txn, GroupId g) throws DbException; Group getGroup(Transaction txn, GroupId g) throws DbException;
/** /**
* Returns the metadata for the given group. * Returns the metadata for the given group.
* <p/>
* Read-only.
*/ */
Metadata getGroupMetadata(Transaction txn, GroupId g) throws DbException; Metadata getGroupMetadata(Transaction txn, GroupId g) throws DbException;
/** /**
* Returns all groups belonging to the given client. * Returns all groups belonging to the given client.
* <p/>
* Read-only.
*/ */
Collection<Group> getGroups(Transaction txn, ClientId c) throws DbException; Collection<Group> getGroups(Transaction txn, ClientId c) throws DbException;
/** /**
* Returns the local pseudonym with the given ID. * Returns the local pseudonym with the given ID.
* <p/>
* Read-only.
*/ */
LocalAuthor getLocalAuthor(Transaction txn, AuthorId a) throws DbException; LocalAuthor getLocalAuthor(Transaction txn, AuthorId a) throws DbException;
/** /**
* Returns all local pseudonyms. * Returns all local pseudonyms.
* <p/>
* Read-only.
*/ */
Collection<LocalAuthor> getLocalAuthors(Transaction txn) throws DbException; Collection<LocalAuthor> getLocalAuthors(Transaction txn) throws DbException;
/** /**
* Returns the IDs of any messages that need to be validated by the given * Returns the IDs of any messages that need to be validated by the given
* client. * client.
* <p/>
* Read-only.
*/ */
Collection<MessageId> getMessagesToValidate(Transaction txn, ClientId c) Collection<MessageId> getMessagesToValidate(Transaction txn, ClientId c)
throws DbException; throws DbException;
/** /**
* Returns the message with the given ID, in serialised form. * Returns the message with the given ID, in serialised form, or null if
* the message has been deleted.
* <p/>
* Read-only.
*/ */
byte[] getRawMessage(Transaction txn, MessageId m) throws DbException; byte[] getRawMessage(Transaction txn, MessageId m) throws DbException;
/** /**
* Returns the metadata for all messages in the given group. * Returns the metadata for all messages in the given group.
* <p/>
* Read-only.
*/ */
Map<MessageId, Metadata> getMessageMetadata(Transaction txn, GroupId g) Map<MessageId, Metadata> getMessageMetadata(Transaction txn, GroupId g)
throws DbException; throws DbException;
/** /**
* Returns the metadata for the given message. * Returns the metadata for the given message.
* <p/>
* Read-only.
*/ */
Metadata getMessageMetadata(Transaction txn, MessageId m) Metadata getMessageMetadata(Transaction txn, MessageId m)
throws DbException; throws DbException;
@@ -213,6 +240,8 @@ public interface DatabaseComponent {
/** /**
* Returns the status of all messages in the given group with respect to * Returns the status of all messages in the given group with respect to
* the given contact. * the given contact.
* <p/>
* Read-only.
*/ */
Collection<MessageStatus> getMessageStatus(Transaction txn, ContactId c, Collection<MessageStatus> getMessageStatus(Transaction txn, ContactId c,
GroupId g) throws DbException; GroupId g) throws DbException;
@@ -220,27 +249,27 @@ public interface DatabaseComponent {
/** /**
* Returns the status of the given message with respect to the given * Returns the status of the given message with respect to the given
* contact. * contact.
* <p/>
* Read-only.
*/ */
MessageStatus getMessageStatus(Transaction txn, ContactId c, MessageId m) MessageStatus getMessageStatus(Transaction txn, ContactId c, MessageId m)
throws DbException; throws DbException;
/** /**
* Returns all settings in the given namespace. * Returns all settings in the given namespace.
* <p/>
* Read-only.
*/ */
Settings getSettings(Transaction txn, String namespace) throws DbException; Settings getSettings(Transaction txn, String namespace) throws DbException;
/** /**
* Returns all transport keys for the given transport. * Returns all transport keys for the given transport.
* <p/>
* Read-only.
*/ */
Map<ContactId, TransportKeys> getTransportKeys(Transaction txn, Map<ContactId, TransportKeys> getTransportKeys(Transaction txn,
TransportId t) throws DbException; TransportId t) throws DbException;
/**
* Returns the maximum latencies in milliseconds of all transports.
*/
Map<TransportId, Integer> getTransportLatencies(Transaction txn)
throws DbException;
/** /**
* Increments the outgoing stream counter for the given contact and * Increments the outgoing stream counter for the given contact and
* transport in the given rotation period . * transport in the given rotation period .
@@ -250,6 +279,8 @@ public interface DatabaseComponent {
/** /**
* Returns true if the given group is visible to the given contact. * Returns true if the given group is visible to the given contact.
* <p/>
* Read-only.
*/ */
boolean isVisibleToContact(Transaction txn, ContactId c, GroupId g) boolean isVisibleToContact(Transaction txn, ContactId c, GroupId g)
throws DbException; throws DbException;

View File

@@ -12,12 +12,14 @@ import java.util.List;
public class Transaction { public class Transaction {
private final Object txn; private final Object txn;
private final boolean readOnly;
private List<Event> events = null; private List<Event> events = null;
private boolean complete = false; private boolean complete = false;
public Transaction(Object txn) { public Transaction(Object txn, boolean readOnly) {
this.txn = txn; this.txn = txn;
this.readOnly = readOnly;
} }
/** /**
@@ -28,6 +30,13 @@ public class Transaction {
return txn; return txn;
} }
/**
* Returns true if the transaction can only be used for reading.
*/
public boolean isReadOnly() {
return readOnly;
}
/** /**
* Attaches an event to be broadcast when the transaction has been * Attaches an event to be broadcast when the transaction has been
* committed. * committed.

View File

@@ -1,23 +0,0 @@
package org.briarproject.api.event;
import org.briarproject.api.TransportId;
/** An event that is broadcast when a transport is added. */
public class TransportAddedEvent extends Event {
private final TransportId transportId;
private final int maxLatency;
public TransportAddedEvent(TransportId transportId, int maxLatency) {
this.transportId = transportId;
this.maxLatency = maxLatency;
}
public TransportId getTransportId() {
return transportId;
}
public int getMaxLatency() {
return maxLatency;
}
}

View File

@@ -1,17 +0,0 @@
package org.briarproject.api.event;
import org.briarproject.api.TransportId;
/** An event that is broadcast when a transport is removed. */
public class TransportRemovedEvent extends Event {
private final TransportId transportId;
public TransportRemovedEvent(TransportId transportId) {
this.transportId = transportId;
}
public TransportId getTransportId() {
return transportId;
}
}

View File

@@ -0,0 +1,13 @@
package org.briarproject.api.plugins;
import org.briarproject.api.plugins.duplex.DuplexPluginFactory;
import org.briarproject.api.plugins.simplex.SimplexPluginFactory;
import java.util.Collection;
public interface PluginConfig {
Collection<DuplexPluginFactory> getDuplexFactories();
Collection<SimplexPluginFactory> getSimplexFactories();
}

View File

@@ -9,10 +9,9 @@ import java.io.InputStream;
*/ */
public interface TransportConnectionReader { public interface TransportConnectionReader {
/** Returns the maximum latency of the transport in milliseconds. */ /**
long getMaxLatency(); * Returns an input stream for reading from the transport connection.
*/
/** Returns an input stream for reading from the transport connection. */
InputStream getInputStream() throws IOException; InputStream getInputStream() throws IOException;
/** /**
@@ -20,10 +19,13 @@ public interface TransportConnectionReader {
* simplex, the connection is closed. If the transport is duplex, the * simplex, the connection is closed. If the transport is duplex, the
* connection is closed if <tt>exception</tt> is true or the other side of * connection is closed if <tt>exception</tt> is true or the other side of
* the connection has been marked as closed. * the connection has been marked as closed.
* @param exception true if the connection is being closed because of an *
* exception. This may affect how resources are disposed of. * @param exception true if the connection is being closed because of an
* exception. This may affect how resources are disposed
* of.
* @param recognised true if the connection is definitely a Briar transport * @param recognised true if the connection is definitely a Briar transport
* connection. This may affect how resources are disposed of. * connection. This may affect how resources are disposed
* of.
*/ */
void dispose(boolean exception, boolean recognised) throws IOException; void dispose(boolean exception, boolean recognised) throws IOException;
} }

View File

@@ -1,8 +0,0 @@
package org.briarproject.api.plugins.duplex;
import java.util.Collection;
public interface DuplexPluginConfig {
Collection<DuplexPluginFactory> getFactories();
}

View File

@@ -2,12 +2,23 @@ package org.briarproject.api.plugins.duplex;
import org.briarproject.api.TransportId; import org.briarproject.api.TransportId;
/** Factory for creating a plugin for a duplex transport. */ /**
* Factory for creating a plugin for a duplex transport.
*/
public interface DuplexPluginFactory { public interface DuplexPluginFactory {
/** Returns the plugin's transport identifier. */ /**
* Returns the plugin's transport identifier.
*/
TransportId getId(); TransportId getId();
/** Creates and returns a plugin, or null if no plugin can be created. */ /**
* Returns the maximum latency of the transport in milliseconds.
*/
int getMaxLatency();
/**
* Creates and returns a plugin, or null if no plugin can be created.
*/
DuplexPlugin createPlugin(DuplexPluginCallback callback); DuplexPlugin createPlugin(DuplexPluginCallback callback);
} }

View File

@@ -1,8 +0,0 @@
package org.briarproject.api.plugins.simplex;
import java.util.Collection;
public interface SimplexPluginConfig {
Collection<SimplexPluginFactory> getFactories();
}

View File

@@ -2,12 +2,23 @@ package org.briarproject.api.plugins.simplex;
import org.briarproject.api.TransportId; import org.briarproject.api.TransportId;
/** Factory for creating a plugin for a simplex transport. */ /**
* Factory for creating a plugin for a simplex transport.
*/
public interface SimplexPluginFactory { public interface SimplexPluginFactory {
/** Returns the plugin's transport identifier. */ /**
* Returns the plugin's transport identifier.
*/
TransportId getId(); TransportId getId();
/** Creates and returns a plugin, or null if no plugin can be created. */ /**
* Returns the maximum latency of the transport in milliseconds.
*/
int getMaxLatency();
/**
* Creates and returns a plugin, or null if no plugin can be created.
*/
SimplexPlugin createPlugin(SimplexPluginCallback callback); SimplexPlugin createPlugin(SimplexPluginCallback callback);
} }

View File

@@ -1,6 +1,5 @@
package org.briarproject.api.sync; package org.briarproject.api.sync;
import org.briarproject.api.TransportId;
import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactId;
import java.io.InputStream; import java.io.InputStream;
@@ -8,12 +7,11 @@ import java.io.OutputStream;
public interface SyncSessionFactory { public interface SyncSessionFactory {
SyncSession createIncomingSession(ContactId c, TransportId t, SyncSession createIncomingSession(ContactId c, InputStream in);
InputStream in);
SyncSession createSimplexOutgoingSession(ContactId c, TransportId t, SyncSession createSimplexOutgoingSession(ContactId c, int maxLatency,
int maxLatency, OutputStream out); OutputStream out);
SyncSession createDuplexOutgoingSession(ContactId c, TransportId t, SyncSession createDuplexOutgoingSession(ContactId c, int maxLatency,
int maxLatency, int maxIdleTime, OutputStream out); int maxIdleTime, OutputStream out);
} }

View File

@@ -26,12 +26,14 @@ public interface KeyManager {
* contact over the given transport, or null if an error occurs or the * contact over the given transport, or null if an error occurs or the
* contact does not support the transport. * contact does not support the transport.
*/ */
StreamContext getStreamContext(ContactId c, TransportId t); StreamContext getStreamContext(ContactId c, TransportId t)
throws DbException;
/** /**
* Looks up the given tag and returns a {@link StreamContext} for reading * Looks up the given tag and returns a {@link StreamContext} for reading
* from the corresponding stream, or null if an error occurs or the tag was * from the corresponding stream, or null if an error occurs or the tag was
* unexpected. * unexpected.
*/ */
StreamContext getStreamContext(TransportId t, byte[] tag); StreamContext getStreamContext(TransportId t, byte[] tag)
throws DbException;
} }

View File

@@ -57,7 +57,7 @@ class ClientHelperImpl implements ClientHelper {
@Override @Override
public void addLocalMessage(Message m, ClientId c, BdfDictionary metadata, public void addLocalMessage(Message m, ClientId c, BdfDictionary metadata,
boolean shared) throws DbException, FormatException { boolean shared) throws DbException, FormatException {
Transaction txn = db.startTransaction(); Transaction txn = db.startTransaction(false);
try { try {
addLocalMessage(txn, m, c, metadata, shared); addLocalMessage(txn, m, c, metadata, shared);
txn.setComplete(); txn.setComplete();
@@ -89,7 +89,7 @@ class ClientHelperImpl implements ClientHelper {
public BdfDictionary getMessageAsDictionary(MessageId m) throws DbException, public BdfDictionary getMessageAsDictionary(MessageId m) throws DbException,
FormatException { FormatException {
BdfDictionary dictionary; BdfDictionary dictionary;
Transaction txn = db.startTransaction(); Transaction txn = db.startTransaction(true);
try { try {
dictionary = getMessageAsDictionary(txn, m); dictionary = getMessageAsDictionary(txn, m);
txn.setComplete(); txn.setComplete();
@@ -112,7 +112,7 @@ class ClientHelperImpl implements ClientHelper {
public BdfList getMessageAsList(MessageId m) throws DbException, public BdfList getMessageAsList(MessageId m) throws DbException,
FormatException { FormatException {
BdfList list; BdfList list;
Transaction txn = db.startTransaction(); Transaction txn = db.startTransaction(true);
try { try {
list = getMessageAsList(txn, m); list = getMessageAsList(txn, m);
txn.setComplete(); txn.setComplete();
@@ -135,7 +135,7 @@ class ClientHelperImpl implements ClientHelper {
public BdfDictionary getGroupMetadataAsDictionary(GroupId g) public BdfDictionary getGroupMetadataAsDictionary(GroupId g)
throws DbException, FormatException { throws DbException, FormatException {
BdfDictionary dictionary; BdfDictionary dictionary;
Transaction txn = db.startTransaction(); Transaction txn = db.startTransaction(true);
try { try {
dictionary = getGroupMetadataAsDictionary(txn, g); dictionary = getGroupMetadataAsDictionary(txn, g);
txn.setComplete(); txn.setComplete();
@@ -156,7 +156,7 @@ class ClientHelperImpl implements ClientHelper {
public BdfDictionary getMessageMetadataAsDictionary(MessageId m) public BdfDictionary getMessageMetadataAsDictionary(MessageId m)
throws DbException, FormatException { throws DbException, FormatException {
BdfDictionary dictionary; BdfDictionary dictionary;
Transaction txn = db.startTransaction(); Transaction txn = db.startTransaction(true);
try { try {
dictionary = getMessageMetadataAsDictionary(txn, m); dictionary = getMessageMetadataAsDictionary(txn, m);
txn.setComplete(); txn.setComplete();
@@ -177,7 +177,7 @@ class ClientHelperImpl implements ClientHelper {
public Map<MessageId, BdfDictionary> getMessageMetatataAsDictionary( public Map<MessageId, BdfDictionary> getMessageMetatataAsDictionary(
GroupId g) throws DbException, FormatException { GroupId g) throws DbException, FormatException {
Map<MessageId, BdfDictionary> map; Map<MessageId, BdfDictionary> map;
Transaction txn = db.startTransaction(); Transaction txn = db.startTransaction(true);
try { try {
map = getMessageMetadataAsDictionary(txn, g); map = getMessageMetadataAsDictionary(txn, g);
txn.setComplete(); txn.setComplete();
@@ -201,7 +201,7 @@ class ClientHelperImpl implements ClientHelper {
@Override @Override
public void mergeGroupMetadata(GroupId g, BdfDictionary metadata) public void mergeGroupMetadata(GroupId g, BdfDictionary metadata)
throws DbException, FormatException { throws DbException, FormatException {
Transaction txn = db.startTransaction(); Transaction txn = db.startTransaction(false);
try { try {
mergeGroupMetadata(txn, g, metadata); mergeGroupMetadata(txn, g, metadata);
txn.setComplete(); txn.setComplete();
@@ -219,7 +219,7 @@ class ClientHelperImpl implements ClientHelper {
@Override @Override
public void mergeMessageMetadata(MessageId m, BdfDictionary metadata) public void mergeMessageMetadata(MessageId m, BdfDictionary metadata)
throws DbException, FormatException { throws DbException, FormatException {
Transaction txn = db.startTransaction(); Transaction txn = db.startTransaction(false);
try { try {
mergeMessageMetadata(txn, m, metadata); mergeMessageMetadata(txn, m, metadata);
txn.setComplete(); txn.setComplete();

View File

@@ -51,7 +51,7 @@ class ContactManagerImpl implements ContactManager, RemoveIdentityHook {
long timestamp, boolean alice, boolean active) long timestamp, boolean alice, boolean active)
throws DbException { throws DbException {
ContactId c; ContactId c;
Transaction txn = db.startTransaction(); Transaction txn = db.startTransaction(false);
try { try {
c = db.addContact(txn, remote, local, active); c = db.addContact(txn, remote, local, active);
keyManager.addContact(txn, c, master, timestamp, alice); keyManager.addContact(txn, c, master, timestamp, alice);
@@ -68,7 +68,7 @@ class ContactManagerImpl implements ContactManager, RemoveIdentityHook {
@Override @Override
public Contact getContact(ContactId c) throws DbException { public Contact getContact(ContactId c) throws DbException {
Contact contact; Contact contact;
Transaction txn = db.startTransaction(); Transaction txn = db.startTransaction(true);
try { try {
contact = db.getContact(txn, c); contact = db.getContact(txn, c);
txn.setComplete(); txn.setComplete();
@@ -81,7 +81,7 @@ class ContactManagerImpl implements ContactManager, RemoveIdentityHook {
@Override @Override
public Collection<Contact> getActiveContacts() throws DbException { public Collection<Contact> getActiveContacts() throws DbException {
Collection<Contact> contacts; Collection<Contact> contacts;
Transaction txn = db.startTransaction(); Transaction txn = db.startTransaction(true);
try { try {
contacts = db.getContacts(txn); contacts = db.getContacts(txn);
txn.setComplete(); txn.setComplete();
@@ -95,7 +95,7 @@ class ContactManagerImpl implements ContactManager, RemoveIdentityHook {
@Override @Override
public void removeContact(ContactId c) throws DbException { public void removeContact(ContactId c) throws DbException {
Transaction txn = db.startTransaction(); Transaction txn = db.startTransaction(false);
try { try {
removeContact(txn, c); removeContact(txn, c);
txn.setComplete(); txn.setComplete();
@@ -107,7 +107,7 @@ class ContactManagerImpl implements ContactManager, RemoveIdentityHook {
@Override @Override
public void setContactActive(ContactId c, boolean active) public void setContactActive(ContactId c, boolean active)
throws DbException { throws DbException {
Transaction txn = db.startTransaction(); Transaction txn = db.startTransaction(false);
try { try {
db.setContactActive(txn, c, active); db.setContactActive(txn, c, active);
txn.setComplete(); txn.setComplete();

View File

@@ -118,38 +118,52 @@ interface Database<T> {
/** /**
* Returns true if the database contains the given contact for the given * Returns true if the database contains the given contact for the given
* local pseudonym. * local pseudonym.
* <p/>
* Read-only.
*/ */
boolean containsContact(T txn, AuthorId remote, AuthorId local) boolean containsContact(T txn, AuthorId remote, AuthorId local)
throws DbException; throws DbException;
/** /**
* Returns true if the database contains the given contact. * Returns true if the database contains the given contact.
* <p/>
* Read-only.
*/ */
boolean containsContact(T txn, ContactId c) throws DbException; boolean containsContact(T txn, ContactId c) throws DbException;
/** /**
* Returns true if the database contains the given group. * Returns true if the database contains the given group.
* <p/>
* Read-only.
*/ */
boolean containsGroup(T txn, GroupId g) throws DbException; boolean containsGroup(T txn, GroupId g) throws DbException;
/** /**
* Returns true if the database contains the given local pseudonym. * Returns true if the database contains the given local pseudonym.
* <p/>
* Read-only.
*/ */
boolean containsLocalAuthor(T txn, AuthorId a) throws DbException; boolean containsLocalAuthor(T txn, AuthorId a) throws DbException;
/** /**
* Returns true if the database contains the given message. * Returns true if the database contains the given message.
* <p/>
* Read-only.
*/ */
boolean containsMessage(T txn, MessageId m) throws DbException; boolean containsMessage(T txn, MessageId m) throws DbException;
/** /**
* Returns true if the database contains the given transport. * Returns true if the database contains the given transport.
* <p/>
* Read-only.
*/ */
boolean containsTransport(T txn, TransportId t) throws DbException; boolean containsTransport(T txn, TransportId t) throws DbException;
/** /**
* Returns true if the database contains the given group and the group is * Returns true if the database contains the given group and the group is
* visible to the given contact. * visible to the given contact.
* <p/>
* Read-only.
*/ */
boolean containsVisibleGroup(T txn, ContactId c, GroupId g) boolean containsVisibleGroup(T txn, ContactId c, GroupId g)
throws DbException; throws DbException;
@@ -157,12 +171,16 @@ interface Database<T> {
/** /**
* Returns true if the database contains the given message and the message * Returns true if the database contains the given message and the message
* is visible to the given contact. * is visible to the given contact.
* <p/>
* Read-only.
*/ */
boolean containsVisibleMessage(T txn, ContactId c, MessageId m) boolean containsVisibleMessage(T txn, ContactId c, MessageId m)
throws DbException; throws DbException;
/** /**
* Returns the number of messages offered by the given contact. * Returns the number of messages offered by the given contact.
* <p/>
* Read-only.
*/ */
int countOfferedMessages(T txn, ContactId c) throws DbException; int countOfferedMessages(T txn, ContactId c) throws DbException;
@@ -171,35 +189,39 @@ interface Database<T> {
* {@link #removeMessage(Object, MessageId)}, the message ID and any other * {@link #removeMessage(Object, MessageId)}, the message ID and any other
* associated data are not deleted, and * associated data are not deleted, and
* {@link #containsMessage(Object, MessageId)} will continue to return true. * {@link #containsMessage(Object, MessageId)} will continue to return true.
* <p>
* Locking: write.
*/ */
void deleteMessage(T txn, MessageId m) throws DbException; void deleteMessage(T txn, MessageId m) throws DbException;
/** /**
* Deletes any metadata associated with the given message. * Deletes any metadata associated with the given message.
* <p>
* Locking: write.
*/ */
void deleteMessageMetadata(T txn, MessageId m) throws DbException; void deleteMessageMetadata(T txn, MessageId m) throws DbException;
/** /**
* Returns the contact with the given ID. * Returns the contact with the given ID.
* <p/>
* Read-only.
*/ */
Contact getContact(T txn, ContactId c) throws DbException; Contact getContact(T txn, ContactId c) throws DbException;
/** /**
* Returns all contacts. * Returns all contacts.
* <p/>
* Read-only.
*/ */
Collection<Contact> getContacts(T txn) throws DbException; Collection<Contact> getContacts(T txn) throws DbException;
/** /**
* Returns all contacts associated with the given local pseudonym. * Returns all contacts associated with the given local pseudonym.
* <p/>
* Read-only.
*/ */
Collection<ContactId> getContacts(T txn, AuthorId a) throws DbException; Collection<ContactId> getContacts(T txn, AuthorId a) throws DbException;
/** /**
* Returns the unique ID for this device. * Returns the unique ID for this device.
* <p/>
* Read-only.
*/ */
DeviceId getDeviceId(T txn) throws DbException; DeviceId getDeviceId(T txn) throws DbException;
@@ -212,48 +234,66 @@ interface Database<T> {
/** /**
* Returns the group with the given ID. * Returns the group with the given ID.
* <p/>
* Read-only.
*/ */
Group getGroup(T txn, GroupId g) throws DbException; Group getGroup(T txn, GroupId g) throws DbException;
/** /**
* Returns the metadata for the given group. * Returns the metadata for the given group.
* <p/>
* Read-only.
*/ */
Metadata getGroupMetadata(T txn, GroupId g) throws DbException; Metadata getGroupMetadata(T txn, GroupId g) throws DbException;
/** /**
* Returns all groups belonging to the given client. * Returns all groups belonging to the given client.
* <p/>
* Read-only.
*/ */
Collection<Group> getGroups(T txn, ClientId c) throws DbException; Collection<Group> getGroups(T txn, ClientId c) throws DbException;
/** /**
* Returns the local pseudonym with the given ID. * Returns the local pseudonym with the given ID.
* <p/>
* Read-only.
*/ */
LocalAuthor getLocalAuthor(T txn, AuthorId a) throws DbException; LocalAuthor getLocalAuthor(T txn, AuthorId a) throws DbException;
/** /**
* Returns all local pseudonyms. * Returns all local pseudonyms.
* <p/>
* Read-only.
*/ */
Collection<LocalAuthor> getLocalAuthors(T txn) throws DbException; Collection<LocalAuthor> getLocalAuthors(T txn) throws DbException;
/** /**
* Returns the IDs of all messages in the given group. * Returns the IDs of all messages in the given group.
* <p/>
* Read-only.
*/ */
Collection<MessageId> getMessageIds(T txn, GroupId g) throws DbException; Collection<MessageId> getMessageIds(T txn, GroupId g) throws DbException;
/** /**
* Returns the metadata for all messages in the given group. * Returns the metadata for all messages in the given group.
* <p/>
* Read-only.
*/ */
Map<MessageId, Metadata> getMessageMetadata(T txn, GroupId g) Map<MessageId, Metadata> getMessageMetadata(T txn, GroupId g)
throws DbException; throws DbException;
/** /**
* Returns the metadata for the given message. * Returns the metadata for the given message.
* <p/>
* Read-only.
*/ */
Metadata getMessageMetadata(T txn, MessageId m) throws DbException; Metadata getMessageMetadata(T txn, MessageId m) throws DbException;
/** /**
* Returns the status of all messages in the given group with respect * Returns the status of all messages in the given group with respect
* to the given contact. * to the given contact.
* <p/>
* Read-only.
*/ */
Collection<MessageStatus> getMessageStatus(T txn, ContactId c, GroupId g) Collection<MessageStatus> getMessageStatus(T txn, ContactId c, GroupId g)
throws DbException; throws DbException;
@@ -261,6 +301,8 @@ interface Database<T> {
/** /**
* Returns the status of the given message with respect to the given * Returns the status of the given message with respect to the given
* contact. * contact.
* <p/>
* Read-only.
*/ */
MessageStatus getMessageStatus(T txn, ContactId c, MessageId m) MessageStatus getMessageStatus(T txn, ContactId c, MessageId m)
throws DbException; throws DbException;
@@ -268,6 +310,8 @@ interface Database<T> {
/** /**
* Returns the IDs of some messages received from the given contact that * Returns the IDs of some messages received from the given contact that
* need to be acknowledged, up to the given number of messages. * need to be acknowledged, up to the given number of messages.
* <p/>
* Read-only.
*/ */
Collection<MessageId> getMessagesToAck(T txn, ContactId c, int maxMessages) Collection<MessageId> getMessagesToAck(T txn, ContactId c, int maxMessages)
throws DbException; throws DbException;
@@ -275,6 +319,8 @@ interface Database<T> {
/** /**
* Returns the IDs of some messages that are eligible to be offered to the * Returns the IDs of some messages that are eligible to be offered to the
* given contact, up to the given number of messages. * given contact, up to the given number of messages.
* <p/>
* Read-only.
*/ */
Collection<MessageId> getMessagesToOffer(T txn, ContactId c, Collection<MessageId> getMessagesToOffer(T txn, ContactId c,
int maxMessages) throws DbException; int maxMessages) throws DbException;
@@ -282,6 +328,8 @@ interface Database<T> {
/** /**
* Returns the IDs of some messages that are eligible to be sent to the * Returns the IDs of some messages that are eligible to be sent to the
* given contact, up to the given total length. * given contact, up to the given total length.
* <p/>
* Read-only.
*/ */
Collection<MessageId> getMessagesToSend(T txn, ContactId c, int maxLength) Collection<MessageId> getMessagesToSend(T txn, ContactId c, int maxLength)
throws DbException; throws DbException;
@@ -289,6 +337,8 @@ interface Database<T> {
/** /**
* Returns the IDs of some messages that are eligible to be requested from * Returns the IDs of some messages that are eligible to be requested from
* the given contact, up to the given number of messages. * the given contact, up to the given number of messages.
* <p/>
* Read-only.
*/ */
Collection<MessageId> getMessagesToRequest(T txn, ContactId c, Collection<MessageId> getMessagesToRequest(T txn, ContactId c,
int maxMessages) throws DbException; int maxMessages) throws DbException;
@@ -296,12 +346,17 @@ interface Database<T> {
/** /**
* Returns the IDs of any messages that need to be validated by the given * Returns the IDs of any messages that need to be validated by the given
* client. * client.
* <p/>
* Read-only.
*/ */
Collection<MessageId> getMessagesToValidate(T txn, ClientId c) Collection<MessageId> getMessagesToValidate(T txn, ClientId c)
throws DbException; throws DbException;
/** /**
* Returns the message with the given ID, in serialised form. * Returns the message with the given ID, in serialised form, or null if
* the message has been deleted.
* <p/>
* Read-only.
*/ */
byte[] getRawMessage(T txn, MessageId m) throws DbException; byte[] getRawMessage(T txn, MessageId m) throws DbException;
@@ -309,28 +364,31 @@ interface Database<T> {
* Returns the IDs of some messages that are eligible to be sent to the * Returns the IDs of some messages that are eligible to be sent to the
* given contact and have been requested by the contact, up to the given * given contact and have been requested by the contact, up to the given
* total length. * total length.
* <p/>
* Read-only.
*/ */
Collection<MessageId> getRequestedMessagesToSend(T txn, ContactId c, Collection<MessageId> getRequestedMessagesToSend(T txn, ContactId c,
int maxLength) throws DbException; int maxLength) throws DbException;
/** /**
* Returns all settings in the given namespace. * Returns all settings in the given namespace.
* <p/>
* Read-only.
*/ */
Settings getSettings(T txn, String namespace) throws DbException; Settings getSettings(T txn, String namespace) throws DbException;
/** /**
* Returns all transport keys for the given transport. * Returns all transport keys for the given transport.
* <p/>
* Read-only.
*/ */
Map<ContactId, TransportKeys> getTransportKeys(T txn, TransportId t) Map<ContactId, TransportKeys> getTransportKeys(T txn, TransportId t)
throws DbException; throws DbException;
/**
* Returns the maximum latencies in milliseconds of all transports.
*/
Map<TransportId, Integer> getTransportLatencies(T txn) throws DbException;
/** /**
* Returns the IDs of all contacts to which the given group is visible. * Returns the IDs of all contacts to which the given group is visible.
* <p/>
* Read-only.
*/ */
Collection<ContactId> getVisibility(T txn, GroupId g) throws DbException; Collection<ContactId> getVisibility(T txn, GroupId g) throws DbException;

View File

@@ -33,8 +33,6 @@ import org.briarproject.api.event.MessageValidatedEvent;
import org.briarproject.api.event.MessagesAckedEvent; import org.briarproject.api.event.MessagesAckedEvent;
import org.briarproject.api.event.MessagesSentEvent; import org.briarproject.api.event.MessagesSentEvent;
import org.briarproject.api.event.SettingsUpdatedEvent; import org.briarproject.api.event.SettingsUpdatedEvent;
import org.briarproject.api.event.TransportAddedEvent;
import org.briarproject.api.event.TransportRemovedEvent;
import org.briarproject.api.identity.Author; import org.briarproject.api.identity.Author;
import org.briarproject.api.identity.AuthorId; import org.briarproject.api.identity.AuthorId;
import org.briarproject.api.identity.LocalAuthor; import org.briarproject.api.identity.LocalAuthor;
@@ -61,6 +59,8 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.inject.Inject; import javax.inject.Inject;
@@ -80,6 +80,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
private final EventBus eventBus; private final EventBus eventBus;
private final ShutdownManager shutdown; private final ShutdownManager shutdown;
private final AtomicBoolean closed = new AtomicBoolean(false); private final AtomicBoolean closed = new AtomicBoolean(false);
private final ReadWriteLock lock = new ReentrantReadWriteLock(true);
private volatile int shutdownHandle = -1; private volatile int shutdownHandle = -1;
@@ -117,18 +118,33 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
db.close(); db.close();
} }
public Transaction startTransaction() throws DbException { public Transaction startTransaction(boolean readOnly) throws DbException {
return new Transaction(db.startTransaction()); if (readOnly) lock.readLock().lock();
else lock.writeLock().lock();
try {
return new Transaction(db.startTransaction(), readOnly);
} catch (DbException e) {
if (readOnly) lock.readLock().unlock();
else lock.writeLock().unlock();
throw e;
} catch (RuntimeException e) {
if (readOnly) lock.readLock().unlock();
else lock.writeLock().unlock();
throw e;
}
} }
public void endTransaction(Transaction transaction) throws DbException { public void endTransaction(Transaction transaction) throws DbException {
T txn = txnClass.cast(transaction.unbox()); try {
if (transaction.isComplete()) { T txn = txnClass.cast(transaction.unbox());
db.commitTransaction(txn); if (transaction.isComplete()) db.commitTransaction(txn);
for (Event e : transaction.getEvents()) eventBus.broadcast(e); else db.abortTransaction(txn);
} else { } finally {
db.abortTransaction(txn); if (transaction.isReadOnly()) lock.readLock().unlock();
else lock.writeLock().unlock();
} }
if (transaction.isComplete())
for (Event e : transaction.getEvents()) eventBus.broadcast(e);
} }
private T unbox(Transaction transaction) { private T unbox(Transaction transaction) {
@@ -138,6 +154,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public ContactId addContact(Transaction transaction, Author remote, public ContactId addContact(Transaction transaction, Author remote,
AuthorId local, boolean active) throws DbException { AuthorId local, boolean active) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsLocalAuthor(txn, local)) if (!db.containsLocalAuthor(txn, local))
throw new NoSuchLocalAuthorException(); throw new NoSuchLocalAuthorException();
@@ -150,6 +167,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
} }
public void addGroup(Transaction transaction, Group g) throws DbException { public void addGroup(Transaction transaction, Group g) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsGroup(txn, g.getId())) { if (!db.containsGroup(txn, g.getId())) {
db.addGroup(txn, g); db.addGroup(txn, g);
@@ -159,6 +177,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public void addLocalAuthor(Transaction transaction, LocalAuthor a) public void addLocalAuthor(Transaction transaction, LocalAuthor a)
throws DbException { throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsLocalAuthor(txn, a.getId())) { if (!db.containsLocalAuthor(txn, a.getId())) {
db.addLocalAuthor(txn, a); db.addLocalAuthor(txn, a);
@@ -168,6 +187,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public void addLocalMessage(Transaction transaction, Message m, ClientId c, public void addLocalMessage(Transaction transaction, Message m, ClientId c,
Metadata meta, boolean shared) throws DbException { Metadata meta, boolean shared) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsGroup(txn, m.getGroupId())) if (!db.containsGroup(txn, m.getGroupId()))
throw new NoSuchGroupException(); throw new NoSuchGroupException();
@@ -191,15 +211,15 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public void addTransport(Transaction transaction, TransportId t, public void addTransport(Transaction transaction, TransportId t,
int maxLatency) throws DbException { int maxLatency) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsTransport(txn, t)) { if (!db.containsTransport(txn, t))
db.addTransport(txn, t, maxLatency); db.addTransport(txn, t, maxLatency);
transaction.attach(new TransportAddedEvent(t, maxLatency));
}
} }
public void addTransportKeys(Transaction transaction, ContactId c, public void addTransportKeys(Transaction transaction, ContactId c,
TransportKeys k) throws DbException { TransportKeys k) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsContact(txn, c)) if (!db.containsContact(txn, c))
throw new NoSuchContactException(); throw new NoSuchContactException();
@@ -210,6 +230,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public void deleteMessage(Transaction transaction, MessageId m) public void deleteMessage(Transaction transaction, MessageId m)
throws DbException { throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsMessage(txn, m)) if (!db.containsMessage(txn, m))
throw new NoSuchMessageException(); throw new NoSuchMessageException();
@@ -218,6 +239,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public void deleteMessageMetadata(Transaction transaction, MessageId m) public void deleteMessageMetadata(Transaction transaction, MessageId m)
throws DbException { throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsMessage(txn, m)) if (!db.containsMessage(txn, m))
throw new NoSuchMessageException(); throw new NoSuchMessageException();
@@ -226,6 +248,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public Ack generateAck(Transaction transaction, ContactId c, public Ack generateAck(Transaction transaction, ContactId c,
int maxMessages) throws DbException { int maxMessages) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsContact(txn, c)) if (!db.containsContact(txn, c))
throw new NoSuchContactException(); throw new NoSuchContactException();
@@ -237,6 +260,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public Collection<byte[]> generateBatch(Transaction transaction, public Collection<byte[]> generateBatch(Transaction transaction,
ContactId c, int maxLength, int maxLatency) throws DbException { ContactId c, int maxLength, int maxLatency) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsContact(txn, c)) if (!db.containsContact(txn, c))
throw new NoSuchContactException(); throw new NoSuchContactException();
@@ -254,6 +278,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public Offer generateOffer(Transaction transaction, ContactId c, public Offer generateOffer(Transaction transaction, ContactId c,
int maxMessages, int maxLatency) throws DbException { int maxMessages, int maxLatency) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsContact(txn, c)) if (!db.containsContact(txn, c))
throw new NoSuchContactException(); throw new NoSuchContactException();
@@ -265,6 +290,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public Request generateRequest(Transaction transaction, ContactId c, public Request generateRequest(Transaction transaction, ContactId c,
int maxMessages) throws DbException { int maxMessages) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsContact(txn, c)) if (!db.containsContact(txn, c))
throw new NoSuchContactException(); throw new NoSuchContactException();
@@ -277,6 +303,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public Collection<byte[]> generateRequestedBatch(Transaction transaction, public Collection<byte[]> generateRequestedBatch(Transaction transaction,
ContactId c, int maxLength, int maxLatency) throws DbException { ContactId c, int maxLength, int maxLatency) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsContact(txn, c)) if (!db.containsContact(txn, c))
throw new NoSuchContactException(); throw new NoSuchContactException();
@@ -420,14 +447,9 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
return db.getTransportKeys(txn, t); return db.getTransportKeys(txn, t);
} }
public Map<TransportId, Integer> getTransportLatencies(
Transaction transaction) throws DbException {
T txn = unbox(transaction);
return db.getTransportLatencies(txn);
}
public void incrementStreamCounter(Transaction transaction, ContactId c, public void incrementStreamCounter(Transaction transaction, ContactId c,
TransportId t, long rotationPeriod) throws DbException { TransportId t, long rotationPeriod) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsContact(txn, c)) if (!db.containsContact(txn, c))
throw new NoSuchContactException(); throw new NoSuchContactException();
@@ -448,6 +470,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public void mergeGroupMetadata(Transaction transaction, GroupId g, public void mergeGroupMetadata(Transaction transaction, GroupId g,
Metadata meta) throws DbException { Metadata meta) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsGroup(txn, g)) if (!db.containsGroup(txn, g))
throw new NoSuchGroupException(); throw new NoSuchGroupException();
@@ -456,6 +479,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public void mergeMessageMetadata(Transaction transaction, MessageId m, public void mergeMessageMetadata(Transaction transaction, MessageId m,
Metadata meta) throws DbException { Metadata meta) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsMessage(txn, m)) if (!db.containsMessage(txn, m))
throw new NoSuchMessageException(); throw new NoSuchMessageException();
@@ -464,6 +488,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public void mergeSettings(Transaction transaction, Settings s, public void mergeSettings(Transaction transaction, Settings s,
String namespace) throws DbException { String namespace) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
Settings old = db.getSettings(txn, namespace); Settings old = db.getSettings(txn, namespace);
Settings merged = new Settings(); Settings merged = new Settings();
@@ -477,6 +502,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public void receiveAck(Transaction transaction, ContactId c, Ack a) public void receiveAck(Transaction transaction, ContactId c, Ack a)
throws DbException { throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsContact(txn, c)) if (!db.containsContact(txn, c))
throw new NoSuchContactException(); throw new NoSuchContactException();
@@ -492,6 +518,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public void receiveMessage(Transaction transaction, ContactId c, Message m) public void receiveMessage(Transaction transaction, ContactId c, Message m)
throws DbException { throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsContact(txn, c)) if (!db.containsContact(txn, c))
throw new NoSuchContactException(); throw new NoSuchContactException();
@@ -507,6 +534,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public void receiveOffer(Transaction transaction, ContactId c, Offer o) public void receiveOffer(Transaction transaction, ContactId c, Offer o)
throws DbException { throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsContact(txn, c)) if (!db.containsContact(txn, c))
throw new NoSuchContactException(); throw new NoSuchContactException();
@@ -529,6 +557,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public void receiveRequest(Transaction transaction, ContactId c, Request r) public void receiveRequest(Transaction transaction, ContactId c, Request r)
throws DbException { throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsContact(txn, c)) if (!db.containsContact(txn, c))
throw new NoSuchContactException(); throw new NoSuchContactException();
@@ -545,6 +574,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public void removeContact(Transaction transaction, ContactId c) public void removeContact(Transaction transaction, ContactId c)
throws DbException { throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsContact(txn, c)) if (!db.containsContact(txn, c))
throw new NoSuchContactException(); throw new NoSuchContactException();
@@ -554,6 +584,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public void removeGroup(Transaction transaction, Group g) public void removeGroup(Transaction transaction, Group g)
throws DbException { throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
GroupId id = g.getId(); GroupId id = g.getId();
if (!db.containsGroup(txn, id)) if (!db.containsGroup(txn, id))
@@ -566,6 +597,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public void removeLocalAuthor(Transaction transaction, AuthorId a) public void removeLocalAuthor(Transaction transaction, AuthorId a)
throws DbException { throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsLocalAuthor(txn, a)) if (!db.containsLocalAuthor(txn, a))
throw new NoSuchLocalAuthorException(); throw new NoSuchLocalAuthorException();
@@ -575,15 +607,16 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public void removeTransport(Transaction transaction, TransportId t) public void removeTransport(Transaction transaction, TransportId t)
throws DbException { throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsTransport(txn, t)) if (!db.containsTransport(txn, t))
throw new NoSuchTransportException(); throw new NoSuchTransportException();
db.removeTransport(txn, t); db.removeTransport(txn, t);
transaction.attach(new TransportRemovedEvent(t));
} }
public void setContactActive(Transaction transaction, ContactId c, public void setContactActive(Transaction transaction, ContactId c,
boolean active) throws DbException { boolean active) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsContact(txn, c)) if (!db.containsContact(txn, c))
throw new NoSuchContactException(); throw new NoSuchContactException();
@@ -593,6 +626,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public void setMessageShared(Transaction transaction, Message m, public void setMessageShared(Transaction transaction, Message m,
boolean shared) throws DbException { boolean shared) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsMessage(txn, m.getId())) if (!db.containsMessage(txn, m.getId()))
throw new NoSuchMessageException(); throw new NoSuchMessageException();
@@ -602,6 +636,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public void setMessageValid(Transaction transaction, Message m, ClientId c, public void setMessageValid(Transaction transaction, Message m, ClientId c,
boolean valid) throws DbException { boolean valid) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsMessage(txn, m.getId())) if (!db.containsMessage(txn, m.getId()))
throw new NoSuchMessageException(); throw new NoSuchMessageException();
@@ -612,6 +647,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public void setReorderingWindow(Transaction transaction, ContactId c, public void setReorderingWindow(Transaction transaction, ContactId c,
TransportId t, long rotationPeriod, long base, byte[] bitmap) TransportId t, long rotationPeriod, long base, byte[] bitmap)
throws DbException { throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsContact(txn, c)) if (!db.containsContact(txn, c))
throw new NoSuchContactException(); throw new NoSuchContactException();
@@ -622,6 +658,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public void setVisibleToContact(Transaction transaction, ContactId c, public void setVisibleToContact(Transaction transaction, ContactId c,
GroupId g, boolean visible) throws DbException { GroupId g, boolean visible) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsContact(txn, c)) if (!db.containsContact(txn, c))
throw new NoSuchContactException(); throw new NoSuchContactException();
@@ -647,6 +684,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public void updateTransportKeys(Transaction transaction, public void updateTransportKeys(Transaction transaction,
Map<ContactId, TransportKeys> keys) throws DbException { Map<ContactId, TransportKeys> keys) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
Map<ContactId, TransportKeys> filtered = Map<ContactId, TransportKeys> filtered =
new HashMap<ContactId, TransportKeys>(); new HashMap<ContactId, TransportKeys>();

View File

@@ -1542,30 +1542,6 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
public Map<TransportId, Integer> getTransportLatencies(Connection txn)
throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT transportId, maxLatency FROM transports";
ps = txn.prepareStatement(sql);
rs = ps.executeQuery();
Map<TransportId, Integer> latencies =
new HashMap<TransportId, Integer>();
while (rs.next()) {
TransportId id = new TransportId(rs.getString(1));
latencies.put(id, rs.getInt(2));
}
rs.close();
ps.close();
return Collections.unmodifiableMap(latencies);
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
throw new DbException(e);
}
}
public Collection<ContactId> getVisibility(Connection txn, GroupId g) public Collection<ContactId> getVisibility(Connection txn, GroupId g)
throws DbException { throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;

View File

@@ -83,7 +83,7 @@ class ForumManagerImpl implements ForumManager {
public Forum getForum(GroupId g) throws DbException { public Forum getForum(GroupId g) throws DbException {
try { try {
Group group; Group group;
Transaction txn = db.startTransaction(); Transaction txn = db.startTransaction(true);
try { try {
group = db.getGroup(txn, g); group = db.getGroup(txn, g);
txn.setComplete(); txn.setComplete();
@@ -100,7 +100,7 @@ class ForumManagerImpl implements ForumManager {
public Collection<Forum> getForums() throws DbException { public Collection<Forum> getForums() throws DbException {
try { try {
Collection<Group> groups; Collection<Group> groups;
Transaction txn = db.startTransaction(); Transaction txn = db.startTransaction(true);
try { try {
groups = db.getGroups(txn, CLIENT_ID); groups = db.getGroups(txn, CLIENT_ID);
txn.setComplete(); txn.setComplete();
@@ -132,7 +132,7 @@ class ForumManagerImpl implements ForumManager {
Set<AuthorId> localAuthorIds = new HashSet<AuthorId>(); Set<AuthorId> localAuthorIds = new HashSet<AuthorId>();
Set<AuthorId> contactAuthorIds = new HashSet<AuthorId>(); Set<AuthorId> contactAuthorIds = new HashSet<AuthorId>();
Map<MessageId, BdfDictionary> metadata; Map<MessageId, BdfDictionary> metadata;
Transaction txn = db.startTransaction(); Transaction txn = db.startTransaction(true);
try { try {
// Load the IDs of the user's identities // Load the IDs of the user's identities
for (LocalAuthor a : db.getLocalAuthors(txn)) for (LocalAuthor a : db.getLocalAuthors(txn))

View File

@@ -131,7 +131,7 @@ class ForumSharingManagerImpl implements ForumSharingManager, AddContactHook,
@Override @Override
public void addForum(Forum f) throws DbException { public void addForum(Forum f) throws DbException {
Transaction txn = db.startTransaction(); Transaction txn = db.startTransaction(false);
try { try {
db.addGroup(txn, f.getGroup()); db.addGroup(txn, f.getGroup());
txn.setComplete(); txn.setComplete();
@@ -144,7 +144,7 @@ class ForumSharingManagerImpl implements ForumSharingManager, AddContactHook,
public void removeForum(Forum f) throws DbException { public void removeForum(Forum f) throws DbException {
try { try {
// Update the list shared with each contact // Update the list shared with each contact
Transaction txn = db.startTransaction(); Transaction txn = db.startTransaction(false);
try { try {
for (Contact c : db.getContacts(txn)) for (Contact c : db.getContacts(txn))
removeFromList(txn, getContactGroup(c).getId(), f); removeFromList(txn, getContactGroup(c).getId(), f);
@@ -162,7 +162,7 @@ class ForumSharingManagerImpl implements ForumSharingManager, AddContactHook,
public Collection<Forum> getAvailableForums() throws DbException { public Collection<Forum> getAvailableForums() throws DbException {
try { try {
Set<Forum> available = new HashSet<Forum>(); Set<Forum> available = new HashSet<Forum>();
Transaction txn = db.startTransaction(); Transaction txn = db.startTransaction(true);
try { try {
// Get any forums we subscribe to // Get any forums we subscribe to
Set<Group> subscribed = new HashSet<Group>(db.getGroups(txn, Set<Group> subscribed = new HashSet<Group>(db.getGroups(txn,
@@ -196,7 +196,7 @@ class ForumSharingManagerImpl implements ForumSharingManager, AddContactHook,
public Collection<Contact> getSharedBy(GroupId g) throws DbException { public Collection<Contact> getSharedBy(GroupId g) throws DbException {
try { try {
List<Contact> subscribers = new ArrayList<Contact>(); List<Contact> subscribers = new ArrayList<Contact>();
Transaction txn = db.startTransaction(); Transaction txn = db.startTransaction(true);
try { try {
for (Contact c : db.getContacts(txn)) { for (Contact c : db.getContacts(txn)) {
if (listContains(txn, getContactGroup(c).getId(), g, false)) if (listContains(txn, getContactGroup(c).getId(), g, false))
@@ -216,7 +216,7 @@ class ForumSharingManagerImpl implements ForumSharingManager, AddContactHook,
public Collection<ContactId> getSharedWith(GroupId g) throws DbException { public Collection<ContactId> getSharedWith(GroupId g) throws DbException {
try { try {
List<ContactId> shared = new ArrayList<ContactId>(); List<ContactId> shared = new ArrayList<ContactId>();
Transaction txn = db.startTransaction(); Transaction txn = db.startTransaction(true);
try { try {
for (Contact c : db.getContacts(txn)) { for (Contact c : db.getContacts(txn)) {
if (listContains(txn, getContactGroup(c).getId(), g, true)) if (listContains(txn, getContactGroup(c).getId(), g, true))
@@ -236,7 +236,7 @@ class ForumSharingManagerImpl implements ForumSharingManager, AddContactHook,
public void setSharedWith(GroupId g, Collection<ContactId> shared) public void setSharedWith(GroupId g, Collection<ContactId> shared)
throws DbException { throws DbException {
try { try {
Transaction txn = db.startTransaction(); Transaction txn = db.startTransaction(false);
try { try {
// Retrieve the forum // Retrieve the forum
Forum f = parseForum(db.getGroup(txn, g)); Forum f = parseForum(db.getGroup(txn, g));
@@ -268,7 +268,7 @@ class ForumSharingManagerImpl implements ForumSharingManager, AddContactHook,
@Override @Override
public void setSharedWithAll(GroupId g) throws DbException { public void setSharedWithAll(GroupId g) throws DbException {
try { try {
Transaction txn = db.startTransaction(); Transaction txn = db.startTransaction(false);
try { try {
// Retrieve the forum // Retrieve the forum
Forum f = parseForum(db.getGroup(txn, g)); Forum f = parseForum(db.getGroup(txn, g));

View File

@@ -37,7 +37,7 @@ class IdentityManagerImpl implements IdentityManager {
@Override @Override
public void addLocalAuthor(LocalAuthor localAuthor) throws DbException { public void addLocalAuthor(LocalAuthor localAuthor) throws DbException {
Transaction txn = db.startTransaction(); Transaction txn = db.startTransaction(false);
try { try {
db.addLocalAuthor(txn, localAuthor); db.addLocalAuthor(txn, localAuthor);
for (AddIdentityHook hook : addHooks) for (AddIdentityHook hook : addHooks)
@@ -51,7 +51,7 @@ class IdentityManagerImpl implements IdentityManager {
@Override @Override
public LocalAuthor getLocalAuthor(AuthorId a) throws DbException { public LocalAuthor getLocalAuthor(AuthorId a) throws DbException {
LocalAuthor author; LocalAuthor author;
Transaction txn = db.startTransaction(); Transaction txn = db.startTransaction(true);
try { try {
author = db.getLocalAuthor(txn, a); author = db.getLocalAuthor(txn, a);
txn.setComplete(); txn.setComplete();
@@ -64,7 +64,7 @@ class IdentityManagerImpl implements IdentityManager {
@Override @Override
public Collection<LocalAuthor> getLocalAuthors() throws DbException { public Collection<LocalAuthor> getLocalAuthors() throws DbException {
Collection<LocalAuthor> authors; Collection<LocalAuthor> authors;
Transaction txn = db.startTransaction(); Transaction txn = db.startTransaction(true);
try { try {
authors = db.getLocalAuthors(txn); authors = db.getLocalAuthors(txn);
txn.setComplete(); txn.setComplete();
@@ -76,7 +76,7 @@ class IdentityManagerImpl implements IdentityManager {
@Override @Override
public void removeLocalAuthor(AuthorId a) throws DbException { public void removeLocalAuthor(AuthorId a) throws DbException {
Transaction txn = db.startTransaction(); Transaction txn = db.startTransaction(false);
try { try {
LocalAuthor localAuthor = db.getLocalAuthor(txn, a); LocalAuthor localAuthor = db.getLocalAuthor(txn, a);
for (RemoveIdentityHook hook : removeHooks) for (RemoveIdentityHook hook : removeHooks)

View File

@@ -6,7 +6,6 @@ import org.briarproject.api.event.EventBus;
import org.briarproject.api.event.ShutdownEvent; import org.briarproject.api.event.ShutdownEvent;
import org.briarproject.api.lifecycle.LifecycleManager; import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.lifecycle.Service; import org.briarproject.api.lifecycle.Service;
import org.briarproject.api.system.Clock;
import java.io.IOException; import java.io.IOException;
import java.util.Collection; import java.util.Collection;
@@ -30,7 +29,6 @@ class LifecycleManagerImpl implements LifecycleManager {
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(LifecycleManagerImpl.class.getName()); Logger.getLogger(LifecycleManagerImpl.class.getName());
private final Clock clock;
private final DatabaseComponent db; private final DatabaseComponent db;
private final EventBus eventBus; private final EventBus eventBus;
private final Collection<Service> services; private final Collection<Service> services;
@@ -41,8 +39,7 @@ class LifecycleManagerImpl implements LifecycleManager {
private final CountDownLatch shutdownLatch = new CountDownLatch(1); private final CountDownLatch shutdownLatch = new CountDownLatch(1);
@Inject @Inject
LifecycleManagerImpl(Clock clock, DatabaseComponent db, EventBus eventBus) { LifecycleManagerImpl(DatabaseComponent db, EventBus eventBus) {
this.clock = clock;
this.db = db; this.db = db;
this.eventBus = eventBus; this.eventBus = eventBus;
services = new CopyOnWriteArrayList<Service>(); services = new CopyOnWriteArrayList<Service>();
@@ -68,9 +65,9 @@ class LifecycleManagerImpl implements LifecycleManager {
} }
try { try {
LOG.info("Starting services"); LOG.info("Starting services");
long now = clock.currentTimeMillis(); long start = System.currentTimeMillis();
boolean reopened = db.open(); boolean reopened = db.open();
long duration = clock.currentTimeMillis() - now; long duration = System.currentTimeMillis() - start;
if (LOG.isLoggable(INFO)) { if (LOG.isLoggable(INFO)) {
if (reopened) if (reopened)
LOG.info("Reopening database took " + duration + " ms"); LOG.info("Reopening database took " + duration + " ms");
@@ -78,9 +75,9 @@ class LifecycleManagerImpl implements LifecycleManager {
} }
dbLatch.countDown(); dbLatch.countDown();
for (Service s : services) { for (Service s : services) {
now = clock.currentTimeMillis(); start = System.currentTimeMillis();
boolean started = s.start(); boolean started = s.start();
duration = clock.currentTimeMillis() - now; duration = System.currentTimeMillis() - start;
if (!started) { if (!started) {
if (LOG.isLoggable(WARNING)) { if (LOG.isLoggable(WARNING)) {
String name = s.getClass().getName(); String name = s.getClass().getName();

View File

@@ -1,6 +1,10 @@
package org.briarproject.lifecycle; package org.briarproject.lifecycle;
import static java.util.concurrent.TimeUnit.SECONDS; import org.briarproject.api.db.DatabaseComponent;
import org.briarproject.api.event.EventBus;
import org.briarproject.api.lifecycle.IoExecutor;
import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.lifecycle.ShutdownManager;
import java.util.concurrent.BlockingQueue; import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
@@ -12,16 +16,11 @@ import java.util.concurrent.ThreadPoolExecutor;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import org.briarproject.api.db.DatabaseComponent;
import org.briarproject.api.event.EventBus;
import org.briarproject.api.lifecycle.IoExecutor;
import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.lifecycle.ShutdownManager;
import org.briarproject.api.system.Clock;
import dagger.Module; import dagger.Module;
import dagger.Provides; import dagger.Provides;
import static java.util.concurrent.TimeUnit.SECONDS;
@Module @Module
public class LifecycleModule { public class LifecycleModule {
@@ -51,9 +50,9 @@ public class LifecycleModule {
@Provides @Provides
@Singleton @Singleton
LifecycleManager provideLifeCycleManager(Clock clock, DatabaseComponent db, LifecycleManager provideLifecycleManager(DatabaseComponent db,
EventBus eventBus) { EventBus eventBus) {
return new LifecycleManagerImpl(clock, db, eventBus); return new LifecycleManagerImpl(db, eventBus);
} }
@Provides @Provides
@@ -63,5 +62,4 @@ public class LifecycleModule {
lifecycleManager.registerForShutdown(ioExecutor); lifecycleManager.registerForShutdown(ioExecutor);
return ioExecutor; return ioExecutor;
} }
} }

View File

@@ -106,7 +106,7 @@ class MessagingManagerImpl implements MessagingManager, AddContactHook,
@Override @Override
public GroupId getConversationId(ContactId c) throws DbException { public GroupId getConversationId(ContactId c) throws DbException {
Contact contact; Contact contact;
Transaction txn = db.startTransaction(); Transaction txn = db.startTransaction(true);
try { try {
contact = db.getContact(txn, c); contact = db.getContact(txn, c);
txn.setComplete(); txn.setComplete();
@@ -121,7 +121,7 @@ class MessagingManagerImpl implements MessagingManager, AddContactHook,
throws DbException { throws DbException {
Map<MessageId, BdfDictionary> metadata; Map<MessageId, BdfDictionary> metadata;
Collection<MessageStatus> statuses; Collection<MessageStatus> statuses;
Transaction txn = db.startTransaction(); Transaction txn = db.startTransaction(true);
try { try {
GroupId g = getContactGroup(db.getContact(txn, c)).getId(); GroupId g = getContactGroup(db.getContact(txn, c)).getId();
metadata = clientHelper.getMessageMetadataAsDictionary(txn, g); metadata = clientHelper.getMessageMetadataAsDictionary(txn, g);

View File

@@ -2,6 +2,7 @@ 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.lifecycle.IoExecutor; import org.briarproject.api.lifecycle.IoExecutor;
import org.briarproject.api.plugins.ConnectionManager; import org.briarproject.api.plugins.ConnectionManager;
import org.briarproject.api.plugins.ConnectionRegistry; import org.briarproject.api.plugins.ConnectionRegistry;
@@ -90,8 +91,8 @@ class ConnectionManagerImpl implements ConnectionManager {
TransportConnectionReader r) throws IOException { TransportConnectionReader r) throws IOException {
InputStream streamReader = streamReaderFactory.createStreamReader( InputStream streamReader = streamReaderFactory.createStreamReader(
r.getInputStream(), ctx); r.getInputStream(), ctx);
return syncSessionFactory.createIncomingSession( return syncSessionFactory.createIncomingSession(ctx.getContactId(),
ctx.getContactId(), ctx.getTransportId(), streamReader); streamReader);
} }
private SyncSession createSimplexOutgoingSession(StreamContext ctx, private SyncSession createSimplexOutgoingSession(StreamContext ctx,
@@ -99,8 +100,7 @@ class ConnectionManagerImpl implements ConnectionManager {
OutputStream streamWriter = streamWriterFactory.createStreamWriter( OutputStream streamWriter = streamWriterFactory.createStreamWriter(
w.getOutputStream(), ctx); w.getOutputStream(), ctx);
return syncSessionFactory.createSimplexOutgoingSession( return syncSessionFactory.createSimplexOutgoingSession(
ctx.getContactId(), ctx.getTransportId(), w.getMaxLatency(), ctx.getContactId(), w.getMaxLatency(), streamWriter);
streamWriter);
} }
private SyncSession createDuplexOutgoingSession(StreamContext ctx, private SyncSession createDuplexOutgoingSession(StreamContext ctx,
@@ -108,8 +108,8 @@ class ConnectionManagerImpl implements ConnectionManager {
OutputStream streamWriter = streamWriterFactory.createStreamWriter( OutputStream streamWriter = streamWriterFactory.createStreamWriter(
w.getOutputStream(), ctx); w.getOutputStream(), ctx);
return syncSessionFactory.createDuplexOutgoingSession( return syncSessionFactory.createDuplexOutgoingSession(
ctx.getContactId(), ctx.getTransportId(), w.getMaxLatency(), ctx.getContactId(), w.getMaxLatency(), w.getMaxIdleTime(),
w.getMaxIdleTime(), streamWriter); streamWriter);
} }
private class ManageIncomingSimplexConnection implements Runnable { private class ManageIncomingSimplexConnection implements Runnable {
@@ -133,10 +133,14 @@ class ConnectionManagerImpl implements ConnectionManager {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
disposeReader(true, false); disposeReader(true, false);
return; return;
} catch (DbException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
disposeReader(true, false);
return;
} }
if (ctx == null) { if (ctx == null) {
LOG.info("Unrecognised tag"); LOG.info("Unrecognised tag");
disposeReader(true, false); disposeReader(false, false);
return; return;
} }
ContactId contactId = ctx.getContactId(); ContactId contactId = ctx.getContactId();
@@ -177,11 +181,17 @@ class ConnectionManagerImpl implements ConnectionManager {
public void run() { public void run() {
// Allocate a stream context // Allocate a stream context
StreamContext ctx = keyManager.getStreamContext(contactId, StreamContext ctx;
transportId); try {
ctx = keyManager.getStreamContext(contactId, transportId);
} catch (DbException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
disposeWriter(true);
return;
}
if (ctx == null) { if (ctx == null) {
LOG.warning("Could not allocate stream context"); LOG.warning("Could not allocate stream context");
disposeWriter(true); disposeWriter(false);
return; return;
} }
connectionRegistry.registerConnection(contactId, transportId); connectionRegistry.registerConnection(contactId, transportId);
@@ -233,10 +243,14 @@ class ConnectionManagerImpl implements ConnectionManager {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
disposeReader(true, false); disposeReader(true, false);
return; return;
} catch (DbException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
disposeReader(true, false);
return;
} }
if (ctx == null) { if (ctx == null) {
LOG.info("Unrecognised tag"); LOG.info("Unrecognised tag");
disposeReader(true, false); disposeReader(false, false);
return; return;
} }
contactId = ctx.getContactId(); contactId = ctx.getContactId();
@@ -262,11 +276,17 @@ class ConnectionManagerImpl implements ConnectionManager {
private void runOutgoingSession() { private void runOutgoingSession() {
// Allocate a stream context // Allocate a stream context
StreamContext ctx = keyManager.getStreamContext(contactId, StreamContext ctx;
transportId); try {
ctx = keyManager.getStreamContext(contactId, transportId);
} catch (DbException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
disposeWriter(true);
return;
}
if (ctx == null) { if (ctx == null) {
LOG.warning("Could not allocate stream context"); LOG.warning("Could not allocate stream context");
disposeWriter(true); disposeWriter(false);
return; return;
} }
try { try {
@@ -321,11 +341,17 @@ class ConnectionManagerImpl implements ConnectionManager {
public void run() { public void run() {
// Allocate a stream context // Allocate a stream context
StreamContext ctx = keyManager.getStreamContext(contactId, StreamContext ctx;
transportId); try {
ctx = keyManager.getStreamContext(contactId, transportId);
} catch (DbException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
disposeWriter(true);
return;
}
if (ctx == null) { if (ctx == null) {
LOG.warning("Could not allocate stream context"); LOG.warning("Could not allocate stream context");
disposeWriter(true); disposeWriter(false);
return; return;
} }
connectionRegistry.registerConnection(contactId, transportId); connectionRegistry.registerConnection(contactId, transportId);
@@ -358,6 +384,10 @@ class ConnectionManagerImpl implements ConnectionManager {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
disposeReader(true, true); disposeReader(true, true);
return; return;
} catch (DbException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
disposeReader(true, true);
return;
} }
// Unrecognised tags are suspicious in this case // Unrecognised tags are suspicious in this case
if (ctx == null) { if (ctx == null) {

View File

@@ -2,9 +2,7 @@ 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.DatabaseComponent;
import org.briarproject.api.db.DbException; import org.briarproject.api.db.DbException;
import org.briarproject.api.db.Transaction;
import org.briarproject.api.event.EventBus; import org.briarproject.api.event.EventBus;
import org.briarproject.api.event.TransportDisabledEvent; import org.briarproject.api.event.TransportDisabledEvent;
import org.briarproject.api.event.TransportEnabledEvent; import org.briarproject.api.event.TransportEnabledEvent;
@@ -13,23 +11,21 @@ import org.briarproject.api.lifecycle.Service;
import org.briarproject.api.plugins.ConnectionManager; import org.briarproject.api.plugins.ConnectionManager;
import org.briarproject.api.plugins.Plugin; import org.briarproject.api.plugins.Plugin;
import org.briarproject.api.plugins.PluginCallback; import org.briarproject.api.plugins.PluginCallback;
import org.briarproject.api.plugins.PluginConfig;
import org.briarproject.api.plugins.PluginManager; import org.briarproject.api.plugins.PluginManager;
import org.briarproject.api.plugins.TransportConnectionReader; import org.briarproject.api.plugins.TransportConnectionReader;
import org.briarproject.api.plugins.TransportConnectionWriter; 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.DuplexPluginConfig;
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.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.SimplexPluginConfig;
import org.briarproject.api.plugins.simplex.SimplexPluginFactory; import org.briarproject.api.plugins.simplex.SimplexPluginFactory;
import org.briarproject.api.properties.TransportProperties; import org.briarproject.api.properties.TransportProperties;
import org.briarproject.api.properties.TransportPropertyManager; import org.briarproject.api.properties.TransportPropertyManager;
import org.briarproject.api.settings.Settings; import org.briarproject.api.settings.Settings;
import org.briarproject.api.settings.SettingsManager; import org.briarproject.api.settings.SettingsManager;
import org.briarproject.api.system.Clock;
import org.briarproject.api.ui.UiCallback; import org.briarproject.api.ui.UiCallback;
import java.io.IOException; import java.io.IOException;
@@ -56,10 +52,7 @@ class PluginManagerImpl implements PluginManager, Service {
private final Executor ioExecutor; private final Executor ioExecutor;
private final EventBus eventBus; private final EventBus eventBus;
private final SimplexPluginConfig simplexPluginConfig; private final PluginConfig pluginConfig;
private final DuplexPluginConfig duplexPluginConfig;
private final Clock clock;
private final DatabaseComponent db;
private final Poller poller; private final Poller poller;
private final ConnectionManager connectionManager; private final ConnectionManager connectionManager;
private final SettingsManager settingsManager; private final SettingsManager settingsManager;
@@ -71,19 +64,14 @@ class PluginManagerImpl implements PluginManager, Service {
@Inject @Inject
PluginManagerImpl(@IoExecutor Executor ioExecutor, EventBus eventBus, PluginManagerImpl(@IoExecutor Executor ioExecutor, EventBus eventBus,
SimplexPluginConfig simplexPluginConfig, PluginConfig pluginConfig, Poller poller,
DuplexPluginConfig duplexPluginConfig, Clock clock,
DatabaseComponent db, Poller poller,
ConnectionManager connectionManager, ConnectionManager connectionManager,
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.simplexPluginConfig = simplexPluginConfig; this.pluginConfig = pluginConfig;
this.duplexPluginConfig = duplexPluginConfig;
this.clock = clock;
this.db = db;
this.poller = poller; this.poller = poller;
this.connectionManager = connectionManager; this.connectionManager = connectionManager;
this.settingsManager = settingsManager; this.settingsManager = settingsManager;
@@ -99,14 +87,14 @@ class PluginManagerImpl implements PluginManager, Service {
// Instantiate and start the simplex plugins // Instantiate and start the simplex plugins
LOG.info("Starting simplex plugins"); LOG.info("Starting simplex plugins");
Collection<SimplexPluginFactory> sFactories = Collection<SimplexPluginFactory> sFactories =
simplexPluginConfig.getFactories(); pluginConfig.getSimplexFactories();
final CountDownLatch sLatch = new CountDownLatch(sFactories.size()); final CountDownLatch sLatch = new CountDownLatch(sFactories.size());
for (SimplexPluginFactory factory : sFactories) for (SimplexPluginFactory factory : sFactories)
ioExecutor.execute(new SimplexPluginStarter(factory, sLatch)); ioExecutor.execute(new SimplexPluginStarter(factory, sLatch));
// 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 = Collection<DuplexPluginFactory> dFactories =
duplexPluginConfig.getFactories(); pluginConfig.getDuplexFactories();
final CountDownLatch dLatch = new CountDownLatch(dFactories.size()); final CountDownLatch dLatch = new CountDownLatch(dFactories.size());
for (DuplexPluginFactory factory : dFactories) for (DuplexPluginFactory factory : dFactories)
ioExecutor.execute(new DuplexPluginStarter(factory, dLatch)); ioExecutor.execute(new DuplexPluginStarter(factory, dLatch));
@@ -185,26 +173,9 @@ class PluginManagerImpl implements PluginManager, Service {
return; return;
} }
try { try {
long start = clock.currentTimeMillis(); long start = System.currentTimeMillis();
Transaction txn = db.startTransaction();
try {
db.addTransport(txn, id, plugin.getMaxLatency());
txn.setComplete();
} finally {
db.endTransaction(txn);
}
long duration = clock.currentTimeMillis() - start;
if (LOG.isLoggable(INFO))
LOG.info("Adding transport took " + duration + " ms");
} catch (DbException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
return;
}
try {
long start = clock.currentTimeMillis();
boolean started = plugin.start(); boolean started = plugin.start();
long duration = clock.currentTimeMillis() - start; long duration = System.currentTimeMillis() - start;
if (started) { if (started) {
plugins.put(id, plugin); plugins.put(id, plugin);
simplexPlugins.add(plugin); simplexPlugins.add(plugin);
@@ -254,26 +225,9 @@ class PluginManagerImpl implements PluginManager, Service {
return; return;
} }
try { try {
long start = clock.currentTimeMillis(); long start = System.currentTimeMillis();
Transaction txn = db.startTransaction();
try {
db.addTransport(txn, id, plugin.getMaxLatency());
txn.setComplete();
} finally {
db.endTransaction(txn);
}
long duration = clock.currentTimeMillis() - start;
if (LOG.isLoggable(INFO))
LOG.info("Adding transport took " + duration + " ms");
} catch (DbException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
return;
}
try {
long start = clock.currentTimeMillis();
boolean started = plugin.start(); boolean started = plugin.start();
long duration = clock.currentTimeMillis() - start; long duration = System.currentTimeMillis() - start;
if (started) { if (started) {
plugins.put(id, plugin); plugins.put(id, plugin);
duplexPlugins.add(plugin); duplexPlugins.add(plugin);
@@ -311,9 +265,9 @@ class PluginManagerImpl implements PluginManager, Service {
public void run() { public void run() {
try { try {
long start = clock.currentTimeMillis(); long start = System.currentTimeMillis();
plugin.stop(); plugin.stop();
long duration = clock.currentTimeMillis() - start; long duration = System.currentTimeMillis() - start;
if (LOG.isLoggable(INFO)) { if (LOG.isLoggable(INFO)) {
String name = plugin.getClass().getSimpleName(); String name = plugin.getClass().getSimpleName();
LOG.info("Stopping " + name + " took " + duration + " ms"); LOG.info("Stopping " + name + " took " + duration + " ms");

View File

@@ -1,8 +1,5 @@
package org.briarproject.plugins; package org.briarproject.plugins;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.briarproject.api.event.EventBus; import org.briarproject.api.event.EventBus;
import org.briarproject.api.lifecycle.IoExecutor; import org.briarproject.api.lifecycle.IoExecutor;
import org.briarproject.api.lifecycle.LifecycleManager; import org.briarproject.api.lifecycle.LifecycleManager;
@@ -19,10 +16,12 @@ 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 javax.inject.Inject;
import javax.inject.Singleton;
import dagger.Module; import dagger.Module;
import dagger.Provides; import dagger.Provides;
@Module @Module
public class PluginsModule { public class PluginsModule {
@@ -45,8 +44,8 @@ public class PluginsModule {
@Provides @Provides
ConnectionManager provideConnectionManager( ConnectionManager provideConnectionManager(
@IoExecutor Executor ioExecutor, @IoExecutor Executor ioExecutor, KeyManager keyManager,
KeyManager keyManager, StreamReaderFactory streamReaderFactory, StreamReaderFactory streamReaderFactory,
StreamWriterFactory streamWriterFactory, StreamWriterFactory streamWriterFactory,
SyncSessionFactory syncSessionFactory, SyncSessionFactory syncSessionFactory,
ConnectionRegistry connectionRegistry) { ConnectionRegistry connectionRegistry) {
@@ -61,7 +60,6 @@ public class PluginsModule {
return new ConnectionRegistryImpl(eventBus); return new ConnectionRegistryImpl(eventBus);
} }
@Provides @Provides
@Singleton @Singleton
PluginManager getPluginManager(LifecycleManager lifecycleManager, PluginManager getPluginManager(LifecycleManager lifecycleManager,
@@ -69,5 +67,4 @@ public class PluginsModule {
lifecycleManager.register(pluginManager); lifecycleManager.register(pluginManager);
return pluginManager; return pluginManager;
} }
} }

View File

@@ -1,13 +1,13 @@
package org.briarproject.plugins.file; package org.briarproject.plugins.file;
import static java.util.logging.Level.WARNING; import org.briarproject.api.plugins.TransportConnectionReader;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.logging.Logger; import java.util.logging.Logger;
import org.briarproject.api.plugins.TransportConnectionReader; import static java.util.logging.Level.WARNING;
class FileTransportReader implements TransportConnectionReader { class FileTransportReader implements TransportConnectionReader {
@@ -24,10 +24,6 @@ class FileTransportReader implements TransportConnectionReader {
this.plugin = plugin; this.plugin = plugin;
} }
public long getMaxLatency() {
return plugin.getMaxLatency();
}
public InputStream getInputStream() { public InputStream getInputStream() {
return in; return in;
} }

View File

@@ -30,6 +30,10 @@ public class LanTcpPluginFactory implements DuplexPluginFactory {
return LanTcpPlugin.ID; return LanTcpPlugin.ID;
} }
public int getMaxLatency() {
return MAX_LATENCY;
}
public DuplexPlugin createPlugin(DuplexPluginCallback callback) { public DuplexPlugin createPlugin(DuplexPluginCallback callback) {
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL, Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE); MAX_POLLING_INTERVAL, BACKOFF_BASE);

View File

@@ -1,16 +1,16 @@
package org.briarproject.plugins.tcp; package org.briarproject.plugins.tcp;
import org.briarproject.api.plugins.Plugin;
import org.briarproject.api.plugins.TransportConnectionReader;
import org.briarproject.api.plugins.TransportConnectionWriter;
import org.briarproject.api.plugins.duplex.DuplexTransportConnection;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.net.Socket; import java.net.Socket;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import org.briarproject.api.plugins.Plugin;
import org.briarproject.api.plugins.TransportConnectionReader;
import org.briarproject.api.plugins.TransportConnectionWriter;
import org.briarproject.api.plugins.duplex.DuplexTransportConnection;
class TcpTransportConnection implements DuplexTransportConnection { class TcpTransportConnection implements DuplexTransportConnection {
private final Plugin plugin; private final Plugin plugin;
@@ -38,10 +38,6 @@ class TcpTransportConnection implements DuplexTransportConnection {
private class Reader implements TransportConnectionReader { private class Reader implements TransportConnectionReader {
public long getMaxLatency() {
return plugin.getMaxLatency();
}
public InputStream getInputStream() throws IOException { public InputStream getInputStream() throws IOException {
return socket.getInputStream(); return socket.getInputStream();
} }

View File

@@ -33,6 +33,10 @@ public class WanTcpPluginFactory implements DuplexPluginFactory {
return WanTcpPlugin.ID; return WanTcpPlugin.ID;
} }
public int getMaxLatency() {
return MAX_LATENCY;
}
public DuplexPlugin createPlugin(DuplexPluginCallback callback) { public DuplexPlugin createPlugin(DuplexPluginCallback callback) {
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL, Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE); MAX_POLLING_INTERVAL, BACKOFF_BASE);

View File

@@ -84,7 +84,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
@Override @Override
public void addRemoteProperties(ContactId c, DeviceId dev, public void addRemoteProperties(ContactId c, DeviceId dev,
Map<TransportId, TransportProperties> props) throws DbException { Map<TransportId, TransportProperties> props) throws DbException {
Transaction txn = db.startTransaction(); Transaction txn = db.startTransaction(false);
try { try {
Group g = getContactGroup(db.getContact(txn, c)); Group g = getContactGroup(db.getContact(txn, c));
for (Entry<TransportId, TransportProperties> e : props.entrySet()) { for (Entry<TransportId, TransportProperties> e : props.entrySet()) {
@@ -101,7 +101,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
public Map<TransportId, TransportProperties> getLocalProperties() public Map<TransportId, TransportProperties> getLocalProperties()
throws DbException { throws DbException {
Map<TransportId, TransportProperties> local; Map<TransportId, TransportProperties> local;
Transaction txn = db.startTransaction(); Transaction txn = db.startTransaction(true);
try { try {
local = getLocalProperties(txn); local = getLocalProperties(txn);
txn.setComplete(); txn.setComplete();
@@ -116,7 +116,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
throws DbException { throws DbException {
try { try {
TransportProperties p = null; TransportProperties p = null;
Transaction txn = db.startTransaction(); Transaction txn = db.startTransaction(true);
try { try {
// Find the latest local update // Find the latest local update
LatestUpdate latest = findLatest(txn, localGroup.getId(), t, LatestUpdate latest = findLatest(txn, localGroup.getId(), t,
@@ -146,7 +146,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
try { try {
Map<ContactId, TransportProperties> remote = Map<ContactId, TransportProperties> remote =
new HashMap<ContactId, TransportProperties>(); new HashMap<ContactId, TransportProperties>();
Transaction txn = db.startTransaction(); Transaction txn = db.startTransaction(true);
try { try {
for (Contact c : db.getContacts(txn)) { for (Contact c : db.getContacts(txn)) {
Group g = getContactGroup(c); Group g = getContactGroup(c);
@@ -173,7 +173,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
public void mergeLocalProperties(TransportId t, TransportProperties p) public void mergeLocalProperties(TransportId t, TransportProperties p)
throws DbException { throws DbException {
try { try {
Transaction txn = db.startTransaction(); Transaction txn = db.startTransaction(false);
try { try {
// Create the local group if necessary // Create the local group if necessary
db.addGroup(txn, localGroup); db.addGroup(txn, localGroup);

View File

@@ -21,7 +21,7 @@ class SettingsManagerImpl implements SettingsManager {
@Override @Override
public Settings getSettings(String namespace) throws DbException { public Settings getSettings(String namespace) throws DbException {
Settings s; Settings s;
Transaction txn = db.startTransaction(); Transaction txn = db.startTransaction(true);
try { try {
s = db.getSettings(txn, namespace); s = db.getSettings(txn, namespace);
txn.setComplete(); txn.setComplete();
@@ -33,7 +33,7 @@ class SettingsManagerImpl implements SettingsManager {
@Override @Override
public void mergeSettings(Settings s, String namespace) throws DbException { public void mergeSettings(Settings s, String namespace) throws DbException {
Transaction txn = db.startTransaction(); Transaction txn = db.startTransaction(false);
try { try {
db.mergeSettings(txn, s, namespace); db.mergeSettings(txn, s, namespace);
txn.setComplete(); txn.setComplete();

View File

@@ -1,6 +1,5 @@
package org.briarproject.sync; package org.briarproject.sync;
import org.briarproject.api.TransportId;
import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactId;
import org.briarproject.api.db.DatabaseComponent; import org.briarproject.api.db.DatabaseComponent;
import org.briarproject.api.db.DbException; import org.briarproject.api.db.DbException;
@@ -15,7 +14,6 @@ import org.briarproject.api.event.MessageSharedEvent;
import org.briarproject.api.event.MessageToAckEvent; import org.briarproject.api.event.MessageToAckEvent;
import org.briarproject.api.event.MessageToRequestEvent; import org.briarproject.api.event.MessageToRequestEvent;
import org.briarproject.api.event.ShutdownEvent; import org.briarproject.api.event.ShutdownEvent;
import org.briarproject.api.event.TransportRemovedEvent;
import org.briarproject.api.sync.Ack; import org.briarproject.api.sync.Ack;
import org.briarproject.api.sync.Offer; import org.briarproject.api.sync.Offer;
import org.briarproject.api.sync.PacketWriter; import org.briarproject.api.sync.PacketWriter;
@@ -59,7 +57,6 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
private final EventBus eventBus; private final EventBus eventBus;
private final Clock clock; private final Clock clock;
private final ContactId contactId; private final ContactId contactId;
private final TransportId transportId;
private final int maxLatency, maxIdleTime; private final int maxLatency, maxIdleTime;
private final PacketWriter packetWriter; private final PacketWriter packetWriter;
private final BlockingQueue<ThrowingRunnable<IOException>> writerTasks; private final BlockingQueue<ThrowingRunnable<IOException>> writerTasks;
@@ -67,15 +64,13 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
private volatile boolean interrupted = false; private volatile boolean interrupted = false;
DuplexOutgoingSession(DatabaseComponent db, Executor dbExecutor, DuplexOutgoingSession(DatabaseComponent db, Executor dbExecutor,
EventBus eventBus, Clock clock, ContactId contactId, EventBus eventBus, Clock clock, ContactId contactId, int maxLatency,
TransportId transportId, int maxLatency, int maxIdleTime, int maxIdleTime, PacketWriter packetWriter) {
PacketWriter packetWriter) {
this.db = db; this.db = db;
this.dbExecutor = dbExecutor; this.dbExecutor = dbExecutor;
this.eventBus = eventBus; this.eventBus = eventBus;
this.clock = clock; this.clock = clock;
this.contactId = contactId; this.contactId = contactId;
this.transportId = transportId;
this.maxLatency = maxLatency; this.maxLatency = maxLatency;
this.maxIdleTime = maxIdleTime; this.maxIdleTime = maxIdleTime;
this.packetWriter = packetWriter; this.packetWriter = packetWriter;
@@ -167,9 +162,6 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
dbExecutor.execute(new GenerateRequest()); dbExecutor.execute(new GenerateRequest());
} else if (e instanceof ShutdownEvent) { } else if (e instanceof ShutdownEvent) {
interrupt(); interrupt();
} else if (e instanceof TransportRemovedEvent) {
TransportRemovedEvent t = (TransportRemovedEvent) e;
if (t.getTransportId().equals(transportId)) interrupt();
} }
} }
@@ -180,7 +172,7 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
if (interrupted) return; if (interrupted) return;
try { try {
Ack a; Ack a;
Transaction txn = db.startTransaction(); Transaction txn = db.startTransaction(false);
try { try {
a = db.generateAck(txn, contactId, MAX_MESSAGE_IDS); a = db.generateAck(txn, contactId, MAX_MESSAGE_IDS);
txn.setComplete(); txn.setComplete();
@@ -221,7 +213,7 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
if (interrupted) return; if (interrupted) return;
try { try {
Collection<byte[]> b; Collection<byte[]> b;
Transaction txn = db.startTransaction(); Transaction txn = db.startTransaction(false);
try { try {
b = db.generateRequestedBatch(txn, contactId, b = db.generateRequestedBatch(txn, contactId,
MAX_PACKET_PAYLOAD_LENGTH, maxLatency); MAX_PACKET_PAYLOAD_LENGTH, maxLatency);
@@ -263,7 +255,7 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
if (interrupted) return; if (interrupted) return;
try { try {
Offer o; Offer o;
Transaction txn = db.startTransaction(); Transaction txn = db.startTransaction(false);
try { try {
o = db.generateOffer(txn, contactId, MAX_MESSAGE_IDS, o = db.generateOffer(txn, contactId, MAX_MESSAGE_IDS,
maxLatency); maxLatency);
@@ -305,7 +297,7 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
if (interrupted) return; if (interrupted) return;
try { try {
Request r; Request r;
Transaction txn = db.startTransaction(); Transaction txn = db.startTransaction(false);
try { try {
r = db.generateRequest(txn, contactId, MAX_MESSAGE_IDS); r = db.generateRequest(txn, contactId, MAX_MESSAGE_IDS);
txn.setComplete(); txn.setComplete();

View File

@@ -1,7 +1,6 @@
package org.briarproject.sync; package org.briarproject.sync;
import org.briarproject.api.FormatException; import org.briarproject.api.FormatException;
import org.briarproject.api.TransportId;
import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactId;
import org.briarproject.api.db.DatabaseComponent; import org.briarproject.api.db.DatabaseComponent;
import org.briarproject.api.db.DbException; import org.briarproject.api.db.DbException;
@@ -11,7 +10,6 @@ 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.EventListener;
import org.briarproject.api.event.ShutdownEvent; import org.briarproject.api.event.ShutdownEvent;
import org.briarproject.api.event.TransportRemovedEvent;
import org.briarproject.api.sync.Ack; import org.briarproject.api.sync.Ack;
import org.briarproject.api.sync.Message; import org.briarproject.api.sync.Message;
import org.briarproject.api.sync.Offer; import org.briarproject.api.sync.Offer;
@@ -37,19 +35,17 @@ class IncomingSession implements SyncSession, EventListener {
private final Executor dbExecutor; private final Executor dbExecutor;
private final EventBus eventBus; private final EventBus eventBus;
private final ContactId contactId; private final ContactId contactId;
private final TransportId transportId;
private final PacketReader packetReader; private final PacketReader packetReader;
private volatile boolean interrupted = false; private volatile boolean interrupted = false;
IncomingSession(DatabaseComponent db, Executor dbExecutor, IncomingSession(DatabaseComponent db, Executor dbExecutor,
EventBus eventBus, ContactId contactId, TransportId transportId, EventBus eventBus, ContactId contactId,
PacketReader packetReader) { PacketReader packetReader) {
this.db = db; this.db = db;
this.dbExecutor = dbExecutor; this.dbExecutor = dbExecutor;
this.eventBus = eventBus; this.eventBus = eventBus;
this.contactId = contactId; this.contactId = contactId;
this.transportId = transportId;
this.packetReader = packetReader; this.packetReader = packetReader;
} }
@@ -90,9 +86,6 @@ class IncomingSession implements SyncSession, EventListener {
if (c.getContactId().equals(contactId)) interrupt(); if (c.getContactId().equals(contactId)) interrupt();
} else if (e instanceof ShutdownEvent) { } else if (e instanceof ShutdownEvent) {
interrupt(); interrupt();
} else if (e instanceof TransportRemovedEvent) {
TransportRemovedEvent t = (TransportRemovedEvent) e;
if (t.getTransportId().equals(transportId)) interrupt();
} }
} }
@@ -106,7 +99,7 @@ class IncomingSession implements SyncSession, EventListener {
public void run() { public void run() {
try { try {
Transaction txn = db.startTransaction(); Transaction txn = db.startTransaction(false);
try { try {
db.receiveAck(txn, contactId, ack); db.receiveAck(txn, contactId, ack);
txn.setComplete(); txn.setComplete();
@@ -130,7 +123,7 @@ class IncomingSession implements SyncSession, EventListener {
public void run() { public void run() {
try { try {
Transaction txn = db.startTransaction(); Transaction txn = db.startTransaction(false);
try { try {
db.receiveMessage(txn, contactId, message); db.receiveMessage(txn, contactId, message);
txn.setComplete(); txn.setComplete();
@@ -154,7 +147,7 @@ class IncomingSession implements SyncSession, EventListener {
public void run() { public void run() {
try { try {
Transaction txn = db.startTransaction(); Transaction txn = db.startTransaction(false);
try { try {
db.receiveOffer(txn, contactId, offer); db.receiveOffer(txn, contactId, offer);
txn.setComplete(); txn.setComplete();
@@ -178,7 +171,7 @@ class IncomingSession implements SyncSession, EventListener {
public void run() { public void run() {
try { try {
Transaction txn = db.startTransaction(); Transaction txn = db.startTransaction(false);
try { try {
db.receiveRequest(txn, contactId, request); db.receiveRequest(txn, contactId, request);
txn.setComplete(); txn.setComplete();

View File

@@ -1,6 +1,5 @@
package org.briarproject.sync; package org.briarproject.sync;
import org.briarproject.api.TransportId;
import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactId;
import org.briarproject.api.db.DatabaseComponent; import org.briarproject.api.db.DatabaseComponent;
import org.briarproject.api.db.DbException; import org.briarproject.api.db.DbException;
@@ -10,7 +9,6 @@ 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.EventListener;
import org.briarproject.api.event.ShutdownEvent; import org.briarproject.api.event.ShutdownEvent;
import org.briarproject.api.event.TransportRemovedEvent;
import org.briarproject.api.sync.Ack; import org.briarproject.api.sync.Ack;
import org.briarproject.api.sync.PacketWriter; import org.briarproject.api.sync.PacketWriter;
import org.briarproject.api.sync.SyncSession; import org.briarproject.api.sync.SyncSession;
@@ -48,7 +46,6 @@ class SimplexOutgoingSession implements SyncSession, EventListener {
private final Executor dbExecutor; private final Executor dbExecutor;
private final EventBus eventBus; private final EventBus eventBus;
private final ContactId contactId; private final ContactId contactId;
private final TransportId transportId;
private final int maxLatency; private final int maxLatency;
private final PacketWriter packetWriter; private final PacketWriter packetWriter;
private final AtomicInteger outstandingQueries; private final AtomicInteger outstandingQueries;
@@ -57,13 +54,12 @@ class SimplexOutgoingSession implements SyncSession, EventListener {
private volatile boolean interrupted = false; private volatile boolean interrupted = false;
SimplexOutgoingSession(DatabaseComponent db, Executor dbExecutor, SimplexOutgoingSession(DatabaseComponent db, Executor dbExecutor,
EventBus eventBus, ContactId contactId, TransportId transportId, EventBus eventBus, ContactId contactId,
int maxLatency, PacketWriter packetWriter) { int maxLatency, PacketWriter packetWriter) {
this.db = db; this.db = db;
this.dbExecutor = dbExecutor; this.dbExecutor = dbExecutor;
this.eventBus = eventBus; this.eventBus = eventBus;
this.contactId = contactId; this.contactId = contactId;
this.transportId = transportId;
this.maxLatency = maxLatency; this.maxLatency = maxLatency;
this.packetWriter = packetWriter; this.packetWriter = packetWriter;
outstandingQueries = new AtomicInteger(2); // One per type of packet outstandingQueries = new AtomicInteger(2); // One per type of packet
@@ -108,9 +104,6 @@ class SimplexOutgoingSession implements SyncSession, EventListener {
if (c.getContactId().equals(contactId)) interrupt(); if (c.getContactId().equals(contactId)) interrupt();
} else if (e instanceof ShutdownEvent) { } else if (e instanceof ShutdownEvent) {
interrupt(); interrupt();
} else if (e instanceof TransportRemovedEvent) {
TransportRemovedEvent t = (TransportRemovedEvent) e;
if (t.getTransportId().equals(transportId)) interrupt();
} }
} }
@@ -121,7 +114,7 @@ class SimplexOutgoingSession implements SyncSession, EventListener {
if (interrupted) return; if (interrupted) return;
try { try {
Ack a; Ack a;
Transaction txn = db.startTransaction(); Transaction txn = db.startTransaction(false);
try { try {
a = db.generateAck(txn, contactId, MAX_MESSAGE_IDS); a = db.generateAck(txn, contactId, MAX_MESSAGE_IDS);
txn.setComplete(); txn.setComplete();
@@ -163,7 +156,7 @@ class SimplexOutgoingSession implements SyncSession, EventListener {
if (interrupted) return; if (interrupted) return;
try { try {
Collection<byte[]> b; Collection<byte[]> b;
Transaction txn = db.startTransaction(); Transaction txn = db.startTransaction(false);
try { try {
b = db.generateBatch(txn, contactId, b = db.generateBatch(txn, contactId,
MAX_PACKET_PAYLOAD_LENGTH, maxLatency); MAX_PACKET_PAYLOAD_LENGTH, maxLatency);

View File

@@ -1,6 +1,5 @@
package org.briarproject.sync; package org.briarproject.sync;
import org.briarproject.api.TransportId;
import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactId;
import org.briarproject.api.db.DatabaseComponent; import org.briarproject.api.db.DatabaseComponent;
import org.briarproject.api.db.DatabaseExecutor; import org.briarproject.api.db.DatabaseExecutor;
@@ -41,24 +40,22 @@ class SyncSessionFactoryImpl implements SyncSessionFactory {
this.packetWriterFactory = packetWriterFactory; this.packetWriterFactory = packetWriterFactory;
} }
public SyncSession createIncomingSession(ContactId c, TransportId t, public SyncSession createIncomingSession(ContactId c, InputStream in) {
InputStream in) {
PacketReader packetReader = packetReaderFactory.createPacketReader(in); PacketReader packetReader = packetReaderFactory.createPacketReader(in);
return new IncomingSession(db, dbExecutor, eventBus, c, t, return new IncomingSession(db, dbExecutor, eventBus, c, packetReader);
packetReader);
} }
public SyncSession createSimplexOutgoingSession(ContactId c, TransportId t, public SyncSession createSimplexOutgoingSession(ContactId c,
int maxLatency, OutputStream out) { int maxLatency, OutputStream out) {
PacketWriter packetWriter = packetWriterFactory.createPacketWriter(out); PacketWriter packetWriter = packetWriterFactory.createPacketWriter(out);
return new SimplexOutgoingSession(db, dbExecutor, eventBus, c, t, return new SimplexOutgoingSession(db, dbExecutor, eventBus, c,
maxLatency, packetWriter); maxLatency, packetWriter);
} }
public SyncSession createDuplexOutgoingSession(ContactId c, TransportId t, public SyncSession createDuplexOutgoingSession(ContactId c, int maxLatency,
int maxLatency, int maxIdleTime, OutputStream out) { int maxIdleTime, OutputStream out) {
PacketWriter packetWriter = packetWriterFactory.createPacketWriter(out); PacketWriter packetWriter = packetWriterFactory.createPacketWriter(out);
return new DuplexOutgoingSession(db, dbExecutor, eventBus, clock, c, t, return new DuplexOutgoingSession(db, dbExecutor, eventBus, clock, c,
maxLatency, maxIdleTime, packetWriter); maxLatency, maxIdleTime, packetWriter);
} }
} }

View File

@@ -83,7 +83,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
public void run() { public void run() {
try { try {
Queue<MessageId> unvalidated = new LinkedList<MessageId>(); Queue<MessageId> unvalidated = new LinkedList<MessageId>();
Transaction txn = db.startTransaction(); Transaction txn = db.startTransaction(true);
try { try {
unvalidated.addAll(db.getMessagesToValidate(txn, c)); unvalidated.addAll(db.getMessagesToValidate(txn, c));
txn.setComplete(); txn.setComplete();
@@ -106,7 +106,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
try { try {
Message m = null; Message m = null;
Group g = null; Group g = null;
Transaction txn = db.startTransaction(); Transaction txn = db.startTransaction(true);
try { try {
MessageId id = unvalidated.poll(); MessageId id = unvalidated.poll();
byte[] raw = db.getRawMessage(txn, id); byte[] raw = db.getRawMessage(txn, id);
@@ -160,7 +160,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
dbExecutor.execute(new Runnable() { dbExecutor.execute(new Runnable() {
public void run() { public void run() {
try { try {
Transaction txn = db.startTransaction(); Transaction txn = db.startTransaction(false);
try { try {
if (meta == null) { if (meta == null) {
db.setMessageValid(txn, m, c, false); db.setMessageValid(txn, m, c, false);
@@ -198,7 +198,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
public void run() { public void run() {
try { try {
Group g; Group g;
Transaction txn = db.startTransaction(); Transaction txn = db.startTransaction(true);
try { try {
g = db.getGroup(txn, m.getGroupId()); g = db.getGroup(txn, m.getGroupId());
txn.setComplete(); txn.setComplete();

View File

@@ -13,15 +13,16 @@ import org.briarproject.api.event.ContactRemovedEvent;
import org.briarproject.api.event.ContactStatusChangedEvent; import org.briarproject.api.event.ContactStatusChangedEvent;
import org.briarproject.api.event.Event; import org.briarproject.api.event.Event;
import org.briarproject.api.event.EventListener; import org.briarproject.api.event.EventListener;
import org.briarproject.api.event.TransportAddedEvent;
import org.briarproject.api.event.TransportRemovedEvent;
import org.briarproject.api.lifecycle.Service; import org.briarproject.api.lifecycle.Service;
import org.briarproject.api.plugins.PluginConfig;
import org.briarproject.api.plugins.duplex.DuplexPluginFactory;
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.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;
import java.util.Collection; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@@ -30,6 +31,7 @@ import java.util.logging.Logger;
import javax.inject.Inject; import javax.inject.Inject;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
class KeyManagerImpl implements KeyManager, Service, EventListener { class KeyManagerImpl implements KeyManager, Service, EventListener {
@@ -40,6 +42,7 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
private final DatabaseComponent db; private final DatabaseComponent db;
private final CryptoComponent crypto; private final CryptoComponent crypto;
private final ExecutorService dbExecutor; private final ExecutorService dbExecutor;
private final PluginConfig pluginConfig;
private final Timer timer; private final Timer timer;
private final Clock clock; private final Clock clock;
private final Map<ContactId, Boolean> activeContacts; private final Map<ContactId, Boolean> activeContacts;
@@ -47,11 +50,12 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
@Inject @Inject
KeyManagerImpl(DatabaseComponent db, CryptoComponent crypto, KeyManagerImpl(DatabaseComponent db, CryptoComponent crypto,
@DatabaseExecutor ExecutorService dbExecutor, Timer timer, @DatabaseExecutor ExecutorService dbExecutor,
Clock clock) { PluginConfig pluginConfig, Timer timer, Clock clock) {
this.db = db; this.db = db;
this.crypto = crypto; this.crypto = crypto;
this.dbExecutor = dbExecutor; this.dbExecutor = dbExecutor;
this.pluginConfig = pluginConfig;
this.timer = timer; 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
@@ -61,21 +65,29 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
@Override @Override
public boolean start() { public boolean start() {
Map<TransportId, Integer> transports =
new HashMap<TransportId, Integer>();
for (SimplexPluginFactory f : pluginConfig.getSimplexFactories())
transports.put(f.getId(), f.getMaxLatency());
for (DuplexPluginFactory f : pluginConfig.getDuplexFactories())
transports.put(f.getId(), f.getMaxLatency());
try { try {
Collection<Contact> contacts; Transaction txn = db.startTransaction(false);
Map<TransportId, Integer> latencies;
Transaction txn = db.startTransaction();
try { try {
contacts = db.getContacts(txn); for (Contact c : db.getContacts(txn))
latencies = db.getTransportLatencies(txn); if (c.isActive()) activeContacts.put(c.getId(), true);
for (Entry<TransportId, Integer> e : transports.entrySet())
db.addTransport(txn, e.getKey(), e.getValue());
for (Entry<TransportId, Integer> e : transports.entrySet()) {
TransportKeyManager m = new TransportKeyManager(db, crypto,
timer, clock, e.getKey(), e.getValue());
managers.put(e.getKey(), m);
m.start(txn);
}
txn.setComplete(); txn.setComplete();
} finally { } finally {
db.endTransaction(txn); db.endTransaction(txn);
} }
for (Contact c : contacts)
if (c.isActive()) activeContacts.put(c.getId(), true);
for (Entry<TransportId, Integer> e : latencies.entrySet())
addTransport(e.getKey(), e.getValue());
} catch (DbException e) { } catch (DbException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
return false; return false;
@@ -94,43 +106,49 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
m.addContact(txn, c, master, timestamp, alice); m.addContact(txn, c, master, timestamp, alice);
} }
public StreamContext getStreamContext(ContactId c, TransportId t) { public StreamContext getStreamContext(ContactId c, TransportId t)
throws DbException {
// Don't allow outgoing streams to inactive contacts // Don't allow outgoing streams to inactive contacts
if (!activeContacts.containsKey(c)) return null; if (!activeContacts.containsKey(c)) return null;
TransportKeyManager m = managers.get(t); TransportKeyManager m = managers.get(t);
return m == null ? null : m.getStreamContext(c); if (m == null) {
if (LOG.isLoggable(INFO)) LOG.info("No key manager for " + t);
return null;
}
StreamContext ctx = null;
Transaction txn = db.startTransaction(false);
try {
ctx = m.getStreamContext(txn, c);
txn.setComplete();
} finally {
db.endTransaction(txn);
}
return ctx;
} }
public StreamContext getStreamContext(TransportId t, byte[] tag) { public StreamContext getStreamContext(TransportId t, byte[] tag)
throws DbException {
TransportKeyManager m = managers.get(t); TransportKeyManager m = managers.get(t);
if (m == null) return null; if (m == null) {
StreamContext ctx = m.getStreamContext(tag); if (LOG.isLoggable(INFO)) LOG.info("No key manager for " + t);
if (ctx == null) return null; return null;
// Activate the contact if not already active }
if (!activeContacts.containsKey(ctx.getContactId())) { StreamContext ctx = null;
try { Transaction txn = db.startTransaction(false);
Transaction txn = db.startTransaction(); try {
try { ctx = m.getStreamContext(txn, tag);
db.setContactActive(txn, ctx.getContactId(), true); // Activate the contact if not already active
txn.setComplete(); if (ctx != null && !activeContacts.containsKey(ctx.getContactId()))
} finally { db.setContactActive(txn, ctx.getContactId(), true);
db.endTransaction(txn); txn.setComplete();
} } finally {
} catch (DbException e) { db.endTransaction(txn);
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
return null;
}
} }
return ctx; return ctx;
} }
public void eventOccurred(Event e) { public void eventOccurred(Event e) {
if (e instanceof TransportAddedEvent) { if (e instanceof ContactRemovedEvent) {
TransportAddedEvent t = (TransportAddedEvent) e;
addTransport(t.getTransportId(), t.getMaxLatency());
} else if (e instanceof TransportRemovedEvent) {
removeTransport(((TransportRemovedEvent) e).getTransportId());
} else if (e instanceof ContactRemovedEvent) {
removeContact(((ContactRemovedEvent) e).getContactId()); removeContact(((ContactRemovedEvent) e).getContactId());
} else if (e instanceof ContactStatusChangedEvent) { } else if (e instanceof ContactStatusChangedEvent) {
ContactStatusChangedEvent c = (ContactStatusChangedEvent) e; ContactStatusChangedEvent c = (ContactStatusChangedEvent) e;
@@ -139,21 +157,6 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
} }
} }
private void addTransport(final TransportId t, final int maxLatency) {
dbExecutor.execute(new Runnable() {
public void run() {
TransportKeyManager m = new TransportKeyManager(db, crypto,
timer, clock, t, maxLatency);
// Don't add transport twice if event is received during startup
if (managers.putIfAbsent(t, m) == null) m.start();
}
});
}
private void removeTransport(TransportId t) {
managers.remove(t);
}
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() {

View File

@@ -60,46 +60,20 @@ class TransportKeyManager {
keys = new HashMap<ContactId, MutableTransportKeys>(); keys = new HashMap<ContactId, MutableTransportKeys>();
} }
void start() { void start(Transaction txn) throws DbException {
long now = clock.currentTimeMillis(); long now = clock.currentTimeMillis();
lock.lock(); lock.lock();
try { try {
// Load the transport keys from the DB // Load the transport keys from the DB
Map<ContactId, TransportKeys> loaded; Map<ContactId, TransportKeys> loaded =
try { db.getTransportKeys(txn, transportId);
Transaction txn = db.startTransaction();
try {
loaded = db.getTransportKeys(txn, transportId);
txn.setComplete();
} finally {
db.endTransaction(txn);
}
} catch (DbException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
return;
}
// Rotate the keys to the current rotation period // Rotate the keys to the current rotation period
Map<ContactId, TransportKeys> rotated = RotationResult rotationResult = rotateKeys(loaded, now);
new HashMap<ContactId, TransportKeys>();
Map<ContactId, TransportKeys> current =
new HashMap<ContactId, TransportKeys>();
long rotationPeriod = now / rotationPeriodLength;
for (Entry<ContactId, TransportKeys> e : loaded.entrySet()) {
ContactId c = e.getKey();
TransportKeys k = e.getValue();
TransportKeys k1 = crypto.rotateTransportKeys(k,
rotationPeriod);
if (k1.getRotationPeriod() > k.getRotationPeriod())
rotated.put(c, k1);
current.put(c, k1);
}
// Initialise mutable state for all contacts // Initialise mutable state for all contacts
for (Entry<ContactId, TransportKeys> e : current.entrySet()) addKeys(rotationResult.current);
addKeys(e.getKey(), new MutableTransportKeys(e.getValue()));
// Write any rotated keys back to the DB // Write any rotated keys back to the DB
updateTransportKeys(rotated); if (!rotationResult.rotated.isEmpty())
} catch (DbException e) { db.updateTransportKeys(txn, rotationResult.rotated);
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
} finally { } finally {
lock.unlock(); lock.unlock();
} }
@@ -107,6 +81,27 @@ class TransportKeyManager {
scheduleKeyRotation(now); scheduleKeyRotation(now);
} }
private RotationResult rotateKeys(Map<ContactId, TransportKeys> keys,
long now) {
RotationResult rotationResult = new RotationResult();
long rotationPeriod = now / rotationPeriodLength;
for (Entry<ContactId, TransportKeys> e : keys.entrySet()) {
ContactId c = e.getKey();
TransportKeys k = e.getValue();
TransportKeys k1 = crypto.rotateTransportKeys(k, rotationPeriod);
if (k1.getRotationPeriod() > k.getRotationPeriod())
rotationResult.rotated.put(c, k1);
rotationResult.current.put(c, k1);
}
return rotationResult;
}
// Locking: lock
private void addKeys(Map<ContactId, TransportKeys> m) {
for (Entry<ContactId, TransportKeys> e : m.entrySet())
addKeys(e.getKey(), new MutableTransportKeys(e.getValue()));
}
// Locking: lock // Locking: lock
private void addKeys(ContactId c, MutableTransportKeys m) { private void addKeys(ContactId c, MutableTransportKeys m) {
encodeTags(c, m.getPreviousIncomingKeys()); encodeTags(c, m.getPreviousIncomingKeys());
@@ -126,23 +121,21 @@ class TransportKeyManager {
} }
} }
private void updateTransportKeys(Map<ContactId, TransportKeys> rotated)
throws DbException {
if (!rotated.isEmpty()) {
Transaction txn = db.startTransaction();
try {
db.updateTransportKeys(txn, rotated);
txn.setComplete();
} finally {
db.endTransaction(txn);
}
}
}
private void scheduleKeyRotation(long now) { private void scheduleKeyRotation(long now) {
TimerTask task = new TimerTask() { TimerTask task = new TimerTask() {
public void run() { public void run() {
rotateKeys(); try {
Transaction txn = db.startTransaction(false);
try {
rotateKeys(txn);
txn.setComplete();
} finally {
db.endTransaction(txn);
}
} catch (DbException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
}
} }
}; };
long delay = rotationPeriodLength - now % rotationPeriodLength; long delay = rotationPeriodLength - now % rotationPeriodLength;
@@ -185,7 +178,8 @@ class TransportKeyManager {
} }
} }
StreamContext getStreamContext(ContactId c) { StreamContext getStreamContext(Transaction txn, ContactId c)
throws DbException {
lock.lock(); lock.lock();
try { try {
// Look up the outgoing keys for the contact // Look up the outgoing keys for the contact
@@ -198,24 +192,16 @@ class TransportKeyManager {
outKeys.getStreamCounter()); outKeys.getStreamCounter());
// Increment the stream counter and write it back to the DB // Increment the stream counter and write it back to the DB
outKeys.incrementStreamCounter(); outKeys.incrementStreamCounter();
Transaction txn = db.startTransaction(); db.incrementStreamCounter(txn, c, transportId,
try { outKeys.getRotationPeriod());
db.incrementStreamCounter(txn, c, transportId,
outKeys.getRotationPeriod());
txn.setComplete();
} finally {
db.endTransaction(txn);
}
return ctx; return ctx;
} catch (DbException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
return null;
} finally { } finally {
lock.unlock(); lock.unlock();
} }
} }
StreamContext getStreamContext(byte[] tag) { StreamContext getStreamContext(Transaction txn, byte[] tag)
throws DbException {
lock.lock(); lock.lock();
try { try {
// Look up the incoming keys for the tag // Look up the incoming keys for the tag
@@ -244,53 +230,33 @@ class TransportKeyManager {
inContexts.remove(new Bytes(removeTag)); inContexts.remove(new Bytes(removeTag));
} }
// Write the window back to the DB // Write the window back to the DB
Transaction txn = db.startTransaction(); db.setReorderingWindow(txn, tagCtx.contactId, transportId,
try { inKeys.getRotationPeriod(), window.getBase(),
db.setReorderingWindow(txn, tagCtx.contactId, transportId, window.getBitmap());
inKeys.getRotationPeriod(), window.getBase(),
window.getBitmap());
txn.setComplete();
} finally {
db.endTransaction(txn);
}
return ctx; return ctx;
} catch (DbException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
return null;
} finally { } finally {
lock.unlock(); lock.unlock();
} }
} }
private void rotateKeys() { private void rotateKeys(Transaction txn) throws DbException {
long now = clock.currentTimeMillis(); long now = clock.currentTimeMillis();
lock.lock(); lock.lock();
try { try {
// Rotate the keys to the current rotation period // Rotate the keys to the current rotation period
Map<ContactId, TransportKeys> rotated = Map<ContactId, TransportKeys> snapshot =
new HashMap<ContactId, TransportKeys>(); new HashMap<ContactId, TransportKeys>();
Map<ContactId, TransportKeys> current = for (Entry<ContactId, MutableTransportKeys> e : keys.entrySet())
new HashMap<ContactId, TransportKeys>(); snapshot.put(e.getKey(), e.getValue().snapshot());
long rotationPeriod = now / rotationPeriodLength; RotationResult rotationResult = rotateKeys(snapshot, now);
for (Entry<ContactId, MutableTransportKeys> e : keys.entrySet()) {
ContactId c = e.getKey();
TransportKeys k = e.getValue().snapshot();
TransportKeys k1 = crypto.rotateTransportKeys(k,
rotationPeriod);
if (k1.getRotationPeriod() > k.getRotationPeriod())
rotated.put(c, k1);
current.put(c, k1);
}
// Rebuild the mutable state for all contacts // Rebuild the mutable state for all contacts
inContexts.clear(); inContexts.clear();
outContexts.clear(); outContexts.clear();
keys.clear(); keys.clear();
for (Entry<ContactId, TransportKeys> e : current.entrySet()) addKeys(rotationResult.current);
addKeys(e.getKey(), new MutableTransportKeys(e.getValue()));
// Write any rotated keys back to the DB // Write any rotated keys back to the DB
updateTransportKeys(rotated); if (!rotationResult.rotated.isEmpty())
} catch (DbException e) { db.updateTransportKeys(txn, rotationResult.rotated);
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
} finally { } finally {
lock.unlock(); lock.unlock();
} }
@@ -311,4 +277,14 @@ class TransportKeyManager {
this.streamNumber = streamNumber; this.streamNumber = streamNumber;
} }
} }
private static class RotationResult {
private final Map<ContactId, TransportKeys> current, rotated;
private RotationResult() {
current = new HashMap<ContactId, TransportKeys>();
rotated = new HashMap<ContactId, TransportKeys>();
}
}
} }

View File

@@ -1,10 +1,6 @@
package org.briarproject.lifecycle; package org.briarproject.lifecycle;
import org.briarproject.api.db.DatabaseComponent;
import org.briarproject.api.event.EventBus;
import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.lifecycle.ShutdownManager; import org.briarproject.api.lifecycle.ShutdownManager;
import org.briarproject.api.system.Clock;
import org.briarproject.util.OsUtils; import org.briarproject.util.OsUtils;
import javax.inject.Singleton; import javax.inject.Singleton;
@@ -15,22 +11,10 @@ import dagger.Provides;
@Module @Module
public class DesktopLifecycleModule extends LifecycleModule { public class DesktopLifecycleModule extends LifecycleModule {
@Provides
@Singleton
LifecycleManager provideLifecycleManager(Clock clock, DatabaseComponent db,
EventBus eventBus) {
return new LifecycleManagerImpl(clock, db, eventBus);
}
@Provides @Provides
@Singleton @Singleton
ShutdownManager provideDesktopShutdownManager() { ShutdownManager provideDesktopShutdownManager() {
if (OsUtils.isWindows()) { if (OsUtils.isWindows()) return new WindowsShutdownManagerImpl();
return new WindowsShutdownManagerImpl(); else return new ShutdownManagerImpl();
}
else {
return new ShutdownManagerImpl();
}
} }
} }

View File

@@ -3,9 +3,8 @@ package org.briarproject.plugins;
import org.briarproject.api.lifecycle.IoExecutor; import org.briarproject.api.lifecycle.IoExecutor;
import org.briarproject.api.lifecycle.ShutdownManager; import org.briarproject.api.lifecycle.ShutdownManager;
import org.briarproject.api.plugins.BackoffFactory; import org.briarproject.api.plugins.BackoffFactory;
import org.briarproject.api.plugins.duplex.DuplexPluginConfig; 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.SimplexPluginConfig;
import org.briarproject.api.plugins.simplex.SimplexPluginFactory; import org.briarproject.api.plugins.simplex.SimplexPluginFactory;
import org.briarproject.api.reliability.ReliabilityLayerFactory; import org.briarproject.api.reliability.ReliabilityLayerFactory;
import org.briarproject.plugins.bluetooth.BluetoothPluginFactory; import org.briarproject.plugins.bluetooth.BluetoothPluginFactory;
@@ -27,21 +26,7 @@ import dagger.Provides;
public class DesktopPluginsModule extends PluginsModule { public class DesktopPluginsModule extends PluginsModule {
@Provides @Provides
SimplexPluginConfig getSimplexPluginConfig( PluginConfig getPluginConfig(@IoExecutor Executor ioExecutor,
@IoExecutor Executor ioExecutor) {
SimplexPluginFactory removable =
new RemovableDrivePluginFactory(ioExecutor);
final Collection<SimplexPluginFactory> factories =
Collections.singletonList(removable);
return new SimplexPluginConfig() {
public Collection<SimplexPluginFactory> getFactories() {
return factories;
}
};
}
@Provides
DuplexPluginConfig getDuplexPluginConfig(@IoExecutor Executor ioExecutor,
SecureRandom random, BackoffFactory backoffFactory, SecureRandom random, BackoffFactory backoffFactory,
ReliabilityLayerFactory reliabilityFactory, ReliabilityLayerFactory reliabilityFactory,
ShutdownManager shutdownManager) { ShutdownManager shutdownManager) {
@@ -53,11 +38,22 @@ public class DesktopPluginsModule extends PluginsModule {
backoffFactory); backoffFactory);
DuplexPluginFactory wan = new WanTcpPluginFactory(ioExecutor, DuplexPluginFactory wan = new WanTcpPluginFactory(ioExecutor,
backoffFactory, shutdownManager); backoffFactory, shutdownManager);
final Collection<DuplexPluginFactory> factories = SimplexPluginFactory removable =
new RemovableDrivePluginFactory(ioExecutor);
final Collection<SimplexPluginFactory> simplex =
Collections.singletonList(removable);
final Collection<DuplexPluginFactory> duplex =
Arrays.asList(bluetooth, modem, lan, wan); Arrays.asList(bluetooth, modem, lan, wan);
return new DuplexPluginConfig() { return new PluginConfig() {
public Collection<DuplexPluginFactory> getFactories() {
return factories; @Override
public Collection<DuplexPluginFactory> getDuplexFactories() {
return duplex;
}
@Override
public Collection<SimplexPluginFactory> getSimplexFactories() {
return simplex;
} }
}; };
} }

View File

@@ -32,6 +32,10 @@ public class BluetoothPluginFactory implements DuplexPluginFactory {
return BluetoothPlugin.ID; return BluetoothPlugin.ID;
} }
public int getMaxLatency() {
return MAX_LATENCY;
}
public DuplexPlugin createPlugin(DuplexPluginCallback callback) { public DuplexPlugin createPlugin(DuplexPluginCallback callback) {
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL, Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE); MAX_POLLING_INTERVAL, BACKOFF_BASE);

View File

@@ -1,5 +1,10 @@
package org.briarproject.plugins.bluetooth; package org.briarproject.plugins.bluetooth;
import org.briarproject.api.plugins.Plugin;
import org.briarproject.api.plugins.TransportConnectionReader;
import org.briarproject.api.plugins.TransportConnectionWriter;
import org.briarproject.api.plugins.duplex.DuplexTransportConnection;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
@@ -7,11 +12,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
import javax.microedition.io.StreamConnection; import javax.microedition.io.StreamConnection;
import org.briarproject.api.plugins.Plugin;
import org.briarproject.api.plugins.TransportConnectionReader;
import org.briarproject.api.plugins.TransportConnectionWriter;
import org.briarproject.api.plugins.duplex.DuplexTransportConnection;
class BluetoothTransportConnection implements DuplexTransportConnection { class BluetoothTransportConnection implements DuplexTransportConnection {
private final Plugin plugin; private final Plugin plugin;
@@ -39,10 +39,6 @@ class BluetoothTransportConnection implements DuplexTransportConnection {
private class Reader implements TransportConnectionReader { private class Reader implements TransportConnectionReader {
public long getMaxLatency() {
return plugin.getMaxLatency();
}
public InputStream getInputStream() throws IOException { public InputStream getInputStream() throws IOException {
return stream.openInputStream(); return stream.openInputStream();
} }

View File

@@ -24,6 +24,10 @@ public class RemovableDrivePluginFactory implements SimplexPluginFactory {
return RemovableDrivePlugin.ID; return RemovableDrivePlugin.ID;
} }
public int getMaxLatency() {
return MAX_LATENCY;
}
public SimplexPlugin createPlugin(SimplexPluginCallback callback) { public SimplexPlugin createPlugin(SimplexPluginCallback callback) {
RemovableDriveFinder finder; RemovableDriveFinder finder;
RemovableDriveMonitor monitor; RemovableDriveMonitor monitor;

View File

@@ -194,10 +194,6 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback {
private class Reader implements TransportConnectionReader { private class Reader implements TransportConnectionReader {
public long getMaxLatency() {
return ModemPlugin.this.getMaxLatency();
}
public InputStream getInputStream() throws IOException { public InputStream getInputStream() throws IOException {
return modem.getInputStream(); return modem.getInputStream();
} }

View File

@@ -1,7 +1,5 @@
package org.briarproject.plugins.modem; package org.briarproject.plugins.modem;
import java.util.concurrent.Executor;
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.duplex.DuplexPluginCallback; import org.briarproject.api.plugins.duplex.DuplexPluginCallback;
@@ -9,6 +7,8 @@ import org.briarproject.api.plugins.duplex.DuplexPluginFactory;
import org.briarproject.api.reliability.ReliabilityLayerFactory; import org.briarproject.api.reliability.ReliabilityLayerFactory;
import org.briarproject.util.StringUtils; import org.briarproject.util.StringUtils;
import java.util.concurrent.Executor;
public class ModemPluginFactory implements DuplexPluginFactory { public class ModemPluginFactory implements DuplexPluginFactory {
private static final int MAX_LATENCY = 30 * 1000; // 30 seconds private static final int MAX_LATENCY = 30 * 1000; // 30 seconds
@@ -26,6 +26,10 @@ public class ModemPluginFactory implements DuplexPluginFactory {
return ModemPlugin.ID; return ModemPlugin.ID;
} }
public int getMaxLatency() {
return MAX_LATENCY;
}
public DuplexPlugin createPlugin(DuplexPluginCallback callback) { public DuplexPlugin createPlugin(DuplexPluginCallback callback) {
// This plugin is not enabled by default // This plugin is not enabled by default
String enabled = callback.getSettings().get("enabled"); String enabled = callback.getSettings().get("enabled");

View File

@@ -0,0 +1,55 @@
package org.briarproject;
import org.briarproject.api.TransportId;
import org.briarproject.api.plugins.PluginConfig;
import org.briarproject.api.plugins.duplex.DuplexPluginFactory;
import org.briarproject.api.plugins.simplex.SimplexPlugin;
import org.briarproject.api.plugins.simplex.SimplexPluginCallback;
import org.briarproject.api.plugins.simplex.SimplexPluginFactory;
import java.util.Collection;
import java.util.Collections;
import dagger.Module;
import dagger.Provides;
@Module
public class TestPluginsModule {
public static final TransportId TRANSPORT_ID = new TransportId("id");
public static final int MAX_LATENCY = 2 * 60 * 1000; // 2 minutes
private final SimplexPluginFactory simplex = new SimplexPluginFactory() {
@Override
public TransportId getId() {
return TRANSPORT_ID;
}
@Override
public int getMaxLatency() {
return MAX_LATENCY;
}
@Override
public SimplexPlugin createPlugin(SimplexPluginCallback callback) {
return null;
}
};
@Provides
PluginConfig providePluginConfig() {
return new PluginConfig() {
@Override
public Collection<DuplexPluginFactory> getDuplexFactories() {
return Collections.emptyList();
}
@Override
public Collection<SimplexPluginFactory> getSimplexFactories() {
return Collections.singletonList(simplex);
}
};
}
}

View File

@@ -56,7 +56,7 @@ public class MessageQueueManagerImplTest extends BriarTestCase {
context.mock(QueueMessageFactory.class); context.mock(QueueMessageFactory.class);
final ValidationManager validationManager = final ValidationManager validationManager =
context.mock(ValidationManager.class); context.mock(ValidationManager.class);
final Transaction txn = new Transaction(null); final Transaction txn = new Transaction(null, false);
final byte[] body = new byte[123]; final byte[] body = new byte[123];
final Metadata groupMetadata = new Metadata(); final Metadata groupMetadata = new Metadata();
final Metadata messageMetadata = new Metadata(); final Metadata messageMetadata = new Metadata();
@@ -249,7 +249,7 @@ public class MessageQueueManagerImplTest extends BriarTestCase {
new AtomicReference<IncomingMessageHook>(); new AtomicReference<IncomingMessageHook>();
final IncomingQueueMessageHook incomingQueueMessageHook = final IncomingQueueMessageHook incomingQueueMessageHook =
context.mock(IncomingQueueMessageHook.class); context.mock(IncomingQueueMessageHook.class);
final Transaction txn = new Transaction(null); final Transaction txn = new Transaction(null, false);
final Metadata groupMetadata = new Metadata(); final Metadata groupMetadata = new Metadata();
final byte[] queueState = new byte[123]; final byte[] queueState = new byte[123];
groupMetadata.put(QUEUE_STATE_KEY, queueState); groupMetadata.put(QUEUE_STATE_KEY, queueState);
@@ -300,7 +300,7 @@ public class MessageQueueManagerImplTest extends BriarTestCase {
new AtomicReference<IncomingMessageHook>(); new AtomicReference<IncomingMessageHook>();
final IncomingQueueMessageHook incomingQueueMessageHook = final IncomingQueueMessageHook incomingQueueMessageHook =
context.mock(IncomingQueueMessageHook.class); context.mock(IncomingQueueMessageHook.class);
final Transaction txn = new Transaction(null); final Transaction txn = new Transaction(null, false);
final Metadata groupMetadata = new Metadata(); final Metadata groupMetadata = new Metadata();
final byte[] queueState = new byte[123]; final byte[] queueState = new byte[123];
groupMetadata.put(QUEUE_STATE_KEY, queueState); groupMetadata.put(QUEUE_STATE_KEY, queueState);
@@ -355,7 +355,7 @@ public class MessageQueueManagerImplTest extends BriarTestCase {
new AtomicReference<IncomingMessageHook>(); new AtomicReference<IncomingMessageHook>();
final IncomingQueueMessageHook incomingQueueMessageHook = final IncomingQueueMessageHook incomingQueueMessageHook =
context.mock(IncomingQueueMessageHook.class); context.mock(IncomingQueueMessageHook.class);
final Transaction txn = new Transaction(null); final Transaction txn = new Transaction(null, false);
final Metadata groupMetadata = new Metadata(); final Metadata groupMetadata = new Metadata();
final byte[] queueState = new byte[123]; final byte[] queueState = new byte[123];
groupMetadata.put(QUEUE_STATE_KEY, queueState); groupMetadata.put(QUEUE_STATE_KEY, queueState);
@@ -412,7 +412,7 @@ public class MessageQueueManagerImplTest extends BriarTestCase {
new AtomicReference<IncomingMessageHook>(); new AtomicReference<IncomingMessageHook>();
final IncomingQueueMessageHook incomingQueueMessageHook = final IncomingQueueMessageHook incomingQueueMessageHook =
context.mock(IncomingQueueMessageHook.class); context.mock(IncomingQueueMessageHook.class);
final Transaction txn = new Transaction(null); final Transaction txn = new Transaction(null, false);
final Metadata groupMetadata = new Metadata(); final Metadata groupMetadata = new Metadata();
final byte[] queueState = new byte[123]; final byte[] queueState = new byte[123];
groupMetadata.put(QUEUE_STATE_KEY, queueState); groupMetadata.put(QUEUE_STATE_KEY, queueState);

View File

@@ -192,7 +192,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
shutdown); shutdown);
assertFalse(db.open()); assertFalse(db.open());
Transaction transaction = db.startTransaction(); Transaction transaction = db.startTransaction(false);
try { try {
db.addLocalAuthor(transaction, localAuthor); db.addLocalAuthor(transaction, localAuthor);
assertEquals(contactId, assertEquals(contactId,
@@ -233,7 +233,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown); shutdown);
Transaction transaction = db.startTransaction(); Transaction transaction = db.startTransaction(false);
try { try {
db.addLocalMessage(transaction, message, clientId, metadata, true); db.addLocalMessage(transaction, message, clientId, metadata, true);
fail(); fail();
@@ -276,7 +276,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown); shutdown);
Transaction transaction = db.startTransaction(); Transaction transaction = db.startTransaction(false);
try { try {
db.addLocalMessage(transaction, message, clientId, metadata, true); db.addLocalMessage(transaction, message, clientId, metadata, true);
transaction.setComplete(); transaction.setComplete();
@@ -306,7 +306,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown); shutdown);
Transaction transaction = db.startTransaction(); Transaction transaction = db.startTransaction(false);
try { try {
db.addTransportKeys(transaction, contactId, createTransportKeys()); db.addTransportKeys(transaction, contactId, createTransportKeys());
fail(); fail();
@@ -316,7 +316,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction); db.endTransaction(transaction);
} }
transaction = db.startTransaction(); transaction = db.startTransaction(false);
try { try {
db.generateAck(transaction, contactId, 123); db.generateAck(transaction, contactId, 123);
fail(); fail();
@@ -326,7 +326,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction); db.endTransaction(transaction);
} }
transaction = db.startTransaction(); transaction = db.startTransaction(false);
try { try {
db.generateBatch(transaction, contactId, 123, 456); db.generateBatch(transaction, contactId, 123, 456);
fail(); fail();
@@ -336,7 +336,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction); db.endTransaction(transaction);
} }
transaction = db.startTransaction(); transaction = db.startTransaction(false);
try { try {
db.generateOffer(transaction, contactId, 123, 456); db.generateOffer(transaction, contactId, 123, 456);
fail(); fail();
@@ -346,7 +346,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction); db.endTransaction(transaction);
} }
transaction = db.startTransaction(); transaction = db.startTransaction(false);
try { try {
db.generateRequest(transaction, contactId, 123); db.generateRequest(transaction, contactId, 123);
fail(); fail();
@@ -356,7 +356,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction); db.endTransaction(transaction);
} }
transaction = db.startTransaction(); transaction = db.startTransaction(false);
try { try {
db.getContact(transaction, contactId); db.getContact(transaction, contactId);
fail(); fail();
@@ -366,7 +366,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction); db.endTransaction(transaction);
} }
transaction = db.startTransaction(); transaction = db.startTransaction(false);
try { try {
db.getMessageStatus(transaction, contactId, groupId); db.getMessageStatus(transaction, contactId, groupId);
fail(); fail();
@@ -376,7 +376,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction); db.endTransaction(transaction);
} }
transaction = db.startTransaction(); transaction = db.startTransaction(false);
try { try {
db.getMessageStatus(transaction, contactId, messageId); db.getMessageStatus(transaction, contactId, messageId);
fail(); fail();
@@ -386,7 +386,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction); db.endTransaction(transaction);
} }
transaction = db.startTransaction(); transaction = db.startTransaction(false);
try { try {
db.incrementStreamCounter(transaction, contactId, transportId, 0); db.incrementStreamCounter(transaction, contactId, transportId, 0);
fail(); fail();
@@ -396,7 +396,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction); db.endTransaction(transaction);
} }
transaction = db.startTransaction(); transaction = db.startTransaction(false);
try { try {
db.isVisibleToContact(transaction, contactId, groupId); db.isVisibleToContact(transaction, contactId, groupId);
fail(); fail();
@@ -406,7 +406,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction); db.endTransaction(transaction);
} }
transaction = db.startTransaction(); transaction = db.startTransaction(false);
try { try {
Ack a = new Ack(Collections.singletonList(messageId)); Ack a = new Ack(Collections.singletonList(messageId));
db.receiveAck(transaction, contactId, a); db.receiveAck(transaction, contactId, a);
@@ -417,7 +417,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction); db.endTransaction(transaction);
} }
transaction = db.startTransaction(); transaction = db.startTransaction(false);
try { try {
db.receiveMessage(transaction, contactId, message); db.receiveMessage(transaction, contactId, message);
fail(); fail();
@@ -427,7 +427,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction); db.endTransaction(transaction);
} }
transaction = db.startTransaction(); transaction = db.startTransaction(false);
try { try {
Offer o = new Offer(Collections.singletonList(messageId)); Offer o = new Offer(Collections.singletonList(messageId));
db.receiveOffer(transaction, contactId, o); db.receiveOffer(transaction, contactId, o);
@@ -438,7 +438,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction); db.endTransaction(transaction);
} }
transaction = db.startTransaction(); transaction = db.startTransaction(false);
try { try {
Request r = new Request(Collections.singletonList(messageId)); Request r = new Request(Collections.singletonList(messageId));
db.receiveRequest(transaction, contactId, r); db.receiveRequest(transaction, contactId, r);
@@ -449,7 +449,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction); db.endTransaction(transaction);
} }
transaction = db.startTransaction(); transaction = db.startTransaction(false);
try { try {
db.removeContact(transaction, contactId); db.removeContact(transaction, contactId);
fail(); fail();
@@ -459,7 +459,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction); db.endTransaction(transaction);
} }
transaction = db.startTransaction(); transaction = db.startTransaction(false);
try { try {
db.setContactActive(transaction, contactId, true); db.setContactActive(transaction, contactId, true);
fail(); fail();
@@ -469,7 +469,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction); db.endTransaction(transaction);
} }
transaction = db.startTransaction(); transaction = db.startTransaction(false);
try { try {
db.setReorderingWindow(transaction, contactId, transportId, 0, 0, db.setReorderingWindow(transaction, contactId, transportId, 0, 0,
new byte[REORDERING_WINDOW_SIZE / 8]); new byte[REORDERING_WINDOW_SIZE / 8]);
@@ -480,7 +480,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction); db.endTransaction(transaction);
} }
transaction = db.startTransaction(); transaction = db.startTransaction(false);
try { try {
db.setVisibleToContact(transaction, contactId, groupId, true); db.setVisibleToContact(transaction, contactId, groupId, true);
fail(); fail();
@@ -512,7 +512,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown); shutdown);
Transaction transaction = db.startTransaction(); Transaction transaction = db.startTransaction(false);
try { try {
db.addContact(transaction, author, localAuthorId, true); db.addContact(transaction, author, localAuthorId, true);
fail(); fail();
@@ -522,7 +522,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction); db.endTransaction(transaction);
} }
transaction = db.startTransaction(); transaction = db.startTransaction(false);
try { try {
db.getLocalAuthor(transaction, localAuthorId); db.getLocalAuthor(transaction, localAuthorId);
fail(); fail();
@@ -532,7 +532,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction); db.endTransaction(transaction);
} }
transaction = db.startTransaction(); transaction = db.startTransaction(false);
try { try {
db.removeLocalAuthor(transaction, localAuthorId); db.removeLocalAuthor(transaction, localAuthorId);
fail(); fail();
@@ -568,7 +568,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown); shutdown);
Transaction transaction = db.startTransaction(); Transaction transaction = db.startTransaction(false);
try { try {
db.getGroup(transaction, groupId); db.getGroup(transaction, groupId);
fail(); fail();
@@ -578,7 +578,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction); db.endTransaction(transaction);
} }
transaction = db.startTransaction(); transaction = db.startTransaction(false);
try { try {
db.getGroupMetadata(transaction, groupId); db.getGroupMetadata(transaction, groupId);
fail(); fail();
@@ -588,7 +588,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction); db.endTransaction(transaction);
} }
transaction = db.startTransaction(); transaction = db.startTransaction(false);
try { try {
db.getMessageStatus(transaction, contactId, groupId); db.getMessageStatus(transaction, contactId, groupId);
fail(); fail();
@@ -598,7 +598,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction); db.endTransaction(transaction);
} }
transaction = db.startTransaction(); transaction = db.startTransaction(false);
try { try {
db.isVisibleToContact(transaction, contactId, groupId); db.isVisibleToContact(transaction, contactId, groupId);
fail(); fail();
@@ -608,7 +608,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction); db.endTransaction(transaction);
} }
transaction = db.startTransaction(); transaction = db.startTransaction(false);
try { try {
db.mergeGroupMetadata(transaction, groupId, metadata); db.mergeGroupMetadata(transaction, groupId, metadata);
fail(); fail();
@@ -618,7 +618,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction); db.endTransaction(transaction);
} }
transaction = db.startTransaction(); transaction = db.startTransaction(false);
try { try {
db.removeGroup(transaction, group); db.removeGroup(transaction, group);
fail(); fail();
@@ -628,7 +628,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction); db.endTransaction(transaction);
} }
transaction = db.startTransaction(); transaction = db.startTransaction(false);
try { try {
db.setVisibleToContact(transaction, contactId, groupId, true); db.setVisibleToContact(transaction, contactId, groupId, true);
fail(); fail();
@@ -663,7 +663,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown); shutdown);
Transaction transaction = db.startTransaction(); Transaction transaction = db.startTransaction(false);
try { try {
db.deleteMessage(transaction, messageId); db.deleteMessage(transaction, messageId);
fail(); fail();
@@ -673,7 +673,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction); db.endTransaction(transaction);
} }
transaction = db.startTransaction(); transaction = db.startTransaction(false);
try { try {
db.deleteMessageMetadata(transaction, messageId); db.deleteMessageMetadata(transaction, messageId);
fail(); fail();
@@ -683,7 +683,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction); db.endTransaction(transaction);
} }
transaction = db.startTransaction(); transaction = db.startTransaction(false);
try { try {
db.getRawMessage(transaction, messageId); db.getRawMessage(transaction, messageId);
fail(); fail();
@@ -693,7 +693,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction); db.endTransaction(transaction);
} }
transaction = db.startTransaction(); transaction = db.startTransaction(false);
try { try {
db.getMessageMetadata(transaction, messageId); db.getMessageMetadata(transaction, messageId);
fail(); fail();
@@ -703,7 +703,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction); db.endTransaction(transaction);
} }
transaction = db.startTransaction(); transaction = db.startTransaction(false);
try { try {
db.getMessageStatus(transaction, contactId, messageId); db.getMessageStatus(transaction, contactId, messageId);
fail(); fail();
@@ -713,7 +713,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction); db.endTransaction(transaction);
} }
transaction = db.startTransaction(); transaction = db.startTransaction(false);
try { try {
db.mergeMessageMetadata(transaction, messageId, metadata); db.mergeMessageMetadata(transaction, messageId, metadata);
fail(); fail();
@@ -723,7 +723,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction); db.endTransaction(transaction);
} }
transaction = db.startTransaction(); transaction = db.startTransaction(false);
try { try {
db.setMessageShared(transaction, message, true); db.setMessageShared(transaction, message, true);
fail(); fail();
@@ -733,7 +733,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction); db.endTransaction(transaction);
} }
transaction = db.startTransaction(); transaction = db.startTransaction(false);
try { try {
db.setMessageValid(transaction, message, clientId, true); db.setMessageValid(transaction, message, clientId, true);
fail(); fail();
@@ -787,7 +787,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown); shutdown);
Transaction transaction = db.startTransaction(); Transaction transaction = db.startTransaction(false);
try { try {
db.addLocalAuthor(transaction, localAuthor); db.addLocalAuthor(transaction, localAuthor);
assertEquals(contactId, assertEquals(contactId,
@@ -797,7 +797,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction); db.endTransaction(transaction);
} }
transaction = db.startTransaction(); transaction = db.startTransaction(false);
try { try {
db.getTransportKeys(transaction, transportId); db.getTransportKeys(transaction, transportId);
fail(); fail();
@@ -807,7 +807,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction); db.endTransaction(transaction);
} }
transaction = db.startTransaction(); transaction = db.startTransaction(false);
try { try {
db.incrementStreamCounter(transaction, contactId, transportId, 0); db.incrementStreamCounter(transaction, contactId, transportId, 0);
fail(); fail();
@@ -817,7 +817,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction); db.endTransaction(transaction);
} }
transaction = db.startTransaction(); transaction = db.startTransaction(false);
try { try {
db.removeTransport(transaction, transportId); db.removeTransport(transaction, transportId);
fail(); fail();
@@ -827,7 +827,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction); db.endTransaction(transaction);
} }
transaction = db.startTransaction(); transaction = db.startTransaction(false);
try { try {
db.setReorderingWindow(transaction, contactId, transportId, 0, 0, db.setReorderingWindow(transaction, contactId, transportId, 0, 0,
new byte[REORDERING_WINDOW_SIZE / 8]); new byte[REORDERING_WINDOW_SIZE / 8]);
@@ -863,7 +863,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown); shutdown);
Transaction transaction = db.startTransaction(); Transaction transaction = db.startTransaction(false);
try { try {
Ack a = db.generateAck(transaction, contactId, 123); Ack a = db.generateAck(transaction, contactId, 123);
assertEquals(messagesToAck, a.getMessageIds()); assertEquals(messagesToAck, a.getMessageIds());
@@ -907,7 +907,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown); shutdown);
Transaction transaction = db.startTransaction(); Transaction transaction = db.startTransaction(false);
try { try {
assertEquals(messages, db.generateBatch(transaction, contactId, assertEquals(messages, db.generateBatch(transaction, contactId,
size * 2, maxLatency)); size * 2, maxLatency));
@@ -944,7 +944,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown); shutdown);
Transaction transaction = db.startTransaction(); Transaction transaction = db.startTransaction(false);
try { try {
Offer o = db.generateOffer(transaction, contactId, 123, maxLatency); Offer o = db.generateOffer(transaction, contactId, 123, maxLatency);
assertEquals(ids, o.getMessageIds()); assertEquals(ids, o.getMessageIds());
@@ -978,7 +978,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown); shutdown);
Transaction transaction = db.startTransaction(); Transaction transaction = db.startTransaction(false);
try { try {
Request r = db.generateRequest(transaction, contactId, 123); Request r = db.generateRequest(transaction, contactId, 123);
assertEquals(ids, r.getMessageIds()); assertEquals(ids, r.getMessageIds());
@@ -1023,7 +1023,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown); shutdown);
Transaction transaction = db.startTransaction(); Transaction transaction = db.startTransaction(false);
try { try {
assertEquals(messages, db.generateRequestedBatch(transaction, assertEquals(messages, db.generateRequestedBatch(transaction,
contactId, size * 2, maxLatency)); contactId, size * 2, maxLatency));
@@ -1056,7 +1056,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown); shutdown);
Transaction transaction = db.startTransaction(); Transaction transaction = db.startTransaction(false);
try { try {
Ack a = new Ack(Collections.singletonList(messageId)); Ack a = new Ack(Collections.singletonList(messageId));
db.receiveAck(transaction, contactId, a); db.receiveAck(transaction, contactId, a);
@@ -1099,7 +1099,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown); shutdown);
Transaction transaction = db.startTransaction(); Transaction transaction = db.startTransaction(false);
try { try {
db.receiveMessage(transaction, contactId, message); db.receiveMessage(transaction, contactId, message);
transaction.setComplete(); transaction.setComplete();
@@ -1135,7 +1135,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown); shutdown);
Transaction transaction = db.startTransaction(); Transaction transaction = db.startTransaction(false);
try { try {
db.receiveMessage(transaction, contactId, message); db.receiveMessage(transaction, contactId, message);
transaction.setComplete(); transaction.setComplete();
@@ -1165,7 +1165,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown); shutdown);
Transaction transaction = db.startTransaction(); Transaction transaction = db.startTransaction(false);
try { try {
db.receiveMessage(transaction, contactId, message); db.receiveMessage(transaction, contactId, message);
transaction.setComplete(); transaction.setComplete();
@@ -1217,7 +1217,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown); shutdown);
Transaction transaction = db.startTransaction(); Transaction transaction = db.startTransaction(false);
try { try {
Offer o = new Offer(Arrays.asList(messageId, messageId1, Offer o = new Offer(Arrays.asList(messageId, messageId1,
messageId2, messageId3)); messageId2, messageId3));
@@ -1252,7 +1252,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown); shutdown);
Transaction transaction = db.startTransaction(); Transaction transaction = db.startTransaction(false);
try { try {
Request r = new Request(Collections.singletonList(messageId)); Request r = new Request(Collections.singletonList(messageId));
db.receiveRequest(transaction, contactId, r); db.receiveRequest(transaction, contactId, r);
@@ -1293,7 +1293,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown); shutdown);
Transaction transaction = db.startTransaction(); Transaction transaction = db.startTransaction(false);
try { try {
db.setVisibleToContact(transaction, contactId, groupId, true); db.setVisibleToContact(transaction, contactId, groupId, true);
transaction.setComplete(); transaction.setComplete();
@@ -1326,7 +1326,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown); shutdown);
Transaction transaction = db.startTransaction(); Transaction transaction = db.startTransaction(false);
try { try {
db.setVisibleToContact(transaction, contactId, groupId, true); db.setVisibleToContact(transaction, contactId, groupId, true);
transaction.setComplete(); transaction.setComplete();
@@ -1368,7 +1368,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown); shutdown);
Transaction transaction = db.startTransaction(); Transaction transaction = db.startTransaction(false);
try { try {
db.updateTransportKeys(transaction, keys); db.updateTransportKeys(transaction, keys);
assertEquals(keys, db.getTransportKeys(transaction, transportId)); assertEquals(keys, db.getTransportKeys(transaction, transportId));
@@ -1434,7 +1434,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown); shutdown);
Transaction transaction = db.startTransaction(); Transaction transaction = db.startTransaction(false);
try { try {
// First merge should broadcast an event // First merge should broadcast an event
db.mergeSettings(transaction, update, "namespace"); db.mergeSettings(transaction, update, "namespace");

View File

@@ -0,0 +1,277 @@
package org.briarproject.db;
import org.briarproject.BriarTestCase;
import org.briarproject.TestUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.File;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class TransactionIsolationTest extends BriarTestCase {
private static final String DROP_TABLE = "DROP TABLE foo IF EXISTS";
private static final String CREATE_TABLE = "CREATE TABLE foo"
+ " (key INT NOT NULL,"
+ " counter INT NOT NULL)";
private static final String INSERT_ROW =
"INSERT INTO foo (key, counter) VALUES (1, 123)";
private static final String GET_COUNTER =
"SELECT counter FROM foo WHERE key = 1";
private static final String SET_COUNTER =
"UPDATE foo SET counter = ? WHERE key = 1";
private final File testDir = TestUtils.getTestDirectory();
private final File db = new File(testDir, "db");
private final String withMvcc = "jdbc:h2:" + db.getAbsolutePath()
+ ";MV_STORE=TRUE;MVCC=TRUE";
private final String withoutMvcc = "jdbc:h2:" + db.getAbsolutePath()
+ ";MV_STORE=FALSE;MVCC=FALSE;LOCK_MODE=1";
@Before
public void setUp() throws Exception {
assertTrue(testDir.mkdirs());
Class.forName("org.h2.Driver");
}
@After
public void tearDown() throws Exception {
TestUtils.deleteTestDirectory(testDir);
}
@Test
public void testDoesNotReadUncommittedWritesWithMvcc() throws Exception {
Connection connection = openConnection(true);
try {
createTableAndInsertRow(connection);
} finally {
connection.close();
}
// Start the first transaction
Connection txn1 = openConnection(true);
try {
txn1.setAutoCommit(false);
// The first transaction should read the initial value
assertEquals(123, getCounter(txn1));
// The first transaction updates the value but doesn't commit it
assertEquals(1, setCounter(txn1, 234));
// Start the second transaction
Connection txn2 = openConnection(true);
try {
txn2.setAutoCommit(false);
// The second transaction should still read the initial value
assertEquals(123, getCounter(txn2));
// Commit the second transaction
txn2.commit();
} finally {
txn2.close();
}
// Commit the first transaction
txn1.commit();
} finally {
txn1.close();
}
}
@Test
public void testLastWriterWinsWithMvcc() throws Exception {
Connection connection = openConnection(true);
try {
createTableAndInsertRow(connection);
} finally {
connection.close();
}
// Start the first transaction
Connection txn1 = openConnection(true);
try {
txn1.setAutoCommit(false);
// The first transaction should read the initial value
assertEquals(123, getCounter(txn1));
// The first transaction updates the value but doesn't commit it
assertEquals(1, setCounter(txn1, 234));
// Start the second transaction
Connection txn2 = openConnection(true);
try {
txn2.setAutoCommit(false);
// The second transaction should still read the initial value
assertEquals(123, getCounter(txn2));
// Commit the first transaction
txn1.commit();
// The second transaction updates the value
assertEquals(1, setCounter(txn2, 345));
// Commit the second transaction
txn2.commit();
} finally {
txn2.close();
}
} finally {
txn1.close();
}
// The second transaction was the last writer, so it should win
connection = openConnection(true);
try {
assertEquals(345, getCounter(connection));
} finally {
connection.close();
}
}
@Test
public void testLockTimeoutOnRowWithMvcc() throws Exception {
Connection connection = openConnection(true);
try {
createTableAndInsertRow(connection);
} finally {
connection.close();
}
// Start the first transaction
Connection txn1 = openConnection(true);
try {
txn1.setAutoCommit(false);
// The first transaction should read the initial value
assertEquals(123, getCounter(txn1));
// Start the second transaction
Connection txn2 = openConnection(true);
try {
txn2.setAutoCommit(false);
// The second transaction should read the initial value
assertEquals(123, getCounter(txn2));
// The first transaction updates the value but doesn't commit it
assertEquals(1, setCounter(txn1, 234));
// The second transaction tries to update the value
try {
setCounter(txn2, 345);
fail();
} catch (SQLException expected) {
// Expected: the row is locked by the first transaction
}
// Abort the transactions
txn1.rollback();
txn2.rollback();
} finally {
txn2.close();
}
} finally {
txn1.close();
}
}
@Test
public void testReadLockTimeoutOnTableWithoutMvcc() throws Exception {
Connection connection = openConnection(false);
try {
createTableAndInsertRow(connection);
} finally {
connection.close();
}
// Start the first transaction
Connection txn1 = openConnection(false);
try {
txn1.setAutoCommit(false);
// The first transaction should read the initial value
assertEquals(123, getCounter(txn1));
// Start the second transaction
Connection txn2 = openConnection(false);
try {
txn2.setAutoCommit(false);
// The second transaction should read the initial value
assertEquals(123, getCounter(txn2));
// The first transaction tries to update the value
try {
setCounter(txn1, 345);
fail();
} catch (SQLException expected) {
// Expected: the table is locked by the second transaction
}
// Abort the transactions
txn1.rollback();
txn2.rollback();
} finally {
txn2.close();
}
} finally {
txn1.close();
}
}
@Test
public void testWriteLockTimeoutOnTableWithoutMvcc() throws Exception {
Connection connection = openConnection(false);
try {
createTableAndInsertRow(connection);
} finally {
connection.close();
}
// Start the first transaction
Connection txn1 = openConnection(false);
try {
txn1.setAutoCommit(false);
// The first transaction should read the initial value
assertEquals(123, getCounter(txn1));
// The first transaction updates the value but doesn't commit yet
assertEquals(1, setCounter(txn1, 345));
// Start the second transaction
Connection txn2 = openConnection(false);
try {
txn2.setAutoCommit(false);
// The second transaction tries to read the value
try {
getCounter(txn2);
fail();
} catch (SQLException expected) {
// Expected: the table is locked by the first transaction
}
// Abort the transactions
txn1.rollback();
txn2.rollback();
} finally {
txn2.close();
}
} finally {
txn1.close();
}
}
private Connection openConnection(boolean mvcc) throws SQLException {
return DriverManager.getConnection(mvcc ? withMvcc : withoutMvcc);
}
private void createTableAndInsertRow(Connection c) throws SQLException {
Statement s = c.createStatement();
s.executeUpdate(DROP_TABLE);
s.executeUpdate(CREATE_TABLE);
s.executeUpdate(INSERT_ROW);
s.close();
}
private int getCounter(Connection c) throws SQLException {
Statement s = c.createStatement();
ResultSet rs = s.executeQuery(GET_COUNTER);
assertTrue(rs.next());
int counter = rs.getInt(1);
assertFalse(rs.next());
rs.close();
s.close();
return counter;
}
private int setCounter(Connection c, int counter)
throws SQLException {
PreparedStatement ps = c.prepareStatement(SET_COUNTER);
ps.setInt(1, counter);
int rowsAffected = ps.executeUpdate();
ps.close();
return rowsAffected;
}
}

View File

@@ -2,23 +2,18 @@ package org.briarproject.plugins;
import org.briarproject.BriarTestCase; import org.briarproject.BriarTestCase;
import org.briarproject.api.TransportId; import org.briarproject.api.TransportId;
import org.briarproject.api.db.DatabaseComponent;
import org.briarproject.api.db.Transaction;
import org.briarproject.api.event.EventBus; import org.briarproject.api.event.EventBus;
import org.briarproject.api.plugins.ConnectionManager; import org.briarproject.api.plugins.ConnectionManager;
import org.briarproject.api.plugins.PluginConfig;
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.DuplexPluginConfig;
import org.briarproject.api.plugins.duplex.DuplexPluginFactory; import org.briarproject.api.plugins.duplex.DuplexPluginFactory;
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.SimplexPluginConfig;
import org.briarproject.api.plugins.simplex.SimplexPluginFactory; import org.briarproject.api.plugins.simplex.SimplexPluginFactory;
import org.briarproject.api.properties.TransportPropertyManager; import org.briarproject.api.properties.TransportPropertyManager;
import org.briarproject.api.settings.SettingsManager; import org.briarproject.api.settings.SettingsManager;
import org.briarproject.api.system.Clock;
import org.briarproject.api.ui.UiCallback; import org.briarproject.api.ui.UiCallback;
import org.briarproject.system.SystemClock;
import org.jmock.Expectations; import org.jmock.Expectations;
import org.jmock.Mockery; import org.jmock.Mockery;
import org.jmock.lib.concurrent.Synchroniser; import org.jmock.lib.concurrent.Synchroniser;
@@ -34,17 +29,12 @@ public class PluginManagerImplTest extends BriarTestCase {
@Test @Test
public void testStartAndStop() throws Exception { public void testStartAndStop() throws Exception {
Clock clock = new SystemClock();
Mockery context = new Mockery() {{ Mockery context = new Mockery() {{
setThreadingPolicy(new Synchroniser()); setThreadingPolicy(new Synchroniser());
}}; }};
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 SimplexPluginConfig simplexPluginConfig = final PluginConfig pluginConfig = context.mock(PluginConfig.class);
context.mock(SimplexPluginConfig.class);
final DuplexPluginConfig duplexPluginConfig =
context.mock(DuplexPluginConfig.class);
final DatabaseComponent db = context.mock(DatabaseComponent.class);
final Poller poller = context.mock(Poller.class); final Poller poller = context.mock(Poller.class);
final ConnectionManager connectionManager = final ConnectionManager connectionManager =
context.mock(ConnectionManager.class); context.mock(ConnectionManager.class);
@@ -53,33 +43,30 @@ public class PluginManagerImplTest extends BriarTestCase {
final TransportPropertyManager transportPropertyManager = final TransportPropertyManager transportPropertyManager =
context.mock(TransportPropertyManager.class); context.mock(TransportPropertyManager.class);
final UiCallback uiCallback = context.mock(UiCallback.class); final UiCallback uiCallback = context.mock(UiCallback.class);
// Two simplex plugin factories: both create plugins, one fails to start // Two simplex plugin factories: both create plugins, one fails to start
final SimplexPluginFactory simplexFactory = final SimplexPluginFactory simplexFactory =
context.mock(SimplexPluginFactory.class); context.mock(SimplexPluginFactory.class);
final SimplexPlugin simplexPlugin = context.mock(SimplexPlugin.class); final SimplexPlugin simplexPlugin = context.mock(SimplexPlugin.class);
final TransportId simplexId = new TransportId("simplex"); final TransportId simplexId = new TransportId("simplex");
final int simplexLatency = 12345;
final Transaction simplexTxn = new Transaction(null);
final SimplexPluginFactory simplexFailFactory = final SimplexPluginFactory simplexFailFactory =
context.mock(SimplexPluginFactory.class, "simplexFailFactory"); context.mock(SimplexPluginFactory.class, "simplexFailFactory");
final SimplexPlugin simplexFailPlugin = final SimplexPlugin simplexFailPlugin =
context.mock(SimplexPlugin.class, "simplexFailPlugin"); context.mock(SimplexPlugin.class, "simplexFailPlugin");
final TransportId simplexFailId = new TransportId("simplex1"); final TransportId simplexFailId = new TransportId("simplex1");
final int simplexFailLatency = 23456;
final Transaction simplexFailTxn = new Transaction(null);
// Two duplex plugin factories: one creates a plugin, the other fails // Two duplex plugin factories: one creates a plugin, the other fails
final DuplexPluginFactory duplexFactory = final DuplexPluginFactory duplexFactory =
context.mock(DuplexPluginFactory.class); context.mock(DuplexPluginFactory.class);
final DuplexPlugin duplexPlugin = context.mock(DuplexPlugin.class); final DuplexPlugin duplexPlugin = context.mock(DuplexPlugin.class);
final TransportId duplexId = new TransportId("duplex"); final TransportId duplexId = new TransportId("duplex");
final int duplexLatency = 34567;
final Transaction duplexTxn = new Transaction(null);
final DuplexPluginFactory duplexFailFactory = final DuplexPluginFactory duplexFailFactory =
context.mock(DuplexPluginFactory.class, "duplexFailFactory"); context.mock(DuplexPluginFactory.class, "duplexFailFactory");
final TransportId duplexFailId = new TransportId("duplex1"); final TransportId duplexFailId = new TransportId("duplex1");
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// First simplex plugin // First simplex plugin
oneOf(simplexPluginConfig).getFactories(); oneOf(pluginConfig).getSimplexFactories();
will(returnValue(Arrays.asList(simplexFactory, will(returnValue(Arrays.asList(simplexFactory,
simplexFailFactory))); simplexFailFactory)));
oneOf(simplexFactory).getId(); oneOf(simplexFactory).getId();
@@ -87,12 +74,6 @@ public class PluginManagerImplTest extends BriarTestCase {
oneOf(simplexFactory).createPlugin(with(any( oneOf(simplexFactory).createPlugin(with(any(
SimplexPluginCallback.class))); SimplexPluginCallback.class)));
will(returnValue(simplexPlugin)); // Created will(returnValue(simplexPlugin)); // Created
oneOf(simplexPlugin).getMaxLatency();
will(returnValue(simplexLatency));
oneOf(db).startTransaction();
will(returnValue(simplexTxn));
oneOf(db).addTransport(simplexTxn, simplexId, simplexLatency);
oneOf(db).endTransaction(simplexTxn);
oneOf(simplexPlugin).start(); oneOf(simplexPlugin).start();
will(returnValue(true)); // Started will(returnValue(true)); // Started
oneOf(simplexPlugin).shouldPoll(); oneOf(simplexPlugin).shouldPoll();
@@ -104,29 +85,16 @@ public class PluginManagerImplTest extends BriarTestCase {
oneOf(simplexFailFactory).createPlugin(with(any( oneOf(simplexFailFactory).createPlugin(with(any(
SimplexPluginCallback.class))); SimplexPluginCallback.class)));
will(returnValue(simplexFailPlugin)); // Created will(returnValue(simplexFailPlugin)); // Created
oneOf(simplexFailPlugin).getMaxLatency();
will(returnValue(simplexFailLatency));
oneOf(db).startTransaction();
will(returnValue(simplexFailTxn));
oneOf(db).addTransport(simplexFailTxn, simplexFailId,
simplexFailLatency);
oneOf(db).endTransaction(simplexFailTxn);
oneOf(simplexFailPlugin).start(); oneOf(simplexFailPlugin).start();
will(returnValue(false)); // Failed to start will(returnValue(false)); // Failed to start
// First duplex plugin // First duplex plugin
oneOf(duplexPluginConfig).getFactories(); oneOf(pluginConfig).getDuplexFactories();
will(returnValue(Arrays.asList(duplexFactory, duplexFailFactory))); will(returnValue(Arrays.asList(duplexFactory, duplexFailFactory)));
oneOf(duplexFactory).getId(); oneOf(duplexFactory).getId();
will(returnValue(duplexId)); will(returnValue(duplexId));
oneOf(duplexFactory).createPlugin(with(any( oneOf(duplexFactory).createPlugin(with(any(
DuplexPluginCallback.class))); DuplexPluginCallback.class)));
will(returnValue(duplexPlugin)); // Created will(returnValue(duplexPlugin)); // Created
oneOf(duplexPlugin).getMaxLatency();
will(returnValue(duplexLatency));
oneOf(db).startTransaction();
will(returnValue(duplexTxn));
oneOf(db).addTransport(duplexTxn, duplexId, duplexLatency);
oneOf(db).endTransaction(duplexTxn);
oneOf(duplexPlugin).start(); oneOf(duplexPlugin).start();
will(returnValue(true)); // Started will(returnValue(true)); // Started
oneOf(duplexPlugin).shouldPoll(); oneOf(duplexPlugin).shouldPoll();
@@ -143,14 +111,15 @@ public class PluginManagerImplTest extends BriarTestCase {
oneOf(simplexPlugin).stop(); oneOf(simplexPlugin).stop();
oneOf(duplexPlugin).stop(); oneOf(duplexPlugin).stop();
}}); }});
PluginManagerImpl p = new PluginManagerImpl(ioExecutor, eventBus, PluginManagerImpl p = new PluginManagerImpl(ioExecutor, eventBus,
simplexPluginConfig, duplexPluginConfig, clock, db, poller, pluginConfig, poller, connectionManager, settingsManager,
connectionManager, settingsManager, transportPropertyManager, transportPropertyManager, uiCallback);
uiCallback);
// Two plugins should be started and stopped // Two plugins should be started and stopped
assertTrue(p.start()); assertTrue(p.start());
assertTrue(p.stop()); assertTrue(p.stop());
context.assertIsSatisfied(); context.assertIsSatisfied();
} }
} }

View File

@@ -3,7 +3,6 @@ package org.briarproject.sync;
import org.briarproject.BriarTestCase; import org.briarproject.BriarTestCase;
import org.briarproject.ImmediateExecutor; import org.briarproject.ImmediateExecutor;
import org.briarproject.TestUtils; import org.briarproject.TestUtils;
import org.briarproject.api.TransportId;
import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactId;
import org.briarproject.api.db.DatabaseComponent; import org.briarproject.api.db.DatabaseComponent;
import org.briarproject.api.db.Transaction; import org.briarproject.api.db.Transaction;
@@ -28,7 +27,6 @@ public class SimplexOutgoingSessionTest extends BriarTestCase {
private final Executor dbExecutor; private final Executor dbExecutor;
private final EventBus eventBus; private final EventBus eventBus;
private final ContactId contactId; private final ContactId contactId;
private final TransportId transportId;
private final MessageId messageId; private final MessageId messageId;
private final int maxLatency; private final int maxLatency;
private final PacketWriter packetWriter; private final PacketWriter packetWriter;
@@ -40,7 +38,6 @@ public class SimplexOutgoingSessionTest extends BriarTestCase {
eventBus = context.mock(EventBus.class); eventBus = context.mock(EventBus.class);
packetWriter = context.mock(PacketWriter.class); packetWriter = context.mock(PacketWriter.class);
contactId = new ContactId(234); contactId = new ContactId(234);
transportId = new TransportId("id");
messageId = new MessageId(TestUtils.getRandomId()); messageId = new MessageId(TestUtils.getRandomId());
maxLatency = Integer.MAX_VALUE; maxLatency = Integer.MAX_VALUE;
} }
@@ -48,21 +45,21 @@ public class SimplexOutgoingSessionTest extends BriarTestCase {
@Test @Test
public void testNothingToSend() throws Exception { public void testNothingToSend() throws Exception {
final SimplexOutgoingSession session = new SimplexOutgoingSession(db, final SimplexOutgoingSession session = new SimplexOutgoingSession(db,
dbExecutor, eventBus, contactId, transportId, maxLatency, dbExecutor, eventBus, contactId, maxLatency, packetWriter);
packetWriter); final Transaction noAckTxn = new Transaction(null, false);
final Transaction noAckTxn = new Transaction(null); final Transaction noMsgTxn = new Transaction(null, false);
final Transaction noMsgTxn = new Transaction(null);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// Add listener // Add listener
oneOf(eventBus).addListener(session); oneOf(eventBus).addListener(session);
// No acks to send // No acks to send
oneOf(db).startTransaction(); oneOf(db).startTransaction(false);
will(returnValue(noAckTxn)); will(returnValue(noAckTxn));
oneOf(db).generateAck(noAckTxn, contactId, MAX_MESSAGE_IDS); oneOf(db).generateAck(noAckTxn, contactId, MAX_MESSAGE_IDS);
will(returnValue(null)); will(returnValue(null));
oneOf(db).endTransaction(noAckTxn); oneOf(db).endTransaction(noAckTxn);
// No messages to send // No messages to send
oneOf(db).startTransaction(); oneOf(db).startTransaction(false);
will(returnValue(noMsgTxn)); will(returnValue(noMsgTxn));
oneOf(db).generateBatch(with(noMsgTxn), with(contactId), oneOf(db).generateBatch(with(noMsgTxn), with(contactId),
with(any(int.class)), with(maxLatency)); with(any(int.class)), with(maxLatency));
@@ -73,7 +70,9 @@ public class SimplexOutgoingSessionTest extends BriarTestCase {
// Remove listener // Remove listener
oneOf(eventBus).removeListener(session); oneOf(eventBus).removeListener(session);
}}); }});
session.run(); session.run();
context.assertIsSatisfied(); context.assertIsSatisfied();
} }
@@ -82,24 +81,24 @@ public class SimplexOutgoingSessionTest extends BriarTestCase {
final Ack ack = new Ack(Collections.singletonList(messageId)); final Ack ack = new Ack(Collections.singletonList(messageId));
final byte[] raw = new byte[1234]; final byte[] raw = new byte[1234];
final SimplexOutgoingSession session = new SimplexOutgoingSession(db, final SimplexOutgoingSession session = new SimplexOutgoingSession(db,
dbExecutor, eventBus, contactId, transportId, maxLatency, dbExecutor, eventBus, contactId, maxLatency, packetWriter);
packetWriter); final Transaction ackTxn = new Transaction(null, false);
final Transaction ackTxn = new Transaction(null); final Transaction noAckTxn = new Transaction(null, false);
final Transaction noAckTxn = new Transaction(null); final Transaction msgTxn = new Transaction(null, false);
final Transaction msgTxn = new Transaction(null); final Transaction noMsgTxn = new Transaction(null, false);
final Transaction noMsgTxn = new Transaction(null);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// Add listener // Add listener
oneOf(eventBus).addListener(session); oneOf(eventBus).addListener(session);
// One ack to send // One ack to send
oneOf(db).startTransaction(); oneOf(db).startTransaction(false);
will(returnValue(ackTxn)); will(returnValue(ackTxn));
oneOf(db).generateAck(ackTxn, contactId, MAX_MESSAGE_IDS); oneOf(db).generateAck(ackTxn, contactId, MAX_MESSAGE_IDS);
will(returnValue(ack)); will(returnValue(ack));
oneOf(db).endTransaction(ackTxn); oneOf(db).endTransaction(ackTxn);
oneOf(packetWriter).writeAck(ack); oneOf(packetWriter).writeAck(ack);
// One message to send // One message to send
oneOf(db).startTransaction(); oneOf(db).startTransaction(false);
will(returnValue(msgTxn)); will(returnValue(msgTxn));
oneOf(db).generateBatch(with(msgTxn), with(contactId), oneOf(db).generateBatch(with(msgTxn), with(contactId),
with(any(int.class)), with(maxLatency)); with(any(int.class)), with(maxLatency));
@@ -107,13 +106,13 @@ public class SimplexOutgoingSessionTest extends BriarTestCase {
oneOf(db).endTransaction(msgTxn); oneOf(db).endTransaction(msgTxn);
oneOf(packetWriter).writeMessage(raw); oneOf(packetWriter).writeMessage(raw);
// No more acks // No more acks
oneOf(db).startTransaction(); oneOf(db).startTransaction(false);
will(returnValue(noAckTxn)); will(returnValue(noAckTxn));
oneOf(db).generateAck(noAckTxn, contactId, MAX_MESSAGE_IDS); oneOf(db).generateAck(noAckTxn, contactId, MAX_MESSAGE_IDS);
will(returnValue(null)); will(returnValue(null));
oneOf(db).endTransaction(noAckTxn); oneOf(db).endTransaction(noAckTxn);
// No more messages // No more messages
oneOf(db).startTransaction(); oneOf(db).startTransaction(false);
will(returnValue(noMsgTxn)); will(returnValue(noMsgTxn));
oneOf(db).generateBatch(with(noMsgTxn), with(contactId), oneOf(db).generateBatch(with(noMsgTxn), with(contactId),
with(any(int.class)), with(maxLatency)); with(any(int.class)), with(maxLatency));
@@ -124,7 +123,9 @@ public class SimplexOutgoingSessionTest extends BriarTestCase {
// Remove listener // Remove listener
oneOf(eventBus).removeListener(session); oneOf(eventBus).removeListener(session);
}}); }});
session.run(); session.run();
context.assertIsSatisfied(); context.assertIsSatisfied();
} }
} }

View File

@@ -58,20 +58,20 @@ public class ValidationManagerImplTest extends BriarTestCase {
final MessageValidator validator = context.mock(MessageValidator.class); final MessageValidator validator = context.mock(MessageValidator.class);
final IncomingMessageHook hook = final IncomingMessageHook hook =
context.mock(IncomingMessageHook.class); context.mock(IncomingMessageHook.class);
final Transaction txn = new Transaction(null); final Transaction txn = new Transaction(null, false);
final Transaction txn1 = new Transaction(null); final Transaction txn1 = new Transaction(null, false);
final Transaction txn2 = new Transaction(null); final Transaction txn2 = new Transaction(null, false);
final Transaction txn3 = new Transaction(null); final Transaction txn3 = new Transaction(null, false);
final Transaction txn4 = new Transaction(null); final Transaction txn4 = new Transaction(null, false);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// Get messages to validate // Get messages to validate
oneOf(db).startTransaction(); oneOf(db).startTransaction(true);
will(returnValue(txn)); will(returnValue(txn));
oneOf(db).getMessagesToValidate(txn, clientId); oneOf(db).getMessagesToValidate(txn, clientId);
will(returnValue(Arrays.asList(messageId, messageId1))); will(returnValue(Arrays.asList(messageId, messageId1)));
oneOf(db).endTransaction(txn); oneOf(db).endTransaction(txn);
// Load the first raw message and group // Load the first raw message and group
oneOf(db).startTransaction(); oneOf(db).startTransaction(true);
will(returnValue(txn1)); will(returnValue(txn1));
oneOf(db).getRawMessage(txn1, messageId); oneOf(db).getRawMessage(txn1, messageId);
will(returnValue(raw)); will(returnValue(raw));
@@ -82,7 +82,7 @@ public class ValidationManagerImplTest extends BriarTestCase {
oneOf(validator).validateMessage(message, group); oneOf(validator).validateMessage(message, group);
will(returnValue(metadata)); will(returnValue(metadata));
// Store the validation result for the first message // Store the validation result for the first message
oneOf(db).startTransaction(); oneOf(db).startTransaction(false);
will(returnValue(txn2)); will(returnValue(txn2));
oneOf(db).mergeMessageMetadata(txn2, messageId, metadata); oneOf(db).mergeMessageMetadata(txn2, messageId, metadata);
oneOf(db).setMessageValid(txn2, message, clientId, true); oneOf(db).setMessageValid(txn2, message, clientId, true);
@@ -91,7 +91,7 @@ public class ValidationManagerImplTest extends BriarTestCase {
oneOf(hook).incomingMessage(txn2, message, metadata); oneOf(hook).incomingMessage(txn2, message, metadata);
oneOf(db).endTransaction(txn2); oneOf(db).endTransaction(txn2);
// Load the second raw message and group // Load the second raw message and group
oneOf(db).startTransaction(); oneOf(db).startTransaction(true);
will(returnValue(txn3)); will(returnValue(txn3));
oneOf(db).getRawMessage(txn3, messageId1); oneOf(db).getRawMessage(txn3, messageId1);
will(returnValue(raw)); will(returnValue(raw));
@@ -102,7 +102,7 @@ public class ValidationManagerImplTest extends BriarTestCase {
oneOf(validator).validateMessage(message1, group); oneOf(validator).validateMessage(message1, group);
will(returnValue(null)); will(returnValue(null));
// Store the validation result for the second message // Store the validation result for the second message
oneOf(db).startTransaction(); oneOf(db).startTransaction(false);
will(returnValue(txn4)); will(returnValue(txn4));
oneOf(db).setMessageValid(txn4, message1, clientId, false); oneOf(db).setMessageValid(txn4, message1, clientId, false);
oneOf(db).endTransaction(txn4); oneOf(db).endTransaction(txn4);
@@ -127,25 +127,25 @@ public class ValidationManagerImplTest extends BriarTestCase {
final MessageValidator validator = context.mock(MessageValidator.class); final MessageValidator validator = context.mock(MessageValidator.class);
final IncomingMessageHook hook = final IncomingMessageHook hook =
context.mock(IncomingMessageHook.class); context.mock(IncomingMessageHook.class);
final Transaction txn = new Transaction(null); final Transaction txn = new Transaction(null, true);
final Transaction txn1 = new Transaction(null); final Transaction txn1 = new Transaction(null, true);
final Transaction txn2 = new Transaction(null); final Transaction txn2 = new Transaction(null, true);
final Transaction txn3 = new Transaction(null); final Transaction txn3 = new Transaction(null, false);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// Get messages to validate // Get messages to validate
oneOf(db).startTransaction(); oneOf(db).startTransaction(true);
will(returnValue(txn)); will(returnValue(txn));
oneOf(db).getMessagesToValidate(txn, clientId); oneOf(db).getMessagesToValidate(txn, clientId);
will(returnValue(Arrays.asList(messageId, messageId1))); will(returnValue(Arrays.asList(messageId, messageId1)));
oneOf(db).endTransaction(txn); oneOf(db).endTransaction(txn);
// Load the first raw message - *gasp* it's gone! // Load the first raw message - *gasp* it's gone!
oneOf(db).startTransaction(); oneOf(db).startTransaction(true);
will(returnValue(txn1)); will(returnValue(txn1));
oneOf(db).getRawMessage(txn1, messageId); oneOf(db).getRawMessage(txn1, messageId);
will(throwException(new NoSuchMessageException())); will(throwException(new NoSuchMessageException()));
oneOf(db).endTransaction(txn1); oneOf(db).endTransaction(txn1);
// Load the second raw message and group // Load the second raw message and group
oneOf(db).startTransaction(); oneOf(db).startTransaction(true);
will(returnValue(txn2)); will(returnValue(txn2));
oneOf(db).getRawMessage(txn2, messageId1); oneOf(db).getRawMessage(txn2, messageId1);
will(returnValue(raw)); will(returnValue(raw));
@@ -156,7 +156,7 @@ public class ValidationManagerImplTest extends BriarTestCase {
oneOf(validator).validateMessage(message1, group); oneOf(validator).validateMessage(message1, group);
will(returnValue(null)); will(returnValue(null));
// Store the validation result for the second message // Store the validation result for the second message
oneOf(db).startTransaction(); oneOf(db).startTransaction(false);
will(returnValue(txn3)); will(returnValue(txn3));
oneOf(db).setMessageValid(txn3, message1, clientId, false); oneOf(db).setMessageValid(txn3, message1, clientId, false);
oneOf(db).endTransaction(txn3); oneOf(db).endTransaction(txn3);
@@ -181,19 +181,19 @@ public class ValidationManagerImplTest extends BriarTestCase {
final MessageValidator validator = context.mock(MessageValidator.class); final MessageValidator validator = context.mock(MessageValidator.class);
final IncomingMessageHook hook = final IncomingMessageHook hook =
context.mock(IncomingMessageHook.class); context.mock(IncomingMessageHook.class);
final Transaction txn = new Transaction(null); final Transaction txn = new Transaction(null, true);
final Transaction txn1 = new Transaction(null); final Transaction txn1 = new Transaction(null, true);
final Transaction txn2 = new Transaction(null); final Transaction txn2 = new Transaction(null, true);
final Transaction txn3 = new Transaction(null); final Transaction txn3 = new Transaction(null, false);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// Get messages to validate // Get messages to validate
oneOf(db).startTransaction(); oneOf(db).startTransaction(true);
will(returnValue(txn)); will(returnValue(txn));
oneOf(db).getMessagesToValidate(txn, clientId); oneOf(db).getMessagesToValidate(txn, clientId);
will(returnValue(Arrays.asList(messageId, messageId1))); will(returnValue(Arrays.asList(messageId, messageId1)));
oneOf(db).endTransaction(txn); oneOf(db).endTransaction(txn);
// Load the first raw message // Load the first raw message
oneOf(db).startTransaction(); oneOf(db).startTransaction(true);
will(returnValue(txn1)); will(returnValue(txn1));
oneOf(db).getRawMessage(txn1, messageId); oneOf(db).getRawMessage(txn1, messageId);
will(returnValue(raw)); will(returnValue(raw));
@@ -202,7 +202,7 @@ public class ValidationManagerImplTest extends BriarTestCase {
will(throwException(new NoSuchGroupException())); will(throwException(new NoSuchGroupException()));
oneOf(db).endTransaction(txn1); oneOf(db).endTransaction(txn1);
// Load the second raw message and group // Load the second raw message and group
oneOf(db).startTransaction(); oneOf(db).startTransaction(true);
will(returnValue(txn2)); will(returnValue(txn2));
oneOf(db).getRawMessage(txn2, messageId1); oneOf(db).getRawMessage(txn2, messageId1);
will(returnValue(raw)); will(returnValue(raw));
@@ -213,7 +213,7 @@ public class ValidationManagerImplTest extends BriarTestCase {
oneOf(validator).validateMessage(message1, group); oneOf(validator).validateMessage(message1, group);
will(returnValue(null)); will(returnValue(null));
// Store the validation result for the second message // Store the validation result for the second message
oneOf(db).startTransaction(); oneOf(db).startTransaction(false);
will(returnValue(txn3)); will(returnValue(txn3));
oneOf(db).setMessageValid(txn3, message1, clientId, false); oneOf(db).setMessageValid(txn3, message1, clientId, false);
oneOf(db).endTransaction(txn3); oneOf(db).endTransaction(txn3);
@@ -237,11 +237,11 @@ public class ValidationManagerImplTest extends BriarTestCase {
final MessageValidator validator = context.mock(MessageValidator.class); final MessageValidator validator = context.mock(MessageValidator.class);
final IncomingMessageHook hook = final IncomingMessageHook hook =
context.mock(IncomingMessageHook.class); context.mock(IncomingMessageHook.class);
final Transaction txn = new Transaction(null); final Transaction txn = new Transaction(null, true);
final Transaction txn1 = new Transaction(null); final Transaction txn1 = new Transaction(null, false);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// Load the group // Load the group
oneOf(db).startTransaction(); oneOf(db).startTransaction(true);
will(returnValue(txn)); will(returnValue(txn));
oneOf(db).getGroup(txn, groupId); oneOf(db).getGroup(txn, groupId);
will(returnValue(group)); will(returnValue(group));
@@ -250,7 +250,7 @@ public class ValidationManagerImplTest extends BriarTestCase {
oneOf(validator).validateMessage(message, group); oneOf(validator).validateMessage(message, group);
will(returnValue(metadata)); will(returnValue(metadata));
// Store the validation result // Store the validation result
oneOf(db).startTransaction(); oneOf(db).startTransaction(false);
will(returnValue(txn1)); will(returnValue(txn1));
oneOf(db).mergeMessageMetadata(txn1, messageId, metadata); oneOf(db).mergeMessageMetadata(txn1, messageId, metadata);
oneOf(db).setMessageValid(txn1, message, clientId, true); oneOf(db).setMessageValid(txn1, message, clientId, true);

View File

@@ -37,6 +37,7 @@ import static org.briarproject.util.ByteUtils.MAX_32_BIT_UNSIGNED;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
public class TransportKeyManagerTest extends BriarTestCase { public class TransportKeyManagerTest extends BriarTestCase {
@@ -57,7 +58,7 @@ public class TransportKeyManagerTest extends BriarTestCase {
final CryptoComponent crypto = context.mock(CryptoComponent.class); final CryptoComponent crypto = context.mock(CryptoComponent.class);
final Timer timer = context.mock(Timer.class); final Timer timer = context.mock(Timer.class);
final Clock clock = context.mock(Clock.class); final Clock clock = context.mock(Clock.class);
final Transaction txn = new Transaction(null);
final Map<ContactId, TransportKeys> loaded = final Map<ContactId, TransportKeys> loaded =
new LinkedHashMap<ContactId, TransportKeys>(); new LinkedHashMap<ContactId, TransportKeys>();
final TransportKeys shouldRotate = createTransportKeys(900, 0); final TransportKeys shouldRotate = createTransportKeys(900, 0);
@@ -65,17 +66,15 @@ public class TransportKeyManagerTest extends BriarTestCase {
loaded.put(contactId, shouldRotate); loaded.put(contactId, shouldRotate);
loaded.put(contactId1, shouldNotRotate); loaded.put(contactId1, shouldNotRotate);
final TransportKeys rotated = createTransportKeys(1000, 0); final TransportKeys rotated = createTransportKeys(1000, 0);
final Transaction txn1 = new Transaction(null); final Transaction txn = new Transaction(null, false);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// Get the current time (1 ms after start of rotation period 1000) // Get the current time (1 ms after start of rotation period 1000)
oneOf(clock).currentTimeMillis(); oneOf(clock).currentTimeMillis();
will(returnValue(rotationPeriodLength * 1000 + 1)); will(returnValue(rotationPeriodLength * 1000 + 1));
// Load the transport keys // Load the transport keys
oneOf(db).startTransaction();
will(returnValue(txn));
oneOf(db).getTransportKeys(txn, transportId); oneOf(db).getTransportKeys(txn, transportId);
will(returnValue(loaded)); will(returnValue(loaded));
oneOf(db).endTransaction(txn);
// Rotate the transport keys // Rotate the transport keys
oneOf(crypto).rotateTransportKeys(shouldRotate, 1000); oneOf(crypto).rotateTransportKeys(shouldRotate, 1000);
will(returnValue(rotated)); will(returnValue(rotated));
@@ -88,11 +87,8 @@ public class TransportKeyManagerTest extends BriarTestCase {
will(new EncodeTagAction()); will(new EncodeTagAction());
} }
// Save the keys that were rotated // Save the keys that were rotated
oneOf(db).startTransaction(); oneOf(db).updateTransportKeys(txn,
will(returnValue(txn1));
oneOf(db).updateTransportKeys(txn1,
Collections.singletonMap(contactId, rotated)); Collections.singletonMap(contactId, rotated));
oneOf(db).endTransaction(txn1);
// 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(timer).schedule(with(any(TimerTask.class)),
with(rotationPeriodLength - 1)); with(rotationPeriodLength - 1));
@@ -100,7 +96,7 @@ public class TransportKeyManagerTest extends BriarTestCase {
TransportKeyManager transportKeyManager = new TransportKeyManager(db, TransportKeyManager transportKeyManager = new TransportKeyManager(db,
crypto, timer, clock, transportId, maxLatency); crypto, timer, clock, transportId, maxLatency);
transportKeyManager.start(); transportKeyManager.start(txn);
context.assertIsSatisfied(); context.assertIsSatisfied();
} }
@@ -112,10 +108,12 @@ public class TransportKeyManagerTest extends BriarTestCase {
final CryptoComponent crypto = context.mock(CryptoComponent.class); final CryptoComponent crypto = context.mock(CryptoComponent.class);
final Timer timer = context.mock(Timer.class); final Timer timer = context.mock(Timer.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(999, 0); final TransportKeys transportKeys = createTransportKeys(999, 0);
final TransportKeys rotated = createTransportKeys(1000, 0); final TransportKeys rotated = createTransportKeys(1000, 0);
final Transaction txn = new Transaction(null); final Transaction txn = new Transaction(null, false);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(crypto).deriveTransportKeys(transportId, masterKey, 999, oneOf(crypto).deriveTransportKeys(transportId, masterKey, 999,
alice); alice);
@@ -155,9 +153,11 @@ public class TransportKeyManagerTest extends BriarTestCase {
final Timer timer = context.mock(Timer.class); final Timer timer = context.mock(Timer.class);
final Clock clock = context.mock(Clock.class); final Clock clock = context.mock(Clock.class);
final Transaction txn = new Transaction(null, false);
TransportKeyManager transportKeyManager = new TransportKeyManager(db, TransportKeyManager transportKeyManager = new TransportKeyManager(db,
crypto, timer, clock, transportId, maxLatency); crypto, timer, clock, transportId, maxLatency);
assertNull(transportKeyManager.getStreamContext(contactId)); assertNull(transportKeyManager.getStreamContext(txn, contactId));
context.assertIsSatisfied(); context.assertIsSatisfied();
} }
@@ -170,11 +170,13 @@ public class TransportKeyManagerTest extends BriarTestCase {
final CryptoComponent crypto = context.mock(CryptoComponent.class); final CryptoComponent crypto = context.mock(CryptoComponent.class);
final Timer timer = context.mock(Timer.class); final Timer timer = context.mock(Timer.class);
final Clock clock = context.mock(Clock.class); final Clock clock = context.mock(Clock.class);
final boolean alice = true; final boolean alice = true;
// The stream counter has been exhausted // The stream counter has been exhausted
final TransportKeys transportKeys = createTransportKeys(1000, final TransportKeys transportKeys = createTransportKeys(1000,
MAX_32_BIT_UNSIGNED + 1); MAX_32_BIT_UNSIGNED + 1);
final Transaction txn = new Transaction(null); final Transaction txn = new Transaction(null, false);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(crypto).deriveTransportKeys(transportId, masterKey, 1000, oneOf(crypto).deriveTransportKeys(transportId, masterKey, 1000,
alice); alice);
@@ -201,7 +203,7 @@ public class TransportKeyManagerTest extends BriarTestCase {
long timestamp = rotationPeriodLength * 1000; long timestamp = rotationPeriodLength * 1000;
transportKeyManager.addContact(txn, contactId, masterKey, timestamp, transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
alice); alice);
assertNull(transportKeyManager.getStreamContext(contactId)); assertNull(transportKeyManager.getStreamContext(txn, contactId));
context.assertIsSatisfied(); context.assertIsSatisfied();
} }
@@ -213,12 +215,13 @@ public class TransportKeyManagerTest extends BriarTestCase {
final CryptoComponent crypto = context.mock(CryptoComponent.class); final CryptoComponent crypto = context.mock(CryptoComponent.class);
final Timer timer = context.mock(Timer.class); final Timer timer = context.mock(Timer.class);
final Clock clock = context.mock(Clock.class); final Clock clock = context.mock(Clock.class);
final boolean alice = true; final boolean alice = true;
// The stream counter can be used one more time before being exhausted // The stream counter can be used one more time before being exhausted
final TransportKeys transportKeys = createTransportKeys(1000, final TransportKeys transportKeys = createTransportKeys(1000,
MAX_32_BIT_UNSIGNED); MAX_32_BIT_UNSIGNED);
final Transaction txn = new Transaction(null); final Transaction txn = new Transaction(null, false);
final Transaction txn1 = new Transaction(null);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(crypto).deriveTransportKeys(transportId, masterKey, 1000, oneOf(crypto).deriveTransportKeys(transportId, masterKey, 1000,
alice); alice);
@@ -238,11 +241,7 @@ public class TransportKeyManagerTest extends BriarTestCase {
// Save the keys // Save the keys
oneOf(db).addTransportKeys(txn, contactId, transportKeys); oneOf(db).addTransportKeys(txn, contactId, transportKeys);
// Increment the stream counter // Increment the stream counter
oneOf(db).startTransaction(); oneOf(db).incrementStreamCounter(txn, contactId, transportId, 1000);
will(returnValue(txn1));
oneOf(db).incrementStreamCounter(txn1, contactId, transportId,
1000);
oneOf(db).endTransaction(txn1);
}}); }});
TransportKeyManager transportKeyManager = new TransportKeyManager(db, TransportKeyManager transportKeyManager = new TransportKeyManager(db,
@@ -252,7 +251,8 @@ public class TransportKeyManagerTest extends BriarTestCase {
transportKeyManager.addContact(txn, contactId, masterKey, timestamp, transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
alice); alice);
// The first request should return a stream context // The first request should return a stream context
StreamContext ctx = transportKeyManager.getStreamContext(contactId); StreamContext ctx = transportKeyManager.getStreamContext(txn,
contactId);
assertNotNull(ctx); assertNotNull(ctx);
assertEquals(contactId, ctx.getContactId()); assertEquals(contactId, ctx.getContactId());
assertEquals(transportId, ctx.getTransportId()); assertEquals(transportId, ctx.getTransportId());
@@ -260,7 +260,7 @@ public class TransportKeyManagerTest extends BriarTestCase {
assertEquals(headerKey, ctx.getHeaderKey()); assertEquals(headerKey, ctx.getHeaderKey());
assertEquals(MAX_32_BIT_UNSIGNED, ctx.getStreamNumber()); assertEquals(MAX_32_BIT_UNSIGNED, ctx.getStreamNumber());
// The second request should return null, the counter is exhausted // The second request should return null, the counter is exhausted
assertNull(transportKeyManager.getStreamContext(contactId)); assertNull(transportKeyManager.getStreamContext(txn, contactId));
context.assertIsSatisfied(); context.assertIsSatisfied();
} }
@@ -273,9 +273,11 @@ public class TransportKeyManagerTest extends BriarTestCase {
final CryptoComponent crypto = context.mock(CryptoComponent.class); final CryptoComponent crypto = context.mock(CryptoComponent.class);
final Timer timer = context.mock(Timer.class); final Timer timer = context.mock(Timer.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);
final Transaction txn = new Transaction(null); final Transaction txn = new Transaction(null, false);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(crypto).deriveTransportKeys(transportId, masterKey, 1000, oneOf(crypto).deriveTransportKeys(transportId, masterKey, 1000,
alice); alice);
@@ -302,7 +304,8 @@ public class TransportKeyManagerTest extends BriarTestCase {
long timestamp = rotationPeriodLength * 1000; long timestamp = rotationPeriodLength * 1000;
transportKeyManager.addContact(txn, contactId, masterKey, timestamp, transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
alice); alice);
assertNull(transportKeyManager.getStreamContext(new byte[TAG_LENGTH])); assertNull(transportKeyManager.getStreamContext(txn,
new byte[TAG_LENGTH]));
context.assertIsSatisfied(); context.assertIsSatisfied();
} }
@@ -314,12 +317,13 @@ public class TransportKeyManagerTest extends BriarTestCase {
final CryptoComponent crypto = context.mock(CryptoComponent.class); final CryptoComponent crypto = context.mock(CryptoComponent.class);
final Timer timer = context.mock(Timer.class); final Timer timer = context.mock(Timer.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);
final Transaction txn = new Transaction(null);
final Transaction txn1 = new Transaction(null);
// 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<byte[]>();
final Transaction txn = new Transaction(null, false);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(crypto).deriveTransportKeys(transportId, masterKey, 1000, oneOf(crypto).deriveTransportKeys(transportId, masterKey, 1000,
alice); alice);
@@ -343,11 +347,8 @@ public class TransportKeyManagerTest extends BriarTestCase {
with(tagKey), with((long) REORDERING_WINDOW_SIZE)); with(tagKey), with((long) REORDERING_WINDOW_SIZE));
will(new EncodeTagAction(tags)); will(new EncodeTagAction(tags));
// Save the reordering window (previous rotation period, base 1) // Save the reordering window (previous rotation period, base 1)
oneOf(db).startTransaction(); oneOf(db).setReorderingWindow(txn, contactId, transportId, 999,
will(returnValue(txn1));
oneOf(db).setReorderingWindow(txn1, contactId, transportId, 999,
1, new byte[REORDERING_WINDOW_SIZE / 8]); 1, new byte[REORDERING_WINDOW_SIZE / 8]);
oneOf(db).endTransaction(txn1);
}}); }});
TransportKeyManager transportKeyManager = new TransportKeyManager(db, TransportKeyManager transportKeyManager = new TransportKeyManager(db,
@@ -360,7 +361,7 @@ public class TransportKeyManagerTest extends BriarTestCase {
assertEquals(REORDERING_WINDOW_SIZE * 3, tags.size()); assertEquals(REORDERING_WINDOW_SIZE * 3, tags.size());
byte[] tag = tags.get(0); byte[] tag = tags.get(0);
// The first request should return a stream context // The first request should return a stream context
StreamContext ctx = transportKeyManager.getStreamContext(tag); StreamContext ctx = transportKeyManager.getStreamContext(txn, tag);
assertNotNull(ctx); assertNotNull(ctx);
assertEquals(contactId, ctx.getContactId()); assertEquals(contactId, ctx.getContactId());
assertEquals(transportId, ctx.getTransportId()); assertEquals(transportId, ctx.getTransportId());
@@ -370,7 +371,7 @@ public class TransportKeyManagerTest extends BriarTestCase {
// Another tag should have been encoded // Another tag should have been encoded
assertEquals(REORDERING_WINDOW_SIZE * 3 + 1, tags.size()); assertEquals(REORDERING_WINDOW_SIZE * 3 + 1, tags.size());
// The second request should return null, the tag has already been used // The second request should return null, the tag has already been used
assertNull(transportKeyManager.getStreamContext(tag)); assertNull(transportKeyManager.getStreamContext(txn, tag));
context.assertIsSatisfied(); context.assertIsSatisfied();
} }
@@ -382,22 +383,21 @@ public class TransportKeyManagerTest extends BriarTestCase {
final CryptoComponent crypto = context.mock(CryptoComponent.class); final CryptoComponent crypto = context.mock(CryptoComponent.class);
final Timer timer = context.mock(Timer.class); final Timer timer = context.mock(Timer.class);
final Clock clock = context.mock(Clock.class); final Clock clock = context.mock(Clock.class);
final Transaction txn = new Transaction(null);
final TransportKeys transportKeys = createTransportKeys(1000, 0); final TransportKeys transportKeys = createTransportKeys(1000, 0);
final Map<ContactId, TransportKeys> loaded = final Map<ContactId, TransportKeys> loaded =
Collections.singletonMap(contactId, transportKeys); Collections.singletonMap(contactId, transportKeys);
final TransportKeys rotated = createTransportKeys(1001, 0); final TransportKeys rotated = createTransportKeys(1001, 0);
final Transaction txn1 = new Transaction(null); final Transaction txn = new Transaction(null, false);
final Transaction txn1 = new Transaction(null, false);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// Get the current time (the start of rotation period 1000) // Get the current time (the start of rotation period 1000)
oneOf(clock).currentTimeMillis(); oneOf(clock).currentTimeMillis();
will(returnValue(rotationPeriodLength * 1000)); will(returnValue(rotationPeriodLength * 1000));
// Load the transport keys // Load the transport keys
oneOf(db).startTransaction();
will(returnValue(txn));
oneOf(db).getTransportKeys(txn, transportId); oneOf(db).getTransportKeys(txn, transportId);
will(returnValue(loaded)); will(returnValue(loaded));
oneOf(db).endTransaction(txn);
// Rotate the transport keys (the keys are unaffected) // Rotate the transport keys (the keys are unaffected)
oneOf(crypto).rotateTransportKeys(transportKeys, 1000); oneOf(crypto).rotateTransportKeys(transportKeys, 1000);
will(returnValue(transportKeys)); will(returnValue(transportKeys));
@@ -411,6 +411,9 @@ public class TransportKeyManagerTest extends BriarTestCase {
oneOf(timer).schedule(with(any(TimerTask.class)), oneOf(timer).schedule(with(any(TimerTask.class)),
with(rotationPeriodLength)); with(rotationPeriodLength));
will(new RunTimerTaskAction()); will(new RunTimerTaskAction());
// Start a transaction for key rotation
oneOf(db).startTransaction(false);
will(returnValue(txn1));
// Get the current time (the start of rotation period 1001) // Get the current time (the start of rotation period 1001)
oneOf(clock).currentTimeMillis(); oneOf(clock).currentTimeMillis();
will(returnValue(rotationPeriodLength * 1001)); will(returnValue(rotationPeriodLength * 1001));
@@ -425,19 +428,19 @@ public class TransportKeyManagerTest extends BriarTestCase {
will(new EncodeTagAction()); will(new EncodeTagAction());
} }
// Save the keys that were rotated // Save the keys that were rotated
oneOf(db).startTransaction();
will(returnValue(txn1));
oneOf(db).updateTransportKeys(txn1, oneOf(db).updateTransportKeys(txn1,
Collections.singletonMap(contactId, rotated)); Collections.singletonMap(contactId, rotated));
oneOf(db).endTransaction(txn1);
// 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(timer).schedule(with(any(TimerTask.class)),
with(rotationPeriodLength)); with(rotationPeriodLength));
// Commit the key rotation transaction
oneOf(db).endTransaction(txn1);
}}); }});
TransportKeyManager transportKeyManager = new TransportKeyManager(db, TransportKeyManager transportKeyManager = new TransportKeyManager(db,
crypto, timer, clock, transportId, maxLatency); crypto, timer, clock, transportId, maxLatency);
transportKeyManager.start(); transportKeyManager.start(txn);
assertTrue(txn1.isComplete());
context.assertIsSatisfied(); context.assertIsSatisfied();
} }