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

View File

@@ -4,12 +4,10 @@ import org.briarproject.BriarTestCase;
import org.briarproject.ImmediateExecutor;
import org.briarproject.TestDatabaseModule;
import org.briarproject.TestUtils;
import org.briarproject.api.TransportId;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.contact.ContactManager;
import org.briarproject.api.crypto.SecretKey;
import org.briarproject.api.db.DatabaseComponent;
import org.briarproject.api.db.Transaction;
import org.briarproject.api.event.Event;
import org.briarproject.api.event.EventBus;
import org.briarproject.api.event.EventListener;
@@ -36,27 +34,26 @@ import org.junit.After;
import org.junit.Before;
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.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStream;
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 aliceDir = new File(testDir, "alice");
private final File bobDir = new File(testDir, "bob");
private final TransportId transportId = new TransportId("id");
private final SecretKey master = TestUtils.createSecretKey();
private final long timestamp = System.currentTimeMillis();
private final AuthorId aliceId = new AuthorId(TestUtils.getRandomId());
@@ -80,7 +77,7 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
private byte[] write() throws Exception {
// Instantiate Alice's services
LifecycleManager lifecycleManager = alice.getLifeCycleManager();
LifecycleManager lifecycleManager = alice.getLifecycleManager();
DatabaseComponent db = alice.getDatabaseComponent();
IdentityManager identityManager = alice.getIdentityManager();
ContactManager contactManager = alice.getContactManager();
@@ -97,14 +94,6 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
// Start the lifecycle manager
lifecycleManager.startServices();
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
LocalAuthor aliceAuthor = new LocalAuthor(aliceId, "Alice",
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);
messagingManager.addLocalMessage(message);
// Get a stream context
StreamContext ctx = keyManager.getStreamContext(contactId, transportId);
StreamContext ctx = keyManager.getStreamContext(contactId,
TRANSPORT_ID);
assertNotNull(ctx);
// Create a stream writer
ByteArrayOutputStream out = new ByteArrayOutputStream();
@@ -132,8 +122,8 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
PacketWriter packetWriter = packetWriterFactory.createPacketWriter(
streamWriter);
SyncSession session = new SimplexOutgoingSession(db,
new ImmediateExecutor(), eventBus, contactId, transportId,
MAX_LATENCY, packetWriter);
new ImmediateExecutor(), eventBus, contactId, MAX_LATENCY,
packetWriter);
// Write whatever needs to be written
session.run();
streamWriter.close();
@@ -148,7 +138,7 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
private void read(byte[] stream) throws Exception {
// Instantiate Bob's services
LifecycleManager lifecycleManager = bob.getLifeCycleManager();
LifecycleManager lifecycleManager = bob.getLifecycleManager();
DatabaseComponent db = bob.getDatabaseComponent();
IdentityManager identityManager = bob.getIdentityManager();
ContactManager contactManager = bob.getContactManager();
@@ -162,14 +152,6 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
// Start the lifecyle manager
lifecycleManager.startServices();
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
LocalAuthor bobAuthor = new LocalAuthor(bobId, "Bob",
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];
int read = in.read(tag);
assertEquals(tag.length, read);
StreamContext ctx = keyManager.getStreamContext(transportId, tag);
StreamContext ctx = keyManager.getStreamContext(TRANSPORT_ID, tag);
assertNotNull(ctx);
// Create a stream reader
InputStream streamReader = streamReaderFactory.createStreamReader(
@@ -197,7 +179,7 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
PacketReader packetReader = packetReaderFactory.createPacketReader(
streamReader);
SyncSession session = new IncomingSession(db, new ImmediateExecutor(),
eventBus, contactId, transportId, packetReader);
eventBus, contactId, packetReader);
// No messages should have been added yet
assertFalse(listener.messageAdded);
// 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.lifecycle.IoExecutor;
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.simplex.SimplexPluginConfig;
import org.briarproject.api.plugins.simplex.SimplexPluginFactory;
import org.briarproject.api.system.LocationUtils;
import org.briarproject.plugins.droidtooth.DroidtoothPluginFactory;
@@ -29,17 +28,8 @@ import dagger.Provides;
public class AndroidPluginsModule {
@Provides
SimplexPluginConfig provideSimplexPluginConfig() {
return new SimplexPluginConfig() {
public Collection<SimplexPluginFactory> getFactories() {
return Collections.emptyList();
}
};
}
@Provides
public DuplexPluginConfig provideDuplexPluginConfig(
@IoExecutor Executor ioExecutor, AndroidExecutor androidExecutor,
public PluginConfig providePluginConfig(@IoExecutor Executor ioExecutor,
AndroidExecutor androidExecutor,
SecureRandom random, BackoffFactory backoffFactory, Application app,
LocationUtils locationUtils, EventBus eventBus) {
Context appContext = app.getApplicationContext();
@@ -49,13 +39,19 @@ public class AndroidPluginsModule {
locationUtils, eventBus);
DuplexPluginFactory lan = new AndroidLanTcpPluginFactory(ioExecutor,
backoffFactory, appContext);
final Collection<DuplexPluginFactory> factories =
final Collection<DuplexPluginFactory> duplex =
Arrays.asList(bluetooth, tor, lan);
return new DuplexPluginConfig() {
public Collection<DuplexPluginFactory> getFactories() {
return factories;
return new PluginConfig() {
@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 org.briarproject.api.TransportId;
import org.briarproject.android.api.AndroidExecutor;
import org.briarproject.api.TransportId;
import org.briarproject.api.plugins.Backoff;
import org.briarproject.api.plugins.BackoffFactory;
import org.briarproject.api.plugins.duplex.DuplexPlugin;
@@ -40,6 +40,10 @@ public class DroidtoothPluginFactory implements DuplexPluginFactory {
return DroidtoothPlugin.ID;
}
public int getMaxLatency() {
return MAX_LATENCY;
}
public DuplexPlugin createPlugin(DuplexPluginCallback callback) {
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE);

View File

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

View File

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

View File

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

View File

@@ -1,16 +1,16 @@
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.InputStream;
import java.io.OutputStream;
import java.net.Socket;
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 {
private final Plugin plugin;
@@ -38,10 +38,6 @@ class TorTransportConnection implements DuplexTransportConnection {
private class Reader implements TransportConnectionReader {
public long getMaxLatency() {
return plugin.getMaxLatency();
}
public InputStream getInputStream() throws IOException {
return socket.getInputStream();
}

View File

@@ -26,10 +26,6 @@ import java.util.Map;
/**
* Encapsulates the database implementation and exposes high-level operations
* 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 {
@@ -45,8 +41,12 @@ public interface DatabaseComponent {
/**
* 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
@@ -142,70 +142,97 @@ public interface DatabaseComponent {
/**
* Returns the contact with the given ID.
* <p/>
* Read-only.
*/
Contact getContact(Transaction txn, ContactId c) throws DbException;
/**
* Returns all contacts.
* <p/>
* Read-only.
*/
Collection<Contact> getContacts(Transaction txn) throws DbException;
/**
* Returns all contacts associated with the given local pseudonym.
* <p/>
* Read-only.
*/
Collection<ContactId> getContacts(Transaction txn, AuthorId a)
throws DbException;
/**
* Returns the unique ID for this device.
* <p/>
* Read-only.
*/
DeviceId getDeviceId(Transaction txn) throws DbException;
/**
* Returns the group with the given ID.
* <p/>
* Read-only.
*/
Group getGroup(Transaction txn, GroupId g) throws DbException;
/**
* Returns the metadata for the given group.
* <p/>
* Read-only.
*/
Metadata getGroupMetadata(Transaction txn, GroupId g) throws DbException;
/**
* Returns all groups belonging to the given client.
* <p/>
* Read-only.
*/
Collection<Group> getGroups(Transaction txn, ClientId c) throws DbException;
/**
* Returns the local pseudonym with the given ID.
* <p/>
* Read-only.
*/
LocalAuthor getLocalAuthor(Transaction txn, AuthorId a) throws DbException;
/**
* Returns all local pseudonyms.
* <p/>
* Read-only.
*/
Collection<LocalAuthor> getLocalAuthors(Transaction txn) throws DbException;
/**
* Returns the IDs of any messages that need to be validated by the given
* client.
* <p/>
* Read-only.
*/
Collection<MessageId> getMessagesToValidate(Transaction txn, ClientId c)
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;
/**
* Returns the metadata for all messages in the given group.
* <p/>
* Read-only.
*/
Map<MessageId, Metadata> getMessageMetadata(Transaction txn, GroupId g)
throws DbException;
/**
* Returns the metadata for the given message.
* <p/>
* Read-only.
*/
Metadata getMessageMetadata(Transaction txn, MessageId m)
throws DbException;
@@ -213,6 +240,8 @@ public interface DatabaseComponent {
/**
* Returns the status of all messages in the given group with respect to
* the given contact.
* <p/>
* Read-only.
*/
Collection<MessageStatus> getMessageStatus(Transaction txn, ContactId c,
GroupId g) throws DbException;
@@ -220,27 +249,27 @@ public interface DatabaseComponent {
/**
* Returns the status of the given message with respect to the given
* contact.
* <p/>
* Read-only.
*/
MessageStatus getMessageStatus(Transaction txn, ContactId c, MessageId m)
throws DbException;
/**
* Returns all settings in the given namespace.
* <p/>
* Read-only.
*/
Settings getSettings(Transaction txn, String namespace) throws DbException;
/**
* Returns all transport keys for the given transport.
* <p/>
* Read-only.
*/
Map<ContactId, TransportKeys> getTransportKeys(Transaction txn,
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
* 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.
* <p/>
* Read-only.
*/
boolean isVisibleToContact(Transaction txn, ContactId c, GroupId g)
throws DbException;

View File

@@ -12,12 +12,14 @@ import java.util.List;
public class Transaction {
private final Object txn;
private final boolean readOnly;
private List<Event> events = null;
private boolean complete = false;
public Transaction(Object txn) {
public Transaction(Object txn, boolean readOnly) {
this.txn = txn;
this.readOnly = readOnly;
}
/**
@@ -28,6 +30,13 @@ public class Transaction {
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
* 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 {
/** 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;
/**
@@ -20,10 +19,13 @@ public interface TransportConnectionReader {
* 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
* 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
* 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;
}

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;
/** Factory for creating a plugin for a duplex transport. */
/**
* Factory for creating a plugin for a duplex transport.
*/
public interface DuplexPluginFactory {
/** Returns the plugin's transport identifier. */
/**
* Returns the plugin's transport identifier.
*/
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);
}

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;
/** Factory for creating a plugin for a simplex transport. */
/**
* Factory for creating a plugin for a simplex transport.
*/
public interface SimplexPluginFactory {
/** Returns the plugin's transport identifier. */
/**
* Returns the plugin's transport identifier.
*/
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);
}

View File

@@ -1,6 +1,5 @@
package org.briarproject.api.sync;
import org.briarproject.api.TransportId;
import org.briarproject.api.contact.ContactId;
import java.io.InputStream;
@@ -8,12 +7,11 @@ import java.io.OutputStream;
public interface SyncSessionFactory {
SyncSession createIncomingSession(ContactId c, TransportId t,
InputStream in);
SyncSession createIncomingSession(ContactId c, InputStream in);
SyncSession createSimplexOutgoingSession(ContactId c, TransportId t,
int maxLatency, OutputStream out);
SyncSession createSimplexOutgoingSession(ContactId c, int maxLatency,
OutputStream out);
SyncSession createDuplexOutgoingSession(ContactId c, TransportId t,
int maxLatency, int maxIdleTime, OutputStream out);
SyncSession createDuplexOutgoingSession(ContactId c, int maxLatency,
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 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
* from the corresponding stream, or null if an error occurs or the tag was
* 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
public void addLocalMessage(Message m, ClientId c, BdfDictionary metadata,
boolean shared) throws DbException, FormatException {
Transaction txn = db.startTransaction();
Transaction txn = db.startTransaction(false);
try {
addLocalMessage(txn, m, c, metadata, shared);
txn.setComplete();
@@ -89,7 +89,7 @@ class ClientHelperImpl implements ClientHelper {
public BdfDictionary getMessageAsDictionary(MessageId m) throws DbException,
FormatException {
BdfDictionary dictionary;
Transaction txn = db.startTransaction();
Transaction txn = db.startTransaction(true);
try {
dictionary = getMessageAsDictionary(txn, m);
txn.setComplete();
@@ -112,7 +112,7 @@ class ClientHelperImpl implements ClientHelper {
public BdfList getMessageAsList(MessageId m) throws DbException,
FormatException {
BdfList list;
Transaction txn = db.startTransaction();
Transaction txn = db.startTransaction(true);
try {
list = getMessageAsList(txn, m);
txn.setComplete();
@@ -135,7 +135,7 @@ class ClientHelperImpl implements ClientHelper {
public BdfDictionary getGroupMetadataAsDictionary(GroupId g)
throws DbException, FormatException {
BdfDictionary dictionary;
Transaction txn = db.startTransaction();
Transaction txn = db.startTransaction(true);
try {
dictionary = getGroupMetadataAsDictionary(txn, g);
txn.setComplete();
@@ -156,7 +156,7 @@ class ClientHelperImpl implements ClientHelper {
public BdfDictionary getMessageMetadataAsDictionary(MessageId m)
throws DbException, FormatException {
BdfDictionary dictionary;
Transaction txn = db.startTransaction();
Transaction txn = db.startTransaction(true);
try {
dictionary = getMessageMetadataAsDictionary(txn, m);
txn.setComplete();
@@ -177,7 +177,7 @@ class ClientHelperImpl implements ClientHelper {
public Map<MessageId, BdfDictionary> getMessageMetatataAsDictionary(
GroupId g) throws DbException, FormatException {
Map<MessageId, BdfDictionary> map;
Transaction txn = db.startTransaction();
Transaction txn = db.startTransaction(true);
try {
map = getMessageMetadataAsDictionary(txn, g);
txn.setComplete();
@@ -201,7 +201,7 @@ class ClientHelperImpl implements ClientHelper {
@Override
public void mergeGroupMetadata(GroupId g, BdfDictionary metadata)
throws DbException, FormatException {
Transaction txn = db.startTransaction();
Transaction txn = db.startTransaction(false);
try {
mergeGroupMetadata(txn, g, metadata);
txn.setComplete();
@@ -219,7 +219,7 @@ class ClientHelperImpl implements ClientHelper {
@Override
public void mergeMessageMetadata(MessageId m, BdfDictionary metadata)
throws DbException, FormatException {
Transaction txn = db.startTransaction();
Transaction txn = db.startTransaction(false);
try {
mergeMessageMetadata(txn, m, metadata);
txn.setComplete();

View File

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

View File

@@ -118,38 +118,52 @@ interface Database<T> {
/**
* Returns true if the database contains the given contact for the given
* local pseudonym.
* <p/>
* Read-only.
*/
boolean containsContact(T txn, AuthorId remote, AuthorId local)
throws DbException;
/**
* Returns true if the database contains the given contact.
* <p/>
* Read-only.
*/
boolean containsContact(T txn, ContactId c) throws DbException;
/**
* Returns true if the database contains the given group.
* <p/>
* Read-only.
*/
boolean containsGroup(T txn, GroupId g) throws DbException;
/**
* Returns true if the database contains the given local pseudonym.
* <p/>
* Read-only.
*/
boolean containsLocalAuthor(T txn, AuthorId a) throws DbException;
/**
* Returns true if the database contains the given message.
* <p/>
* Read-only.
*/
boolean containsMessage(T txn, MessageId m) throws DbException;
/**
* Returns true if the database contains the given transport.
* <p/>
* Read-only.
*/
boolean containsTransport(T txn, TransportId t) throws DbException;
/**
* Returns true if the database contains the given group and the group is
* visible to the given contact.
* <p/>
* Read-only.
*/
boolean containsVisibleGroup(T txn, ContactId c, GroupId g)
throws DbException;
@@ -157,12 +171,16 @@ interface Database<T> {
/**
* Returns true if the database contains the given message and the message
* is visible to the given contact.
* <p/>
* Read-only.
*/
boolean containsVisibleMessage(T txn, ContactId c, MessageId m)
throws DbException;
/**
* Returns the number of messages offered by the given contact.
* <p/>
* Read-only.
*/
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
* associated data are not deleted, and
* {@link #containsMessage(Object, MessageId)} will continue to return true.
* <p>
* Locking: write.
*/
void deleteMessage(T txn, MessageId m) throws DbException;
/**
* Deletes any metadata associated with the given message.
* <p>
* Locking: write.
*/
void deleteMessageMetadata(T txn, MessageId m) throws DbException;
/**
* Returns the contact with the given ID.
* <p/>
* Read-only.
*/
Contact getContact(T txn, ContactId c) throws DbException;
/**
* Returns all contacts.
* <p/>
* Read-only.
*/
Collection<Contact> getContacts(T txn) throws DbException;
/**
* Returns all contacts associated with the given local pseudonym.
* <p/>
* Read-only.
*/
Collection<ContactId> getContacts(T txn, AuthorId a) throws DbException;
/**
* Returns the unique ID for this device.
* <p/>
* Read-only.
*/
DeviceId getDeviceId(T txn) throws DbException;
@@ -212,48 +234,66 @@ interface Database<T> {
/**
* Returns the group with the given ID.
* <p/>
* Read-only.
*/
Group getGroup(T txn, GroupId g) throws DbException;
/**
* Returns the metadata for the given group.
* <p/>
* Read-only.
*/
Metadata getGroupMetadata(T txn, GroupId g) throws DbException;
/**
* Returns all groups belonging to the given client.
* <p/>
* Read-only.
*/
Collection<Group> getGroups(T txn, ClientId c) throws DbException;
/**
* Returns the local pseudonym with the given ID.
* <p/>
* Read-only.
*/
LocalAuthor getLocalAuthor(T txn, AuthorId a) throws DbException;
/**
* Returns all local pseudonyms.
* <p/>
* Read-only.
*/
Collection<LocalAuthor> getLocalAuthors(T txn) throws DbException;
/**
* Returns the IDs of all messages in the given group.
* <p/>
* Read-only.
*/
Collection<MessageId> getMessageIds(T txn, GroupId g) throws DbException;
/**
* Returns the metadata for all messages in the given group.
* <p/>
* Read-only.
*/
Map<MessageId, Metadata> getMessageMetadata(T txn, GroupId g)
throws DbException;
/**
* Returns the metadata for the given message.
* <p/>
* Read-only.
*/
Metadata getMessageMetadata(T txn, MessageId m) throws DbException;
/**
* Returns the status of all messages in the given group with respect
* to the given contact.
* <p/>
* Read-only.
*/
Collection<MessageStatus> getMessageStatus(T txn, ContactId c, GroupId g)
throws DbException;
@@ -261,6 +301,8 @@ interface Database<T> {
/**
* Returns the status of the given message with respect to the given
* contact.
* <p/>
* Read-only.
*/
MessageStatus getMessageStatus(T txn, ContactId c, MessageId m)
throws DbException;
@@ -268,6 +310,8 @@ interface Database<T> {
/**
* Returns the IDs of some messages received from the given contact that
* need to be acknowledged, up to the given number of messages.
* <p/>
* Read-only.
*/
Collection<MessageId> getMessagesToAck(T txn, ContactId c, int maxMessages)
throws DbException;
@@ -275,6 +319,8 @@ interface Database<T> {
/**
* Returns the IDs of some messages that are eligible to be offered to the
* given contact, up to the given number of messages.
* <p/>
* Read-only.
*/
Collection<MessageId> getMessagesToOffer(T txn, ContactId c,
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
* given contact, up to the given total length.
* <p/>
* Read-only.
*/
Collection<MessageId> getMessagesToSend(T txn, ContactId c, int maxLength)
throws DbException;
@@ -289,6 +337,8 @@ interface Database<T> {
/**
* Returns the IDs of some messages that are eligible to be requested from
* the given contact, up to the given number of messages.
* <p/>
* Read-only.
*/
Collection<MessageId> getMessagesToRequest(T txn, ContactId c,
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
* client.
* <p/>
* Read-only.
*/
Collection<MessageId> getMessagesToValidate(T txn, ClientId c)
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;
@@ -309,28 +364,31 @@ interface Database<T> {
* 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
* total length.
* <p/>
* Read-only.
*/
Collection<MessageId> getRequestedMessagesToSend(T txn, ContactId c,
int maxLength) throws DbException;
/**
* Returns all settings in the given namespace.
* <p/>
* Read-only.
*/
Settings getSettings(T txn, String namespace) throws DbException;
/**
* Returns all transport keys for the given transport.
* <p/>
* Read-only.
*/
Map<ContactId, TransportKeys> getTransportKeys(T txn, TransportId t)
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.
* <p/>
* Read-only.
*/
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.MessagesSentEvent;
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.AuthorId;
import org.briarproject.api.identity.LocalAuthor;
@@ -61,6 +59,8 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.logging.Logger;
import javax.inject.Inject;
@@ -80,6 +80,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
private final EventBus eventBus;
private final ShutdownManager shutdown;
private final AtomicBoolean closed = new AtomicBoolean(false);
private final ReadWriteLock lock = new ReentrantReadWriteLock(true);
private volatile int shutdownHandle = -1;
@@ -117,18 +118,33 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
db.close();
}
public Transaction startTransaction() throws DbException {
return new Transaction(db.startTransaction());
public Transaction startTransaction(boolean readOnly) throws DbException {
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 {
T txn = txnClass.cast(transaction.unbox());
if (transaction.isComplete()) {
db.commitTransaction(txn);
for (Event e : transaction.getEvents()) eventBus.broadcast(e);
} else {
db.abortTransaction(txn);
try {
T txn = txnClass.cast(transaction.unbox());
if (transaction.isComplete()) db.commitTransaction(txn);
else db.abortTransaction(txn);
} finally {
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) {
@@ -138,6 +154,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public ContactId addContact(Transaction transaction, Author remote,
AuthorId local, boolean active) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsLocalAuthor(txn, local))
throw new NoSuchLocalAuthorException();
@@ -150,6 +167,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
}
public void addGroup(Transaction transaction, Group g) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsGroup(txn, g.getId())) {
db.addGroup(txn, g);
@@ -159,6 +177,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public void addLocalAuthor(Transaction transaction, LocalAuthor a)
throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsLocalAuthor(txn, a.getId())) {
db.addLocalAuthor(txn, a);
@@ -168,6 +187,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public void addLocalMessage(Transaction transaction, Message m, ClientId c,
Metadata meta, boolean shared) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsGroup(txn, m.getGroupId()))
throw new NoSuchGroupException();
@@ -191,15 +211,15 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public void addTransport(Transaction transaction, TransportId t,
int maxLatency) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsTransport(txn, t)) {
if (!db.containsTransport(txn, t))
db.addTransport(txn, t, maxLatency);
transaction.attach(new TransportAddedEvent(t, maxLatency));
}
}
public void addTransportKeys(Transaction transaction, ContactId c,
TransportKeys k) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsContact(txn, c))
throw new NoSuchContactException();
@@ -210,6 +230,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public void deleteMessage(Transaction transaction, MessageId m)
throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsMessage(txn, m))
throw new NoSuchMessageException();
@@ -218,6 +239,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public void deleteMessageMetadata(Transaction transaction, MessageId m)
throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsMessage(txn, m))
throw new NoSuchMessageException();
@@ -226,6 +248,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public Ack generateAck(Transaction transaction, ContactId c,
int maxMessages) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsContact(txn, c))
throw new NoSuchContactException();
@@ -237,6 +260,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public Collection<byte[]> generateBatch(Transaction transaction,
ContactId c, int maxLength, int maxLatency) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsContact(txn, c))
throw new NoSuchContactException();
@@ -254,6 +278,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public Offer generateOffer(Transaction transaction, ContactId c,
int maxMessages, int maxLatency) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsContact(txn, c))
throw new NoSuchContactException();
@@ -265,6 +290,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public Request generateRequest(Transaction transaction, ContactId c,
int maxMessages) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsContact(txn, c))
throw new NoSuchContactException();
@@ -277,6 +303,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public Collection<byte[]> generateRequestedBatch(Transaction transaction,
ContactId c, int maxLength, int maxLatency) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsContact(txn, c))
throw new NoSuchContactException();
@@ -420,14 +447,9 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
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,
TransportId t, long rotationPeriod) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsContact(txn, c))
throw new NoSuchContactException();
@@ -448,6 +470,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public void mergeGroupMetadata(Transaction transaction, GroupId g,
Metadata meta) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsGroup(txn, g))
throw new NoSuchGroupException();
@@ -456,6 +479,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public void mergeMessageMetadata(Transaction transaction, MessageId m,
Metadata meta) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsMessage(txn, m))
throw new NoSuchMessageException();
@@ -464,6 +488,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public void mergeSettings(Transaction transaction, Settings s,
String namespace) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
Settings old = db.getSettings(txn, namespace);
Settings merged = new Settings();
@@ -477,6 +502,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public void receiveAck(Transaction transaction, ContactId c, Ack a)
throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsContact(txn, c))
throw new NoSuchContactException();
@@ -492,6 +518,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public void receiveMessage(Transaction transaction, ContactId c, Message m)
throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsContact(txn, c))
throw new NoSuchContactException();
@@ -507,6 +534,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public void receiveOffer(Transaction transaction, ContactId c, Offer o)
throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsContact(txn, c))
throw new NoSuchContactException();
@@ -529,6 +557,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public void receiveRequest(Transaction transaction, ContactId c, Request r)
throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsContact(txn, c))
throw new NoSuchContactException();
@@ -545,6 +574,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public void removeContact(Transaction transaction, ContactId c)
throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsContact(txn, c))
throw new NoSuchContactException();
@@ -554,6 +584,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public void removeGroup(Transaction transaction, Group g)
throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
GroupId id = g.getId();
if (!db.containsGroup(txn, id))
@@ -566,6 +597,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public void removeLocalAuthor(Transaction transaction, AuthorId a)
throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsLocalAuthor(txn, a))
throw new NoSuchLocalAuthorException();
@@ -575,15 +607,16 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public void removeTransport(Transaction transaction, TransportId t)
throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsTransport(txn, t))
throw new NoSuchTransportException();
db.removeTransport(txn, t);
transaction.attach(new TransportRemovedEvent(t));
}
public void setContactActive(Transaction transaction, ContactId c,
boolean active) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsContact(txn, c))
throw new NoSuchContactException();
@@ -593,6 +626,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public void setMessageShared(Transaction transaction, Message m,
boolean shared) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsMessage(txn, m.getId()))
throw new NoSuchMessageException();
@@ -602,6 +636,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public void setMessageValid(Transaction transaction, Message m, ClientId c,
boolean valid) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsMessage(txn, m.getId()))
throw new NoSuchMessageException();
@@ -612,6 +647,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public void setReorderingWindow(Transaction transaction, ContactId c,
TransportId t, long rotationPeriod, long base, byte[] bitmap)
throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsContact(txn, c))
throw new NoSuchContactException();
@@ -622,6 +658,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public void setVisibleToContact(Transaction transaction, ContactId c,
GroupId g, boolean visible) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsContact(txn, c))
throw new NoSuchContactException();
@@ -647,6 +684,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public void updateTransportKeys(Transaction transaction,
Map<ContactId, TransportKeys> keys) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
Map<ContactId, TransportKeys> filtered =
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)
throws DbException {
PreparedStatement ps = null;

View File

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

View File

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

View File

@@ -37,7 +37,7 @@ class IdentityManagerImpl implements IdentityManager {
@Override
public void addLocalAuthor(LocalAuthor localAuthor) throws DbException {
Transaction txn = db.startTransaction();
Transaction txn = db.startTransaction(false);
try {
db.addLocalAuthor(txn, localAuthor);
for (AddIdentityHook hook : addHooks)
@@ -51,7 +51,7 @@ class IdentityManagerImpl implements IdentityManager {
@Override
public LocalAuthor getLocalAuthor(AuthorId a) throws DbException {
LocalAuthor author;
Transaction txn = db.startTransaction();
Transaction txn = db.startTransaction(true);
try {
author = db.getLocalAuthor(txn, a);
txn.setComplete();
@@ -64,7 +64,7 @@ class IdentityManagerImpl implements IdentityManager {
@Override
public Collection<LocalAuthor> getLocalAuthors() throws DbException {
Collection<LocalAuthor> authors;
Transaction txn = db.startTransaction();
Transaction txn = db.startTransaction(true);
try {
authors = db.getLocalAuthors(txn);
txn.setComplete();
@@ -76,7 +76,7 @@ class IdentityManagerImpl implements IdentityManager {
@Override
public void removeLocalAuthor(AuthorId a) throws DbException {
Transaction txn = db.startTransaction();
Transaction txn = db.startTransaction(false);
try {
LocalAuthor localAuthor = db.getLocalAuthor(txn, a);
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.lifecycle.LifecycleManager;
import org.briarproject.api.lifecycle.Service;
import org.briarproject.api.system.Clock;
import java.io.IOException;
import java.util.Collection;
@@ -30,7 +29,6 @@ class LifecycleManagerImpl implements LifecycleManager {
private static final Logger LOG =
Logger.getLogger(LifecycleManagerImpl.class.getName());
private final Clock clock;
private final DatabaseComponent db;
private final EventBus eventBus;
private final Collection<Service> services;
@@ -41,8 +39,7 @@ class LifecycleManagerImpl implements LifecycleManager {
private final CountDownLatch shutdownLatch = new CountDownLatch(1);
@Inject
LifecycleManagerImpl(Clock clock, DatabaseComponent db, EventBus eventBus) {
this.clock = clock;
LifecycleManagerImpl(DatabaseComponent db, EventBus eventBus) {
this.db = db;
this.eventBus = eventBus;
services = new CopyOnWriteArrayList<Service>();
@@ -68,9 +65,9 @@ class LifecycleManagerImpl implements LifecycleManager {
}
try {
LOG.info("Starting services");
long now = clock.currentTimeMillis();
long start = System.currentTimeMillis();
boolean reopened = db.open();
long duration = clock.currentTimeMillis() - now;
long duration = System.currentTimeMillis() - start;
if (LOG.isLoggable(INFO)) {
if (reopened)
LOG.info("Reopening database took " + duration + " ms");
@@ -78,9 +75,9 @@ class LifecycleManagerImpl implements LifecycleManager {
}
dbLatch.countDown();
for (Service s : services) {
now = clock.currentTimeMillis();
start = System.currentTimeMillis();
boolean started = s.start();
duration = clock.currentTimeMillis() - now;
duration = System.currentTimeMillis() - start;
if (!started) {
if (LOG.isLoggable(WARNING)) {
String name = s.getClass().getName();

View File

@@ -1,6 +1,10 @@
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.Executor;
@@ -12,16 +16,11 @@ import java.util.concurrent.ThreadPoolExecutor;
import javax.inject.Inject;
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.Provides;
import static java.util.concurrent.TimeUnit.SECONDS;
@Module
public class LifecycleModule {
@@ -51,9 +50,9 @@ public class LifecycleModule {
@Provides
@Singleton
LifecycleManager provideLifeCycleManager(Clock clock, DatabaseComponent db,
LifecycleManager provideLifecycleManager(DatabaseComponent db,
EventBus eventBus) {
return new LifecycleManagerImpl(clock, db, eventBus);
return new LifecycleManagerImpl(db, eventBus);
}
@Provides
@@ -63,5 +62,4 @@ public class LifecycleModule {
lifecycleManager.registerForShutdown(ioExecutor);
return ioExecutor;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,16 +1,16 @@
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.InputStream;
import java.io.OutputStream;
import java.net.Socket;
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 {
private final Plugin plugin;
@@ -38,10 +38,6 @@ class TcpTransportConnection implements DuplexTransportConnection {
private class Reader implements TransportConnectionReader {
public long getMaxLatency() {
return plugin.getMaxLatency();
}
public InputStream getInputStream() throws IOException {
return socket.getInputStream();
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -83,7 +83,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
public void run() {
try {
Queue<MessageId> unvalidated = new LinkedList<MessageId>();
Transaction txn = db.startTransaction();
Transaction txn = db.startTransaction(true);
try {
unvalidated.addAll(db.getMessagesToValidate(txn, c));
txn.setComplete();
@@ -106,7 +106,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
try {
Message m = null;
Group g = null;
Transaction txn = db.startTransaction();
Transaction txn = db.startTransaction(true);
try {
MessageId id = unvalidated.poll();
byte[] raw = db.getRawMessage(txn, id);
@@ -160,7 +160,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
dbExecutor.execute(new Runnable() {
public void run() {
try {
Transaction txn = db.startTransaction();
Transaction txn = db.startTransaction(false);
try {
if (meta == null) {
db.setMessageValid(txn, m, c, false);
@@ -198,7 +198,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
public void run() {
try {
Group g;
Transaction txn = db.startTransaction();
Transaction txn = db.startTransaction(true);
try {
g = db.getGroup(txn, m.getGroupId());
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.Event;
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.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.Timer;
import org.briarproject.api.transport.KeyManager;
import org.briarproject.api.transport.StreamContext;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
@@ -30,6 +31,7 @@ import java.util.logging.Logger;
import javax.inject.Inject;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
class KeyManagerImpl implements KeyManager, Service, EventListener {
@@ -40,6 +42,7 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
private final DatabaseComponent db;
private final CryptoComponent crypto;
private final ExecutorService dbExecutor;
private final PluginConfig pluginConfig;
private final Timer timer;
private final Clock clock;
private final Map<ContactId, Boolean> activeContacts;
@@ -47,11 +50,12 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
@Inject
KeyManagerImpl(DatabaseComponent db, CryptoComponent crypto,
@DatabaseExecutor ExecutorService dbExecutor, Timer timer,
Clock clock) {
@DatabaseExecutor ExecutorService dbExecutor,
PluginConfig pluginConfig, Timer timer, Clock clock) {
this.db = db;
this.crypto = crypto;
this.dbExecutor = dbExecutor;
this.pluginConfig = pluginConfig;
this.timer = timer;
this.clock = clock;
// Use a ConcurrentHashMap as a thread-safe set
@@ -61,21 +65,29 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
@Override
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 {
Collection<Contact> contacts;
Map<TransportId, Integer> latencies;
Transaction txn = db.startTransaction();
Transaction txn = db.startTransaction(false);
try {
contacts = db.getContacts(txn);
latencies = db.getTransportLatencies(txn);
for (Contact c : db.getContacts(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();
} finally {
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) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
return false;
@@ -94,43 +106,49 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
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
if (!activeContacts.containsKey(c)) return null;
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);
if (m == null) return null;
StreamContext ctx = m.getStreamContext(tag);
if (ctx == null) return null;
// Activate the contact if not already active
if (!activeContacts.containsKey(ctx.getContactId())) {
try {
Transaction txn = db.startTransaction();
try {
db.setContactActive(txn, ctx.getContactId(), true);
txn.setComplete();
} finally {
db.endTransaction(txn);
}
} catch (DbException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
return null;
}
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, tag);
// Activate the contact if not already active
if (ctx != null && !activeContacts.containsKey(ctx.getContactId()))
db.setContactActive(txn, ctx.getContactId(), true);
txn.setComplete();
} finally {
db.endTransaction(txn);
}
return ctx;
}
public void eventOccurred(Event e) {
if (e instanceof TransportAddedEvent) {
TransportAddedEvent t = (TransportAddedEvent) e;
addTransport(t.getTransportId(), t.getMaxLatency());
} else if (e instanceof TransportRemovedEvent) {
removeTransport(((TransportRemovedEvent) e).getTransportId());
} else if (e instanceof ContactRemovedEvent) {
if (e instanceof ContactRemovedEvent) {
removeContact(((ContactRemovedEvent) e).getContactId());
} else if (e instanceof ContactStatusChangedEvent) {
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) {
activeContacts.remove(c);
dbExecutor.execute(new Runnable() {

View File

@@ -60,46 +60,20 @@ class TransportKeyManager {
keys = new HashMap<ContactId, MutableTransportKeys>();
}
void start() {
void start(Transaction txn) throws DbException {
long now = clock.currentTimeMillis();
lock.lock();
try {
// Load the transport keys from the DB
Map<ContactId, TransportKeys> loaded;
try {
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;
}
Map<ContactId, TransportKeys> loaded =
db.getTransportKeys(txn, transportId);
// Rotate the keys to the current rotation period
Map<ContactId, TransportKeys> rotated =
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);
}
RotationResult rotationResult = rotateKeys(loaded, now);
// Initialise mutable state for all contacts
for (Entry<ContactId, TransportKeys> e : current.entrySet())
addKeys(e.getKey(), new MutableTransportKeys(e.getValue()));
addKeys(rotationResult.current);
// Write any rotated keys back to the DB
updateTransportKeys(rotated);
} catch (DbException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
if (!rotationResult.rotated.isEmpty())
db.updateTransportKeys(txn, rotationResult.rotated);
} finally {
lock.unlock();
}
@@ -107,6 +81,27 @@ class TransportKeyManager {
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
private void addKeys(ContactId c, MutableTransportKeys m) {
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) {
TimerTask task = new TimerTask() {
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;
@@ -185,7 +178,8 @@ class TransportKeyManager {
}
}
StreamContext getStreamContext(ContactId c) {
StreamContext getStreamContext(Transaction txn, ContactId c)
throws DbException {
lock.lock();
try {
// Look up the outgoing keys for the contact
@@ -198,24 +192,16 @@ class TransportKeyManager {
outKeys.getStreamCounter());
// Increment the stream counter and write it back to the DB
outKeys.incrementStreamCounter();
Transaction txn = db.startTransaction();
try {
db.incrementStreamCounter(txn, c, transportId,
outKeys.getRotationPeriod());
txn.setComplete();
} finally {
db.endTransaction(txn);
}
db.incrementStreamCounter(txn, c, transportId,
outKeys.getRotationPeriod());
return ctx;
} catch (DbException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
return null;
} finally {
lock.unlock();
}
}
StreamContext getStreamContext(byte[] tag) {
StreamContext getStreamContext(Transaction txn, byte[] tag)
throws DbException {
lock.lock();
try {
// Look up the incoming keys for the tag
@@ -244,53 +230,33 @@ class TransportKeyManager {
inContexts.remove(new Bytes(removeTag));
}
// Write the window back to the DB
Transaction txn = db.startTransaction();
try {
db.setReorderingWindow(txn, tagCtx.contactId, transportId,
inKeys.getRotationPeriod(), window.getBase(),
window.getBitmap());
txn.setComplete();
} finally {
db.endTransaction(txn);
}
db.setReorderingWindow(txn, tagCtx.contactId, transportId,
inKeys.getRotationPeriod(), window.getBase(),
window.getBitmap());
return ctx;
} catch (DbException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
return null;
} finally {
lock.unlock();
}
}
private void rotateKeys() {
private void rotateKeys(Transaction txn) throws DbException {
long now = clock.currentTimeMillis();
lock.lock();
try {
// Rotate the keys to the current rotation period
Map<ContactId, TransportKeys> rotated =
Map<ContactId, TransportKeys> snapshot =
new HashMap<ContactId, TransportKeys>();
Map<ContactId, TransportKeys> current =
new HashMap<ContactId, TransportKeys>();
long rotationPeriod = now / rotationPeriodLength;
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);
}
for (Entry<ContactId, MutableTransportKeys> e : keys.entrySet())
snapshot.put(e.getKey(), e.getValue().snapshot());
RotationResult rotationResult = rotateKeys(snapshot, now);
// Rebuild the mutable state for all contacts
inContexts.clear();
outContexts.clear();
keys.clear();
for (Entry<ContactId, TransportKeys> e : current.entrySet())
addKeys(e.getKey(), new MutableTransportKeys(e.getValue()));
addKeys(rotationResult.current);
// Write any rotated keys back to the DB
updateTransportKeys(rotated);
} catch (DbException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
if (!rotationResult.rotated.isEmpty())
db.updateTransportKeys(txn, rotationResult.rotated);
} finally {
lock.unlock();
}
@@ -311,4 +277,14 @@ class TransportKeyManager {
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;
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.system.Clock;
import org.briarproject.util.OsUtils;
import javax.inject.Singleton;
@@ -15,22 +11,10 @@ import dagger.Provides;
@Module
public class DesktopLifecycleModule extends LifecycleModule {
@Provides
@Singleton
LifecycleManager provideLifecycleManager(Clock clock, DatabaseComponent db,
EventBus eventBus) {
return new LifecycleManagerImpl(clock, db, eventBus);
}
@Provides
@Singleton
ShutdownManager provideDesktopShutdownManager() {
if (OsUtils.isWindows()) {
return new WindowsShutdownManagerImpl();
}
else {
return new ShutdownManagerImpl();
}
if (OsUtils.isWindows()) return new WindowsShutdownManagerImpl();
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.ShutdownManager;
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.simplex.SimplexPluginConfig;
import org.briarproject.api.plugins.simplex.SimplexPluginFactory;
import org.briarproject.api.reliability.ReliabilityLayerFactory;
import org.briarproject.plugins.bluetooth.BluetoothPluginFactory;
@@ -27,21 +26,7 @@ import dagger.Provides;
public class DesktopPluginsModule extends PluginsModule {
@Provides
SimplexPluginConfig getSimplexPluginConfig(
@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,
PluginConfig getPluginConfig(@IoExecutor Executor ioExecutor,
SecureRandom random, BackoffFactory backoffFactory,
ReliabilityLayerFactory reliabilityFactory,
ShutdownManager shutdownManager) {
@@ -53,11 +38,22 @@ public class DesktopPluginsModule extends PluginsModule {
backoffFactory);
DuplexPluginFactory wan = new WanTcpPluginFactory(ioExecutor,
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);
return new DuplexPluginConfig() {
public Collection<DuplexPluginFactory> getFactories() {
return factories;
return new PluginConfig() {
@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;
}
public int getMaxLatency() {
return MAX_LATENCY;
}
public DuplexPlugin createPlugin(DuplexPluginCallback callback) {
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE);

View File

@@ -1,5 +1,10 @@
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.InputStream;
import java.io.OutputStream;
@@ -7,11 +12,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
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 {
private final Plugin plugin;
@@ -39,10 +39,6 @@ class BluetoothTransportConnection implements DuplexTransportConnection {
private class Reader implements TransportConnectionReader {
public long getMaxLatency() {
return plugin.getMaxLatency();
}
public InputStream getInputStream() throws IOException {
return stream.openInputStream();
}

View File

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

View File

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

View File

@@ -1,7 +1,5 @@
package org.briarproject.plugins.modem;
import java.util.concurrent.Executor;
import org.briarproject.api.TransportId;
import org.briarproject.api.plugins.duplex.DuplexPlugin;
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.util.StringUtils;
import java.util.concurrent.Executor;
public class ModemPluginFactory implements DuplexPluginFactory {
private static final int MAX_LATENCY = 30 * 1000; // 30 seconds
@@ -26,6 +26,10 @@ public class ModemPluginFactory implements DuplexPluginFactory {
return ModemPlugin.ID;
}
public int getMaxLatency() {
return MAX_LATENCY;
}
public DuplexPlugin createPlugin(DuplexPluginCallback callback) {
// This plugin is not enabled by default
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);
final ValidationManager validationManager =
context.mock(ValidationManager.class);
final Transaction txn = new Transaction(null);
final Transaction txn = new Transaction(null, false);
final byte[] body = new byte[123];
final Metadata groupMetadata = new Metadata();
final Metadata messageMetadata = new Metadata();
@@ -249,7 +249,7 @@ public class MessageQueueManagerImplTest extends BriarTestCase {
new AtomicReference<IncomingMessageHook>();
final IncomingQueueMessageHook incomingQueueMessageHook =
context.mock(IncomingQueueMessageHook.class);
final Transaction txn = new Transaction(null);
final Transaction txn = new Transaction(null, false);
final Metadata groupMetadata = new Metadata();
final byte[] queueState = new byte[123];
groupMetadata.put(QUEUE_STATE_KEY, queueState);
@@ -300,7 +300,7 @@ public class MessageQueueManagerImplTest extends BriarTestCase {
new AtomicReference<IncomingMessageHook>();
final IncomingQueueMessageHook incomingQueueMessageHook =
context.mock(IncomingQueueMessageHook.class);
final Transaction txn = new Transaction(null);
final Transaction txn = new Transaction(null, false);
final Metadata groupMetadata = new Metadata();
final byte[] queueState = new byte[123];
groupMetadata.put(QUEUE_STATE_KEY, queueState);
@@ -355,7 +355,7 @@ public class MessageQueueManagerImplTest extends BriarTestCase {
new AtomicReference<IncomingMessageHook>();
final IncomingQueueMessageHook incomingQueueMessageHook =
context.mock(IncomingQueueMessageHook.class);
final Transaction txn = new Transaction(null);
final Transaction txn = new Transaction(null, false);
final Metadata groupMetadata = new Metadata();
final byte[] queueState = new byte[123];
groupMetadata.put(QUEUE_STATE_KEY, queueState);
@@ -412,7 +412,7 @@ public class MessageQueueManagerImplTest extends BriarTestCase {
new AtomicReference<IncomingMessageHook>();
final IncomingQueueMessageHook incomingQueueMessageHook =
context.mock(IncomingQueueMessageHook.class);
final Transaction txn = new Transaction(null);
final Transaction txn = new Transaction(null, false);
final Metadata groupMetadata = new Metadata();
final byte[] queueState = new byte[123];
groupMetadata.put(QUEUE_STATE_KEY, queueState);

View File

@@ -192,7 +192,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
shutdown);
assertFalse(db.open());
Transaction transaction = db.startTransaction();
Transaction transaction = db.startTransaction(false);
try {
db.addLocalAuthor(transaction, localAuthor);
assertEquals(contactId,
@@ -233,7 +233,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown);
Transaction transaction = db.startTransaction();
Transaction transaction = db.startTransaction(false);
try {
db.addLocalMessage(transaction, message, clientId, metadata, true);
fail();
@@ -276,7 +276,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown);
Transaction transaction = db.startTransaction();
Transaction transaction = db.startTransaction(false);
try {
db.addLocalMessage(transaction, message, clientId, metadata, true);
transaction.setComplete();
@@ -306,7 +306,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown);
Transaction transaction = db.startTransaction();
Transaction transaction = db.startTransaction(false);
try {
db.addTransportKeys(transaction, contactId, createTransportKeys());
fail();
@@ -316,7 +316,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction);
}
transaction = db.startTransaction();
transaction = db.startTransaction(false);
try {
db.generateAck(transaction, contactId, 123);
fail();
@@ -326,7 +326,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction);
}
transaction = db.startTransaction();
transaction = db.startTransaction(false);
try {
db.generateBatch(transaction, contactId, 123, 456);
fail();
@@ -336,7 +336,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction);
}
transaction = db.startTransaction();
transaction = db.startTransaction(false);
try {
db.generateOffer(transaction, contactId, 123, 456);
fail();
@@ -346,7 +346,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction);
}
transaction = db.startTransaction();
transaction = db.startTransaction(false);
try {
db.generateRequest(transaction, contactId, 123);
fail();
@@ -356,7 +356,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction);
}
transaction = db.startTransaction();
transaction = db.startTransaction(false);
try {
db.getContact(transaction, contactId);
fail();
@@ -366,7 +366,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction);
}
transaction = db.startTransaction();
transaction = db.startTransaction(false);
try {
db.getMessageStatus(transaction, contactId, groupId);
fail();
@@ -376,7 +376,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction);
}
transaction = db.startTransaction();
transaction = db.startTransaction(false);
try {
db.getMessageStatus(transaction, contactId, messageId);
fail();
@@ -386,7 +386,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction);
}
transaction = db.startTransaction();
transaction = db.startTransaction(false);
try {
db.incrementStreamCounter(transaction, contactId, transportId, 0);
fail();
@@ -396,7 +396,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction);
}
transaction = db.startTransaction();
transaction = db.startTransaction(false);
try {
db.isVisibleToContact(transaction, contactId, groupId);
fail();
@@ -406,7 +406,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction);
}
transaction = db.startTransaction();
transaction = db.startTransaction(false);
try {
Ack a = new Ack(Collections.singletonList(messageId));
db.receiveAck(transaction, contactId, a);
@@ -417,7 +417,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction);
}
transaction = db.startTransaction();
transaction = db.startTransaction(false);
try {
db.receiveMessage(transaction, contactId, message);
fail();
@@ -427,7 +427,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction);
}
transaction = db.startTransaction();
transaction = db.startTransaction(false);
try {
Offer o = new Offer(Collections.singletonList(messageId));
db.receiveOffer(transaction, contactId, o);
@@ -438,7 +438,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction);
}
transaction = db.startTransaction();
transaction = db.startTransaction(false);
try {
Request r = new Request(Collections.singletonList(messageId));
db.receiveRequest(transaction, contactId, r);
@@ -449,7 +449,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction);
}
transaction = db.startTransaction();
transaction = db.startTransaction(false);
try {
db.removeContact(transaction, contactId);
fail();
@@ -459,7 +459,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction);
}
transaction = db.startTransaction();
transaction = db.startTransaction(false);
try {
db.setContactActive(transaction, contactId, true);
fail();
@@ -469,7 +469,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction);
}
transaction = db.startTransaction();
transaction = db.startTransaction(false);
try {
db.setReorderingWindow(transaction, contactId, transportId, 0, 0,
new byte[REORDERING_WINDOW_SIZE / 8]);
@@ -480,7 +480,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction);
}
transaction = db.startTransaction();
transaction = db.startTransaction(false);
try {
db.setVisibleToContact(transaction, contactId, groupId, true);
fail();
@@ -512,7 +512,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown);
Transaction transaction = db.startTransaction();
Transaction transaction = db.startTransaction(false);
try {
db.addContact(transaction, author, localAuthorId, true);
fail();
@@ -522,7 +522,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction);
}
transaction = db.startTransaction();
transaction = db.startTransaction(false);
try {
db.getLocalAuthor(transaction, localAuthorId);
fail();
@@ -532,7 +532,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction);
}
transaction = db.startTransaction();
transaction = db.startTransaction(false);
try {
db.removeLocalAuthor(transaction, localAuthorId);
fail();
@@ -568,7 +568,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown);
Transaction transaction = db.startTransaction();
Transaction transaction = db.startTransaction(false);
try {
db.getGroup(transaction, groupId);
fail();
@@ -578,7 +578,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction);
}
transaction = db.startTransaction();
transaction = db.startTransaction(false);
try {
db.getGroupMetadata(transaction, groupId);
fail();
@@ -588,7 +588,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction);
}
transaction = db.startTransaction();
transaction = db.startTransaction(false);
try {
db.getMessageStatus(transaction, contactId, groupId);
fail();
@@ -598,7 +598,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction);
}
transaction = db.startTransaction();
transaction = db.startTransaction(false);
try {
db.isVisibleToContact(transaction, contactId, groupId);
fail();
@@ -608,7 +608,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction);
}
transaction = db.startTransaction();
transaction = db.startTransaction(false);
try {
db.mergeGroupMetadata(transaction, groupId, metadata);
fail();
@@ -618,7 +618,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction);
}
transaction = db.startTransaction();
transaction = db.startTransaction(false);
try {
db.removeGroup(transaction, group);
fail();
@@ -628,7 +628,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction);
}
transaction = db.startTransaction();
transaction = db.startTransaction(false);
try {
db.setVisibleToContact(transaction, contactId, groupId, true);
fail();
@@ -663,7 +663,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown);
Transaction transaction = db.startTransaction();
Transaction transaction = db.startTransaction(false);
try {
db.deleteMessage(transaction, messageId);
fail();
@@ -673,7 +673,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction);
}
transaction = db.startTransaction();
transaction = db.startTransaction(false);
try {
db.deleteMessageMetadata(transaction, messageId);
fail();
@@ -683,7 +683,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction);
}
transaction = db.startTransaction();
transaction = db.startTransaction(false);
try {
db.getRawMessage(transaction, messageId);
fail();
@@ -693,7 +693,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction);
}
transaction = db.startTransaction();
transaction = db.startTransaction(false);
try {
db.getMessageMetadata(transaction, messageId);
fail();
@@ -703,7 +703,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction);
}
transaction = db.startTransaction();
transaction = db.startTransaction(false);
try {
db.getMessageStatus(transaction, contactId, messageId);
fail();
@@ -713,7 +713,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction);
}
transaction = db.startTransaction();
transaction = db.startTransaction(false);
try {
db.mergeMessageMetadata(transaction, messageId, metadata);
fail();
@@ -723,7 +723,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction);
}
transaction = db.startTransaction();
transaction = db.startTransaction(false);
try {
db.setMessageShared(transaction, message, true);
fail();
@@ -733,7 +733,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction);
}
transaction = db.startTransaction();
transaction = db.startTransaction(false);
try {
db.setMessageValid(transaction, message, clientId, true);
fail();
@@ -787,7 +787,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown);
Transaction transaction = db.startTransaction();
Transaction transaction = db.startTransaction(false);
try {
db.addLocalAuthor(transaction, localAuthor);
assertEquals(contactId,
@@ -797,7 +797,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction);
}
transaction = db.startTransaction();
transaction = db.startTransaction(false);
try {
db.getTransportKeys(transaction, transportId);
fail();
@@ -807,7 +807,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction);
}
transaction = db.startTransaction();
transaction = db.startTransaction(false);
try {
db.incrementStreamCounter(transaction, contactId, transportId, 0);
fail();
@@ -817,7 +817,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction);
}
transaction = db.startTransaction();
transaction = db.startTransaction(false);
try {
db.removeTransport(transaction, transportId);
fail();
@@ -827,7 +827,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction);
}
transaction = db.startTransaction();
transaction = db.startTransaction(false);
try {
db.setReorderingWindow(transaction, contactId, transportId, 0, 0,
new byte[REORDERING_WINDOW_SIZE / 8]);
@@ -863,7 +863,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown);
Transaction transaction = db.startTransaction();
Transaction transaction = db.startTransaction(false);
try {
Ack a = db.generateAck(transaction, contactId, 123);
assertEquals(messagesToAck, a.getMessageIds());
@@ -907,7 +907,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown);
Transaction transaction = db.startTransaction();
Transaction transaction = db.startTransaction(false);
try {
assertEquals(messages, db.generateBatch(transaction, contactId,
size * 2, maxLatency));
@@ -944,7 +944,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown);
Transaction transaction = db.startTransaction();
Transaction transaction = db.startTransaction(false);
try {
Offer o = db.generateOffer(transaction, contactId, 123, maxLatency);
assertEquals(ids, o.getMessageIds());
@@ -978,7 +978,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown);
Transaction transaction = db.startTransaction();
Transaction transaction = db.startTransaction(false);
try {
Request r = db.generateRequest(transaction, contactId, 123);
assertEquals(ids, r.getMessageIds());
@@ -1023,7 +1023,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown);
Transaction transaction = db.startTransaction();
Transaction transaction = db.startTransaction(false);
try {
assertEquals(messages, db.generateRequestedBatch(transaction,
contactId, size * 2, maxLatency));
@@ -1056,7 +1056,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown);
Transaction transaction = db.startTransaction();
Transaction transaction = db.startTransaction(false);
try {
Ack a = new Ack(Collections.singletonList(messageId));
db.receiveAck(transaction, contactId, a);
@@ -1099,7 +1099,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown);
Transaction transaction = db.startTransaction();
Transaction transaction = db.startTransaction(false);
try {
db.receiveMessage(transaction, contactId, message);
transaction.setComplete();
@@ -1135,7 +1135,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown);
Transaction transaction = db.startTransaction();
Transaction transaction = db.startTransaction(false);
try {
db.receiveMessage(transaction, contactId, message);
transaction.setComplete();
@@ -1165,7 +1165,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown);
Transaction transaction = db.startTransaction();
Transaction transaction = db.startTransaction(false);
try {
db.receiveMessage(transaction, contactId, message);
transaction.setComplete();
@@ -1217,7 +1217,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown);
Transaction transaction = db.startTransaction();
Transaction transaction = db.startTransaction(false);
try {
Offer o = new Offer(Arrays.asList(messageId, messageId1,
messageId2, messageId3));
@@ -1252,7 +1252,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown);
Transaction transaction = db.startTransaction();
Transaction transaction = db.startTransaction(false);
try {
Request r = new Request(Collections.singletonList(messageId));
db.receiveRequest(transaction, contactId, r);
@@ -1293,7 +1293,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown);
Transaction transaction = db.startTransaction();
Transaction transaction = db.startTransaction(false);
try {
db.setVisibleToContact(transaction, contactId, groupId, true);
transaction.setComplete();
@@ -1326,7 +1326,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown);
Transaction transaction = db.startTransaction();
Transaction transaction = db.startTransaction(false);
try {
db.setVisibleToContact(transaction, contactId, groupId, true);
transaction.setComplete();
@@ -1368,7 +1368,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown);
Transaction transaction = db.startTransaction();
Transaction transaction = db.startTransaction(false);
try {
db.updateTransportKeys(transaction, keys);
assertEquals(keys, db.getTransportKeys(transaction, transportId));
@@ -1434,7 +1434,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown);
Transaction transaction = db.startTransaction();
Transaction transaction = db.startTransaction(false);
try {
// First merge should broadcast an event
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.api.TransportId;
import org.briarproject.api.db.DatabaseComponent;
import org.briarproject.api.db.Transaction;
import org.briarproject.api.event.EventBus;
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.DuplexPluginCallback;
import org.briarproject.api.plugins.duplex.DuplexPluginConfig;
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.SimplexPluginConfig;
import org.briarproject.api.plugins.simplex.SimplexPluginFactory;
import org.briarproject.api.properties.TransportPropertyManager;
import org.briarproject.api.settings.SettingsManager;
import org.briarproject.api.system.Clock;
import org.briarproject.api.ui.UiCallback;
import org.briarproject.system.SystemClock;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.jmock.lib.concurrent.Synchroniser;
@@ -34,17 +29,12 @@ public class PluginManagerImplTest extends BriarTestCase {
@Test
public void testStartAndStop() throws Exception {
Clock clock = new SystemClock();
Mockery context = new Mockery() {{
setThreadingPolicy(new Synchroniser());
}};
final Executor ioExecutor = Executors.newSingleThreadExecutor();
final EventBus eventBus = context.mock(EventBus.class);
final SimplexPluginConfig simplexPluginConfig =
context.mock(SimplexPluginConfig.class);
final DuplexPluginConfig duplexPluginConfig =
context.mock(DuplexPluginConfig.class);
final DatabaseComponent db = context.mock(DatabaseComponent.class);
final PluginConfig pluginConfig = context.mock(PluginConfig.class);
final Poller poller = context.mock(Poller.class);
final ConnectionManager connectionManager =
context.mock(ConnectionManager.class);
@@ -53,33 +43,30 @@ public class PluginManagerImplTest extends BriarTestCase {
final TransportPropertyManager transportPropertyManager =
context.mock(TransportPropertyManager.class);
final UiCallback uiCallback = context.mock(UiCallback.class);
// Two simplex plugin factories: both create plugins, one fails to start
final SimplexPluginFactory simplexFactory =
context.mock(SimplexPluginFactory.class);
final SimplexPlugin simplexPlugin = context.mock(SimplexPlugin.class);
final TransportId simplexId = new TransportId("simplex");
final int simplexLatency = 12345;
final Transaction simplexTxn = new Transaction(null);
final SimplexPluginFactory simplexFailFactory =
context.mock(SimplexPluginFactory.class, "simplexFailFactory");
final SimplexPlugin simplexFailPlugin =
context.mock(SimplexPlugin.class, "simplexFailPlugin");
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
final DuplexPluginFactory duplexFactory =
context.mock(DuplexPluginFactory.class);
final DuplexPlugin duplexPlugin = context.mock(DuplexPlugin.class);
final TransportId duplexId = new TransportId("duplex");
final int duplexLatency = 34567;
final Transaction duplexTxn = new Transaction(null);
final DuplexPluginFactory duplexFailFactory =
context.mock(DuplexPluginFactory.class, "duplexFailFactory");
final TransportId duplexFailId = new TransportId("duplex1");
context.checking(new Expectations() {{
// First simplex plugin
oneOf(simplexPluginConfig).getFactories();
oneOf(pluginConfig).getSimplexFactories();
will(returnValue(Arrays.asList(simplexFactory,
simplexFailFactory)));
oneOf(simplexFactory).getId();
@@ -87,12 +74,6 @@ public class PluginManagerImplTest extends BriarTestCase {
oneOf(simplexFactory).createPlugin(with(any(
SimplexPluginCallback.class)));
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();
will(returnValue(true)); // Started
oneOf(simplexPlugin).shouldPoll();
@@ -104,29 +85,16 @@ public class PluginManagerImplTest extends BriarTestCase {
oneOf(simplexFailFactory).createPlugin(with(any(
SimplexPluginCallback.class)));
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();
will(returnValue(false)); // Failed to start
// First duplex plugin
oneOf(duplexPluginConfig).getFactories();
oneOf(pluginConfig).getDuplexFactories();
will(returnValue(Arrays.asList(duplexFactory, duplexFailFactory)));
oneOf(duplexFactory).getId();
will(returnValue(duplexId));
oneOf(duplexFactory).createPlugin(with(any(
DuplexPluginCallback.class)));
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();
will(returnValue(true)); // Started
oneOf(duplexPlugin).shouldPoll();
@@ -143,14 +111,15 @@ public class PluginManagerImplTest extends BriarTestCase {
oneOf(simplexPlugin).stop();
oneOf(duplexPlugin).stop();
}});
PluginManagerImpl p = new PluginManagerImpl(ioExecutor, eventBus,
simplexPluginConfig, duplexPluginConfig, clock, db, poller,
connectionManager, settingsManager, transportPropertyManager,
uiCallback);
pluginConfig, poller, connectionManager, settingsManager,
transportPropertyManager, uiCallback);
// Two plugins should be started and stopped
assertTrue(p.start());
assertTrue(p.stop());
context.assertIsSatisfied();
}
}

View File

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

View File

@@ -58,20 +58,20 @@ public class ValidationManagerImplTest extends BriarTestCase {
final MessageValidator validator = context.mock(MessageValidator.class);
final IncomingMessageHook hook =
context.mock(IncomingMessageHook.class);
final Transaction txn = new Transaction(null);
final Transaction txn1 = new Transaction(null);
final Transaction txn2 = new Transaction(null);
final Transaction txn3 = new Transaction(null);
final Transaction txn4 = new Transaction(null);
final Transaction txn = new Transaction(null, false);
final Transaction txn1 = new Transaction(null, false);
final Transaction txn2 = new Transaction(null, false);
final Transaction txn3 = new Transaction(null, false);
final Transaction txn4 = new Transaction(null, false);
context.checking(new Expectations() {{
// Get messages to validate
oneOf(db).startTransaction();
oneOf(db).startTransaction(true);
will(returnValue(txn));
oneOf(db).getMessagesToValidate(txn, clientId);
will(returnValue(Arrays.asList(messageId, messageId1)));
oneOf(db).endTransaction(txn);
// Load the first raw message and group
oneOf(db).startTransaction();
oneOf(db).startTransaction(true);
will(returnValue(txn1));
oneOf(db).getRawMessage(txn1, messageId);
will(returnValue(raw));
@@ -82,7 +82,7 @@ public class ValidationManagerImplTest extends BriarTestCase {
oneOf(validator).validateMessage(message, group);
will(returnValue(metadata));
// Store the validation result for the first message
oneOf(db).startTransaction();
oneOf(db).startTransaction(false);
will(returnValue(txn2));
oneOf(db).mergeMessageMetadata(txn2, messageId, metadata);
oneOf(db).setMessageValid(txn2, message, clientId, true);
@@ -91,7 +91,7 @@ public class ValidationManagerImplTest extends BriarTestCase {
oneOf(hook).incomingMessage(txn2, message, metadata);
oneOf(db).endTransaction(txn2);
// Load the second raw message and group
oneOf(db).startTransaction();
oneOf(db).startTransaction(true);
will(returnValue(txn3));
oneOf(db).getRawMessage(txn3, messageId1);
will(returnValue(raw));
@@ -102,7 +102,7 @@ public class ValidationManagerImplTest extends BriarTestCase {
oneOf(validator).validateMessage(message1, group);
will(returnValue(null));
// Store the validation result for the second message
oneOf(db).startTransaction();
oneOf(db).startTransaction(false);
will(returnValue(txn4));
oneOf(db).setMessageValid(txn4, message1, clientId, false);
oneOf(db).endTransaction(txn4);
@@ -127,25 +127,25 @@ public class ValidationManagerImplTest extends BriarTestCase {
final MessageValidator validator = context.mock(MessageValidator.class);
final IncomingMessageHook hook =
context.mock(IncomingMessageHook.class);
final Transaction txn = new Transaction(null);
final Transaction txn1 = new Transaction(null);
final Transaction txn2 = new Transaction(null);
final Transaction txn3 = new Transaction(null);
final Transaction txn = new Transaction(null, true);
final Transaction txn1 = new Transaction(null, true);
final Transaction txn2 = new Transaction(null, true);
final Transaction txn3 = new Transaction(null, false);
context.checking(new Expectations() {{
// Get messages to validate
oneOf(db).startTransaction();
oneOf(db).startTransaction(true);
will(returnValue(txn));
oneOf(db).getMessagesToValidate(txn, clientId);
will(returnValue(Arrays.asList(messageId, messageId1)));
oneOf(db).endTransaction(txn);
// Load the first raw message - *gasp* it's gone!
oneOf(db).startTransaction();
oneOf(db).startTransaction(true);
will(returnValue(txn1));
oneOf(db).getRawMessage(txn1, messageId);
will(throwException(new NoSuchMessageException()));
oneOf(db).endTransaction(txn1);
// Load the second raw message and group
oneOf(db).startTransaction();
oneOf(db).startTransaction(true);
will(returnValue(txn2));
oneOf(db).getRawMessage(txn2, messageId1);
will(returnValue(raw));
@@ -156,7 +156,7 @@ public class ValidationManagerImplTest extends BriarTestCase {
oneOf(validator).validateMessage(message1, group);
will(returnValue(null));
// Store the validation result for the second message
oneOf(db).startTransaction();
oneOf(db).startTransaction(false);
will(returnValue(txn3));
oneOf(db).setMessageValid(txn3, message1, clientId, false);
oneOf(db).endTransaction(txn3);
@@ -181,19 +181,19 @@ public class ValidationManagerImplTest extends BriarTestCase {
final MessageValidator validator = context.mock(MessageValidator.class);
final IncomingMessageHook hook =
context.mock(IncomingMessageHook.class);
final Transaction txn = new Transaction(null);
final Transaction txn1 = new Transaction(null);
final Transaction txn2 = new Transaction(null);
final Transaction txn3 = new Transaction(null);
final Transaction txn = new Transaction(null, true);
final Transaction txn1 = new Transaction(null, true);
final Transaction txn2 = new Transaction(null, true);
final Transaction txn3 = new Transaction(null, false);
context.checking(new Expectations() {{
// Get messages to validate
oneOf(db).startTransaction();
oneOf(db).startTransaction(true);
will(returnValue(txn));
oneOf(db).getMessagesToValidate(txn, clientId);
will(returnValue(Arrays.asList(messageId, messageId1)));
oneOf(db).endTransaction(txn);
// Load the first raw message
oneOf(db).startTransaction();
oneOf(db).startTransaction(true);
will(returnValue(txn1));
oneOf(db).getRawMessage(txn1, messageId);
will(returnValue(raw));
@@ -202,7 +202,7 @@ public class ValidationManagerImplTest extends BriarTestCase {
will(throwException(new NoSuchGroupException()));
oneOf(db).endTransaction(txn1);
// Load the second raw message and group
oneOf(db).startTransaction();
oneOf(db).startTransaction(true);
will(returnValue(txn2));
oneOf(db).getRawMessage(txn2, messageId1);
will(returnValue(raw));
@@ -213,7 +213,7 @@ public class ValidationManagerImplTest extends BriarTestCase {
oneOf(validator).validateMessage(message1, group);
will(returnValue(null));
// Store the validation result for the second message
oneOf(db).startTransaction();
oneOf(db).startTransaction(false);
will(returnValue(txn3));
oneOf(db).setMessageValid(txn3, message1, clientId, false);
oneOf(db).endTransaction(txn3);
@@ -237,11 +237,11 @@ public class ValidationManagerImplTest extends BriarTestCase {
final MessageValidator validator = context.mock(MessageValidator.class);
final IncomingMessageHook hook =
context.mock(IncomingMessageHook.class);
final Transaction txn = new Transaction(null);
final Transaction txn1 = new Transaction(null);
final Transaction txn = new Transaction(null, true);
final Transaction txn1 = new Transaction(null, false);
context.checking(new Expectations() {{
// Load the group
oneOf(db).startTransaction();
oneOf(db).startTransaction(true);
will(returnValue(txn));
oneOf(db).getGroup(txn, groupId);
will(returnValue(group));
@@ -250,7 +250,7 @@ public class ValidationManagerImplTest extends BriarTestCase {
oneOf(validator).validateMessage(message, group);
will(returnValue(metadata));
// Store the validation result
oneOf(db).startTransaction();
oneOf(db).startTransaction(false);
will(returnValue(txn1));
oneOf(db).mergeMessageMetadata(txn1, messageId, metadata);
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.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
public class TransportKeyManagerTest extends BriarTestCase {
@@ -57,7 +58,7 @@ public class TransportKeyManagerTest extends BriarTestCase {
final CryptoComponent crypto = context.mock(CryptoComponent.class);
final Timer timer = context.mock(Timer.class);
final Clock clock = context.mock(Clock.class);
final Transaction txn = new Transaction(null);
final Map<ContactId, TransportKeys> loaded =
new LinkedHashMap<ContactId, TransportKeys>();
final TransportKeys shouldRotate = createTransportKeys(900, 0);
@@ -65,17 +66,15 @@ public class TransportKeyManagerTest extends BriarTestCase {
loaded.put(contactId, shouldRotate);
loaded.put(contactId1, shouldNotRotate);
final TransportKeys rotated = createTransportKeys(1000, 0);
final Transaction txn1 = new Transaction(null);
final Transaction txn = new Transaction(null, false);
context.checking(new Expectations() {{
// Get the current time (1 ms after start of rotation period 1000)
oneOf(clock).currentTimeMillis();
will(returnValue(rotationPeriodLength * 1000 + 1));
// Load the transport keys
oneOf(db).startTransaction();
will(returnValue(txn));
oneOf(db).getTransportKeys(txn, transportId);
will(returnValue(loaded));
oneOf(db).endTransaction(txn);
// Rotate the transport keys
oneOf(crypto).rotateTransportKeys(shouldRotate, 1000);
will(returnValue(rotated));
@@ -88,11 +87,8 @@ public class TransportKeyManagerTest extends BriarTestCase {
will(new EncodeTagAction());
}
// Save the keys that were rotated
oneOf(db).startTransaction();
will(returnValue(txn1));
oneOf(db).updateTransportKeys(txn1,
oneOf(db).updateTransportKeys(txn,
Collections.singletonMap(contactId, rotated));
oneOf(db).endTransaction(txn1);
// Schedule key rotation at the start of the next rotation period
oneOf(timer).schedule(with(any(TimerTask.class)),
with(rotationPeriodLength - 1));
@@ -100,7 +96,7 @@ public class TransportKeyManagerTest extends BriarTestCase {
TransportKeyManager transportKeyManager = new TransportKeyManager(db,
crypto, timer, clock, transportId, maxLatency);
transportKeyManager.start();
transportKeyManager.start(txn);
context.assertIsSatisfied();
}
@@ -112,10 +108,12 @@ public class TransportKeyManagerTest extends BriarTestCase {
final CryptoComponent crypto = context.mock(CryptoComponent.class);
final Timer timer = context.mock(Timer.class);
final Clock clock = context.mock(Clock.class);
final boolean alice = true;
final TransportKeys transportKeys = createTransportKeys(999, 0);
final TransportKeys rotated = createTransportKeys(1000, 0);
final Transaction txn = new Transaction(null);
final Transaction txn = new Transaction(null, false);
context.checking(new Expectations() {{
oneOf(crypto).deriveTransportKeys(transportId, masterKey, 999,
alice);
@@ -155,9 +153,11 @@ public class TransportKeyManagerTest extends BriarTestCase {
final Timer timer = context.mock(Timer.class);
final Clock clock = context.mock(Clock.class);
final Transaction txn = new Transaction(null, false);
TransportKeyManager transportKeyManager = new TransportKeyManager(db,
crypto, timer, clock, transportId, maxLatency);
assertNull(transportKeyManager.getStreamContext(contactId));
assertNull(transportKeyManager.getStreamContext(txn, contactId));
context.assertIsSatisfied();
}
@@ -170,11 +170,13 @@ public class TransportKeyManagerTest extends BriarTestCase {
final CryptoComponent crypto = context.mock(CryptoComponent.class);
final Timer timer = context.mock(Timer.class);
final Clock clock = context.mock(Clock.class);
final boolean alice = true;
// The stream counter has been exhausted
final TransportKeys transportKeys = createTransportKeys(1000,
MAX_32_BIT_UNSIGNED + 1);
final Transaction txn = new Transaction(null);
final Transaction txn = new Transaction(null, false);
context.checking(new Expectations() {{
oneOf(crypto).deriveTransportKeys(transportId, masterKey, 1000,
alice);
@@ -201,7 +203,7 @@ public class TransportKeyManagerTest extends BriarTestCase {
long timestamp = rotationPeriodLength * 1000;
transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
alice);
assertNull(transportKeyManager.getStreamContext(contactId));
assertNull(transportKeyManager.getStreamContext(txn, contactId));
context.assertIsSatisfied();
}
@@ -213,12 +215,13 @@ public class TransportKeyManagerTest extends BriarTestCase {
final CryptoComponent crypto = context.mock(CryptoComponent.class);
final Timer timer = context.mock(Timer.class);
final Clock clock = context.mock(Clock.class);
final boolean alice = true;
// The stream counter can be used one more time before being exhausted
final TransportKeys transportKeys = createTransportKeys(1000,
MAX_32_BIT_UNSIGNED);
final Transaction txn = new Transaction(null);
final Transaction txn1 = new Transaction(null);
final Transaction txn = new Transaction(null, false);
context.checking(new Expectations() {{
oneOf(crypto).deriveTransportKeys(transportId, masterKey, 1000,
alice);
@@ -238,11 +241,7 @@ public class TransportKeyManagerTest extends BriarTestCase {
// Save the keys
oneOf(db).addTransportKeys(txn, contactId, transportKeys);
// Increment the stream counter
oneOf(db).startTransaction();
will(returnValue(txn1));
oneOf(db).incrementStreamCounter(txn1, contactId, transportId,
1000);
oneOf(db).endTransaction(txn1);
oneOf(db).incrementStreamCounter(txn, contactId, transportId, 1000);
}});
TransportKeyManager transportKeyManager = new TransportKeyManager(db,
@@ -252,7 +251,8 @@ public class TransportKeyManagerTest extends BriarTestCase {
transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
alice);
// The first request should return a stream context
StreamContext ctx = transportKeyManager.getStreamContext(contactId);
StreamContext ctx = transportKeyManager.getStreamContext(txn,
contactId);
assertNotNull(ctx);
assertEquals(contactId, ctx.getContactId());
assertEquals(transportId, ctx.getTransportId());
@@ -260,7 +260,7 @@ public class TransportKeyManagerTest extends BriarTestCase {
assertEquals(headerKey, ctx.getHeaderKey());
assertEquals(MAX_32_BIT_UNSIGNED, ctx.getStreamNumber());
// The second request should return null, the counter is exhausted
assertNull(transportKeyManager.getStreamContext(contactId));
assertNull(transportKeyManager.getStreamContext(txn, contactId));
context.assertIsSatisfied();
}
@@ -273,9 +273,11 @@ public class TransportKeyManagerTest extends BriarTestCase {
final CryptoComponent crypto = context.mock(CryptoComponent.class);
final Timer timer = context.mock(Timer.class);
final Clock clock = context.mock(Clock.class);
final boolean alice = true;
final TransportKeys transportKeys = createTransportKeys(1000, 0);
final Transaction txn = new Transaction(null);
final Transaction txn = new Transaction(null, false);
context.checking(new Expectations() {{
oneOf(crypto).deriveTransportKeys(transportId, masterKey, 1000,
alice);
@@ -302,7 +304,8 @@ public class TransportKeyManagerTest extends BriarTestCase {
long timestamp = rotationPeriodLength * 1000;
transportKeyManager.addContact(txn, contactId, masterKey, timestamp,
alice);
assertNull(transportKeyManager.getStreamContext(new byte[TAG_LENGTH]));
assertNull(transportKeyManager.getStreamContext(txn,
new byte[TAG_LENGTH]));
context.assertIsSatisfied();
}
@@ -314,12 +317,13 @@ public class TransportKeyManagerTest extends BriarTestCase {
final CryptoComponent crypto = context.mock(CryptoComponent.class);
final Timer timer = context.mock(Timer.class);
final Clock clock = context.mock(Clock.class);
final boolean alice = true;
final TransportKeys transportKeys = createTransportKeys(1000, 0);
final Transaction txn = new Transaction(null);
final Transaction txn1 = new Transaction(null);
// Keep a copy of the tags
final List<byte[]> tags = new ArrayList<byte[]>();
final Transaction txn = new Transaction(null, false);
context.checking(new Expectations() {{
oneOf(crypto).deriveTransportKeys(transportId, masterKey, 1000,
alice);
@@ -343,11 +347,8 @@ public class TransportKeyManagerTest extends BriarTestCase {
with(tagKey), with((long) REORDERING_WINDOW_SIZE));
will(new EncodeTagAction(tags));
// Save the reordering window (previous rotation period, base 1)
oneOf(db).startTransaction();
will(returnValue(txn1));
oneOf(db).setReorderingWindow(txn1, contactId, transportId, 999,
oneOf(db).setReorderingWindow(txn, contactId, transportId, 999,
1, new byte[REORDERING_WINDOW_SIZE / 8]);
oneOf(db).endTransaction(txn1);
}});
TransportKeyManager transportKeyManager = new TransportKeyManager(db,
@@ -360,7 +361,7 @@ public class TransportKeyManagerTest extends BriarTestCase {
assertEquals(REORDERING_WINDOW_SIZE * 3, tags.size());
byte[] tag = tags.get(0);
// The first request should return a stream context
StreamContext ctx = transportKeyManager.getStreamContext(tag);
StreamContext ctx = transportKeyManager.getStreamContext(txn, tag);
assertNotNull(ctx);
assertEquals(contactId, ctx.getContactId());
assertEquals(transportId, ctx.getTransportId());
@@ -370,7 +371,7 @@ public class TransportKeyManagerTest extends BriarTestCase {
// Another tag should have been encoded
assertEquals(REORDERING_WINDOW_SIZE * 3 + 1, tags.size());
// The second request should return null, the tag has already been used
assertNull(transportKeyManager.getStreamContext(tag));
assertNull(transportKeyManager.getStreamContext(txn, tag));
context.assertIsSatisfied();
}
@@ -382,22 +383,21 @@ public class TransportKeyManagerTest extends BriarTestCase {
final CryptoComponent crypto = context.mock(CryptoComponent.class);
final Timer timer = context.mock(Timer.class);
final Clock clock = context.mock(Clock.class);
final Transaction txn = new Transaction(null);
final TransportKeys transportKeys = createTransportKeys(1000, 0);
final Map<ContactId, TransportKeys> loaded =
Collections.singletonMap(contactId, transportKeys);
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() {{
// Get the current time (the start of rotation period 1000)
oneOf(clock).currentTimeMillis();
will(returnValue(rotationPeriodLength * 1000));
// Load the transport keys
oneOf(db).startTransaction();
will(returnValue(txn));
oneOf(db).getTransportKeys(txn, transportId);
will(returnValue(loaded));
oneOf(db).endTransaction(txn);
// Rotate the transport keys (the keys are unaffected)
oneOf(crypto).rotateTransportKeys(transportKeys, 1000);
will(returnValue(transportKeys));
@@ -411,6 +411,9 @@ public class TransportKeyManagerTest extends BriarTestCase {
oneOf(timer).schedule(with(any(TimerTask.class)),
with(rotationPeriodLength));
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)
oneOf(clock).currentTimeMillis();
will(returnValue(rotationPeriodLength * 1001));
@@ -425,19 +428,19 @@ public class TransportKeyManagerTest extends BriarTestCase {
will(new EncodeTagAction());
}
// Save the keys that were rotated
oneOf(db).startTransaction();
will(returnValue(txn1));
oneOf(db).updateTransportKeys(txn1,
Collections.singletonMap(contactId, rotated));
oneOf(db).endTransaction(txn1);
// Schedule key rotation at the start of the next rotation period
oneOf(timer).schedule(with(any(TimerTask.class)),
with(rotationPeriodLength));
// Commit the key rotation transaction
oneOf(db).endTransaction(txn1);
}});
TransportKeyManager transportKeyManager = new TransportKeyManager(db,
crypto, timer, clock, transportId, maxLatency);
transportKeyManager.start();
transportKeyManager.start(txn);
assertTrue(txn1.isComplete());
context.assertIsSatisfied();
}