mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 18:59:06 +01:00
Moved transactions out of database component.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package org.briarproject.api.contact;
|
||||
|
||||
import org.briarproject.api.db.DbException;
|
||||
import org.briarproject.api.db.Transaction;
|
||||
import org.briarproject.api.identity.Author;
|
||||
import org.briarproject.api.identity.AuthorId;
|
||||
|
||||
@@ -30,10 +31,10 @@ public interface ContactManager {
|
||||
void removeContact(ContactId c) throws DbException;
|
||||
|
||||
interface AddContactHook {
|
||||
void addingContact(Contact c);
|
||||
void addingContact(Transaction txn, Contact c) throws DbException;
|
||||
}
|
||||
|
||||
interface RemoveContactHook {
|
||||
void removingContact(Contact c);
|
||||
void removingContact(Transaction txn, Contact c) throws DbException;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,72 +26,85 @@ import java.util.Map;
|
||||
/**
|
||||
* Encapsulates the database implementation and exposes high-level operations
|
||||
* to other components.
|
||||
* <p>
|
||||
* <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 {
|
||||
|
||||
/** Opens the database and returns true if the database already existed. */
|
||||
/**
|
||||
* Opens the database and returns true if the database already existed.
|
||||
*/
|
||||
boolean open() throws DbException;
|
||||
|
||||
/** Waits for any open transactions to finish and closes the database. */
|
||||
/**
|
||||
* Waits for any open transactions to finish and closes the database.
|
||||
*/
|
||||
void close() throws DbException, IOException;
|
||||
|
||||
/** Starts a new transaction and returns an object representing it. */
|
||||
/**
|
||||
* Starts a new transaction and returns an object representing it.
|
||||
*/
|
||||
Transaction startTransaction() throws DbException;
|
||||
|
||||
/**
|
||||
* Aborts the given transaction - no changes made during the transaction
|
||||
* will be applied to the database.
|
||||
* Ends a transaction. If the transaction's
|
||||
* {@link Transaction#setComplete() commit} flag is set, the
|
||||
* transaction is committed, otherwise it is aborted.
|
||||
*/
|
||||
void abortTransaction(Transaction txn);
|
||||
|
||||
/**
|
||||
* Commits the given transaction - all changes made during the transaction
|
||||
* will be applied to the database.
|
||||
*/
|
||||
void commitTransaction(Transaction txn) throws DbException;
|
||||
void endTransaction(Transaction txn) throws DbException;
|
||||
|
||||
/**
|
||||
* Stores a contact associated with the given local and remote pseudonyms,
|
||||
* and returns an ID for the contact.
|
||||
*/
|
||||
ContactId addContact(Author remote, AuthorId local) throws DbException;
|
||||
|
||||
/** Stores a group. */
|
||||
void addGroup(Group g) throws DbException;
|
||||
|
||||
/** Stores a local pseudonym. */
|
||||
void addLocalAuthor(LocalAuthor a) throws DbException;
|
||||
|
||||
/** Stores a local message. */
|
||||
void addLocalMessage(Message m, ClientId c, Metadata meta, boolean shared)
|
||||
ContactId addContact(Transaction txn, Author remote, AuthorId local)
|
||||
throws DbException;
|
||||
|
||||
/** Stores a transport. */
|
||||
void addTransport(TransportId t, int maxLatency) throws DbException;
|
||||
/**
|
||||
* Stores a group.
|
||||
*/
|
||||
void addGroup(Transaction txn, Group g) throws DbException;
|
||||
|
||||
/**
|
||||
* Stores a local pseudonym.
|
||||
*/
|
||||
void addLocalAuthor(Transaction txn, LocalAuthor a) throws DbException;
|
||||
|
||||
/**
|
||||
* Stores a local message.
|
||||
*/
|
||||
void addLocalMessage(Transaction txn, Message m, ClientId c, Metadata meta,
|
||||
boolean shared) throws DbException;
|
||||
|
||||
/**
|
||||
* Stores a transport.
|
||||
*/
|
||||
void addTransport(Transaction txn, TransportId t, int maxLatency)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Stores transport keys for a newly added contact.
|
||||
*/
|
||||
void addTransportKeys(ContactId c, TransportKeys k) throws DbException;
|
||||
void addTransportKeys(Transaction txn, ContactId c, TransportKeys k)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Deletes the message with the given ID. The message ID and any other
|
||||
* associated data are not deleted.
|
||||
*/
|
||||
void deleteMessage(MessageId m) throws DbException;
|
||||
void deleteMessage(Transaction txn, MessageId m) throws DbException;
|
||||
|
||||
/** Deletes any metadata associated with the given message. */
|
||||
void deleteMessageMetadata(MessageId m) throws DbException;
|
||||
void deleteMessageMetadata(Transaction txn, MessageId m) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns an acknowledgement for the given contact, or null if there are
|
||||
* no messages to acknowledge.
|
||||
*/
|
||||
Ack generateAck(ContactId c, int maxMessages) throws DbException;
|
||||
Ack generateAck(Transaction txn, ContactId c, int maxMessages)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Returns a batch of raw messages for the given contact, with a total
|
||||
@@ -99,22 +112,23 @@ public interface DatabaseComponent {
|
||||
* transport with the given maximum latency. Returns null if there are no
|
||||
* sendable messages that fit in the given length.
|
||||
*/
|
||||
Collection<byte[]> generateBatch(ContactId c, int maxLength,
|
||||
int maxLatency) throws DbException;
|
||||
Collection<byte[]> generateBatch(Transaction txn, ContactId c,
|
||||
int maxLength, int maxLatency) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns an offer for the given contact for transmission over a
|
||||
* transport with the given maximum latency, or null if there are no
|
||||
* messages to offer.
|
||||
*/
|
||||
Offer generateOffer(ContactId c, int maxMessages, int maxLatency)
|
||||
throws DbException;
|
||||
Offer generateOffer(Transaction txn, ContactId c, int maxMessages,
|
||||
int maxLatency) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns a request for the given contact, or null if there are no
|
||||
* messages to request.
|
||||
*/
|
||||
Request generateRequest(ContactId c, int maxMessages) throws DbException;
|
||||
Request generateRequest(Transaction txn, ContactId c, int maxMessages)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Returns a batch of raw messages for the given contact, with a total
|
||||
@@ -123,158 +137,226 @@ public interface DatabaseComponent {
|
||||
* requested by the contact are returned. Returns null if there are no
|
||||
* sendable messages that fit in the given length.
|
||||
*/
|
||||
Collection<byte[]> generateRequestedBatch(ContactId c, int maxLength,
|
||||
int maxLatency) throws DbException;
|
||||
Collection<byte[]> generateRequestedBatch(Transaction txn, ContactId c,
|
||||
int maxLength, int maxLatency) throws DbException;
|
||||
|
||||
/** Returns the contact with the given ID. */
|
||||
Contact getContact(ContactId c) throws DbException;
|
||||
/**
|
||||
* Returns the contact with the given ID.
|
||||
*/
|
||||
Contact getContact(Transaction txn, ContactId c) throws DbException;
|
||||
|
||||
/** Returns all contacts. */
|
||||
Collection<Contact> getContacts() throws DbException;
|
||||
/**
|
||||
* Returns all contacts.
|
||||
*/
|
||||
Collection<Contact> getContacts(Transaction txn) throws DbException;
|
||||
|
||||
/** Returns all contacts associated with the given local pseudonym. */
|
||||
Collection<ContactId> getContacts(AuthorId a) throws DbException;
|
||||
/**
|
||||
* Returns all contacts associated with the given local pseudonym.
|
||||
*/
|
||||
Collection<ContactId> getContacts(Transaction txn, AuthorId a)
|
||||
throws DbException;
|
||||
|
||||
/** Returns the unique ID for this device. */
|
||||
DeviceId getDeviceId() throws DbException;
|
||||
/**
|
||||
* Returns the unique ID for this device.
|
||||
*/
|
||||
DeviceId getDeviceId(Transaction txn) throws DbException;
|
||||
|
||||
/** Returns the group with the given ID. */
|
||||
Group getGroup(GroupId g) throws DbException;
|
||||
/**
|
||||
* Returns the group with the given ID.
|
||||
*/
|
||||
Group getGroup(Transaction txn, GroupId g) throws DbException;
|
||||
|
||||
/** Returns the metadata for the given group. */
|
||||
Metadata getGroupMetadata(GroupId g) throws DbException;
|
||||
/**
|
||||
* Returns the metadata for the given group.
|
||||
*/
|
||||
Metadata getGroupMetadata(Transaction txn, GroupId g) throws DbException;
|
||||
|
||||
/** Returns all groups belonging to the given client. */
|
||||
Collection<Group> getGroups(ClientId c) throws DbException;
|
||||
/**
|
||||
* Returns all groups belonging to the given client.
|
||||
*/
|
||||
Collection<Group> getGroups(Transaction txn, ClientId c) throws DbException;
|
||||
|
||||
/** Returns the local pseudonym with the given ID. */
|
||||
LocalAuthor getLocalAuthor(AuthorId a) throws DbException;
|
||||
/**
|
||||
* Returns the local pseudonym with the given ID.
|
||||
*/
|
||||
LocalAuthor getLocalAuthor(Transaction txn, AuthorId a) throws DbException;
|
||||
|
||||
/** Returns all local pseudonyms. */
|
||||
Collection<LocalAuthor> getLocalAuthors() throws DbException;
|
||||
/**
|
||||
* Returns all local pseudonyms.
|
||||
*/
|
||||
Collection<LocalAuthor> getLocalAuthors(Transaction txn) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the IDs of any messages that need to be validated by the given
|
||||
* client.
|
||||
*/
|
||||
Collection<MessageId> getMessagesToValidate(ClientId c) throws DbException;
|
||||
|
||||
/** Returns the message with the given ID, in serialised form. */
|
||||
byte[] getRawMessage(MessageId m) throws DbException;
|
||||
|
||||
/** Returns the metadata for all messages in the given group. */
|
||||
Map<MessageId, Metadata> getMessageMetadata(GroupId g)
|
||||
Collection<MessageId> getMessagesToValidate(Transaction txn, ClientId c)
|
||||
throws DbException;
|
||||
|
||||
/** Returns the metadata for the given message. */
|
||||
Metadata getMessageMetadata(MessageId m) throws DbException;
|
||||
/**
|
||||
* Returns the message with the given ID, in serialised form.
|
||||
*/
|
||||
byte[] getRawMessage(Transaction txn, MessageId m) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the metadata for all messages in the given group.
|
||||
*/
|
||||
Map<MessageId, Metadata> getMessageMetadata(Transaction txn, GroupId g)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the metadata for the given message.
|
||||
*/
|
||||
Metadata getMessageMetadata(Transaction txn, MessageId m)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the status of all messages in the given group with respect to
|
||||
* the given contact.
|
||||
*/
|
||||
Collection<MessageStatus> getMessageStatus(ContactId c, GroupId g)
|
||||
throws DbException;
|
||||
Collection<MessageStatus> getMessageStatus(Transaction txn, ContactId c,
|
||||
GroupId g) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the status of the given message with respect to the given
|
||||
* contact.
|
||||
*/
|
||||
MessageStatus getMessageStatus(ContactId c, MessageId m)
|
||||
MessageStatus getMessageStatus(Transaction txn, ContactId c, MessageId m)
|
||||
throws DbException;
|
||||
|
||||
/** Returns all settings in the given namespace. */
|
||||
Settings getSettings(String namespace) throws DbException;
|
||||
/**
|
||||
* Returns all settings in the given namespace.
|
||||
*/
|
||||
Settings getSettings(Transaction txn, String namespace) throws DbException;
|
||||
|
||||
/** Returns all transport keys for the given transport. */
|
||||
Map<ContactId, TransportKeys> getTransportKeys(TransportId t)
|
||||
/**
|
||||
* Returns all transport keys for the given transport.
|
||||
*/
|
||||
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;
|
||||
|
||||
/** Returns the maximum latencies in milliseconds of all transports. */
|
||||
Map<TransportId, Integer> getTransportLatencies() throws DbException;
|
||||
|
||||
/**
|
||||
* Increments the outgoing stream counter for the given contact and
|
||||
* transport in the given rotation period .
|
||||
*/
|
||||
void incrementStreamCounter(ContactId c, TransportId t, long rotationPeriod)
|
||||
throws DbException;
|
||||
void incrementStreamCounter(Transaction txn, ContactId c, TransportId t,
|
||||
long rotationPeriod) throws DbException;
|
||||
|
||||
/** Returns true if the given group is visible to the given contact. */
|
||||
boolean isVisibleToContact(ContactId c, GroupId g) throws DbException;
|
||||
/**
|
||||
* Returns true if the given group is visible to the given contact.
|
||||
*/
|
||||
boolean isVisibleToContact(Transaction txn, ContactId c, GroupId g)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Merges the given metadata with the existing metadata for the given
|
||||
* group.
|
||||
*/
|
||||
void mergeGroupMetadata(GroupId g, Metadata meta) throws DbException;
|
||||
void mergeGroupMetadata(Transaction txn, GroupId g, Metadata meta)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Merges the given metadata with the existing metadata for the given
|
||||
* message.
|
||||
*/
|
||||
void mergeMessageMetadata(MessageId m, Metadata meta) throws DbException;
|
||||
void mergeMessageMetadata(Transaction txn, MessageId m, Metadata meta)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Merges the given settings with the existing settings in the given
|
||||
* namespace.
|
||||
*/
|
||||
void mergeSettings(Settings s, String namespace) throws DbException;
|
||||
void mergeSettings(Transaction txn, Settings s, String namespace)
|
||||
throws DbException;
|
||||
|
||||
/** Processes an ack from the given contact. */
|
||||
void receiveAck(ContactId c, Ack a) throws DbException;
|
||||
/**
|
||||
* Processes an ack from the given contact.
|
||||
*/
|
||||
void receiveAck(Transaction txn, ContactId c, Ack a) throws DbException;
|
||||
|
||||
/** Processes a message from the given contact. */
|
||||
void receiveMessage(ContactId c, Message m) throws DbException;
|
||||
/**
|
||||
* Processes a message from the given contact.
|
||||
*/
|
||||
void receiveMessage(Transaction txn, ContactId c, Message m)
|
||||
throws DbException;
|
||||
|
||||
/** Processes an offer from the given contact. */
|
||||
void receiveOffer(ContactId c, Offer o) throws DbException;
|
||||
/**
|
||||
* Processes an offer from the given contact.
|
||||
*/
|
||||
void receiveOffer(Transaction txn, ContactId c, Offer o) throws DbException;
|
||||
|
||||
/** Processes a request from the given contact. */
|
||||
void receiveRequest(ContactId c, Request r) throws DbException;
|
||||
/**
|
||||
* Processes a request from the given contact.
|
||||
*/
|
||||
void receiveRequest(Transaction txn, ContactId c, Request r)
|
||||
throws DbException;
|
||||
|
||||
/** Removes a contact (and all associated state) from the database. */
|
||||
void removeContact(ContactId c) throws DbException;
|
||||
/**
|
||||
* Removes a contact (and all associated state) from the database.
|
||||
*/
|
||||
void removeContact(Transaction txn, ContactId c) throws DbException;
|
||||
|
||||
/** Removes a group (and all associated state) from the database. */
|
||||
void removeGroup(Group g) throws DbException;
|
||||
/**
|
||||
* Removes a group (and all associated state) from the database.
|
||||
*/
|
||||
void removeGroup(Transaction txn, Group g) throws DbException;
|
||||
|
||||
/**
|
||||
* Removes a local pseudonym (and all associated state) from the database.
|
||||
*/
|
||||
void removeLocalAuthor(AuthorId a) throws DbException;
|
||||
void removeLocalAuthor(Transaction txn, AuthorId a) throws DbException;
|
||||
|
||||
/** Removes a transport (and all associated state) from the database. */
|
||||
void removeTransport(TransportId t) throws DbException;
|
||||
/**
|
||||
* Removes a transport (and all associated state) from the database.
|
||||
*/
|
||||
void removeTransport(Transaction txn, TransportId t) throws DbException;
|
||||
|
||||
/** Sets the status of the given contact. */
|
||||
void setContactStatus(ContactId c, StorageStatus s) throws DbException;
|
||||
/**
|
||||
* Sets the status of the given contact.
|
||||
*/
|
||||
void setContactStatus(Transaction txn, ContactId c, StorageStatus s)
|
||||
throws DbException;
|
||||
|
||||
/** Sets the status of the given local pseudonym. */
|
||||
void setLocalAuthorStatus(AuthorId a, StorageStatus s)
|
||||
throws DbException;
|
||||
/**
|
||||
* Sets the status of the given local pseudonym.
|
||||
*/
|
||||
void setLocalAuthorStatus(Transaction txn, AuthorId a, StorageStatus s)
|
||||
throws DbException;
|
||||
|
||||
/** Marks the given message as shared or unshared. */
|
||||
void setMessageShared(Message m, boolean shared) throws DbException;
|
||||
/**
|
||||
* Marks the given message as shared or unshared.
|
||||
*/
|
||||
void setMessageShared(Transaction txn, Message m, boolean shared)
|
||||
throws DbException;
|
||||
|
||||
/** Marks the given message as valid or invalid. */
|
||||
void setMessageValid(Message m, ClientId c, boolean valid)
|
||||
/**
|
||||
* Marks the given message as valid or invalid.
|
||||
*/
|
||||
void setMessageValid(Transaction txn, Message m, ClientId c, boolean valid)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Sets the reordering window for the given contact and transport in the
|
||||
* given rotation period.
|
||||
*/
|
||||
void setReorderingWindow(ContactId c, TransportId t, long rotationPeriod,
|
||||
long base, byte[] bitmap) throws DbException;
|
||||
void setReorderingWindow(Transaction txn, ContactId c, TransportId t,
|
||||
long rotationPeriod, long base, byte[] bitmap) throws DbException;
|
||||
|
||||
/** Makes a group visible or invisible to a contact. */
|
||||
void setVisibleToContact(ContactId c, GroupId g, boolean visible)
|
||||
throws DbException;
|
||||
/**
|
||||
* Makes a group visible or invisible to a contact.
|
||||
*/
|
||||
void setVisibleToContact(Transaction txn, ContactId c, GroupId g,
|
||||
boolean visible) throws DbException;
|
||||
|
||||
/**
|
||||
* Stores the given transport keys, deleting any keys they have replaced.
|
||||
*/
|
||||
void updateTransportKeys(Map<ContactId, TransportKeys> keys)
|
||||
throws DbException;
|
||||
void updateTransportKeys(Transaction txn,
|
||||
Map<ContactId, TransportKeys> keys) throws DbException;
|
||||
}
|
||||
|
||||
@@ -1,15 +1,38 @@
|
||||
package org.briarproject.api.db;
|
||||
|
||||
/** A wrapper around a database transaction. */
|
||||
/**
|
||||
* A wrapper around a database transaction. Transactions are not thread-safe.
|
||||
*/
|
||||
public class Transaction {
|
||||
|
||||
private final Object txn;
|
||||
private boolean complete = false;
|
||||
|
||||
public Transaction(Object txn) {
|
||||
this.txn = txn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the database transaction. The type of the returned object
|
||||
* depends on the database implementation.
|
||||
*/
|
||||
public Object unbox() {
|
||||
return txn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the transaction is ready to be committed.
|
||||
*/
|
||||
public boolean isComplete() {
|
||||
return complete;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the transaction as ready to be committed. This method must not be
|
||||
* called more than once.
|
||||
*/
|
||||
public void setComplete() {
|
||||
if (complete) throw new IllegalStateException();
|
||||
complete = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.briarproject.api.identity;
|
||||
|
||||
import org.briarproject.api.db.DbException;
|
||||
import org.briarproject.api.db.Transaction;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
@@ -25,10 +26,11 @@ public interface IdentityManager {
|
||||
void removeLocalAuthor(AuthorId a) throws DbException;
|
||||
|
||||
interface AddIdentityHook {
|
||||
void addingIdentity(LocalAuthor a);
|
||||
void addingIdentity(Transaction txn, LocalAuthor a) throws DbException;
|
||||
}
|
||||
|
||||
interface RemoveIdentityHook {
|
||||
void removingIdentity(LocalAuthor a);
|
||||
void removingIdentity(Transaction txn, LocalAuthor a)
|
||||
throws DbException;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package org.briarproject.api.sync;
|
||||
|
||||
import org.briarproject.api.db.DbException;
|
||||
import org.briarproject.api.db.Metadata;
|
||||
import org.briarproject.api.db.Transaction;
|
||||
|
||||
/**
|
||||
* Responsible for managing message validators and passing them messages to
|
||||
@@ -35,6 +37,7 @@ public interface ValidationManager {
|
||||
void registerValidationHook(ValidationHook hook);
|
||||
|
||||
interface ValidationHook {
|
||||
void validatingMessage(Message m, ClientId c, Metadata meta);
|
||||
void validatingMessage(Transaction txn, Message m, ClientId c,
|
||||
Metadata meta) throws DbException;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import org.briarproject.api.contact.ContactManager;
|
||||
import org.briarproject.api.db.DatabaseComponent;
|
||||
import org.briarproject.api.db.DbException;
|
||||
import org.briarproject.api.db.NoSuchContactException;
|
||||
import org.briarproject.api.db.Transaction;
|
||||
import org.briarproject.api.event.ContactAddedEvent;
|
||||
import org.briarproject.api.event.ContactRemovedEvent;
|
||||
import org.briarproject.api.event.EventBus;
|
||||
@@ -52,19 +53,31 @@ class ContactManagerImpl implements ContactManager, Service,
|
||||
public boolean start() {
|
||||
// Finish adding/removing any partly added/removed contacts
|
||||
try {
|
||||
for (Contact c : db.getContacts()) {
|
||||
if (c.getStatus().equals(ADDING)) {
|
||||
for (AddContactHook hook : addHooks)
|
||||
hook.addingContact(c);
|
||||
db.setContactStatus(c.getId(), ACTIVE);
|
||||
eventBus.broadcast(new ContactAddedEvent(c.getId()));
|
||||
} else if (c.getStatus().equals(REMOVING)) {
|
||||
for (RemoveContactHook hook : removeHooks)
|
||||
hook.removingContact(c);
|
||||
db.removeContact(c.getId());
|
||||
eventBus.broadcast(new ContactRemovedEvent(c.getId()));
|
||||
List<ContactId> added = new ArrayList<ContactId>();
|
||||
List<ContactId> removed = new ArrayList<ContactId>();
|
||||
Transaction txn = db.startTransaction();
|
||||
try {
|
||||
for (Contact c : db.getContacts(txn)) {
|
||||
if (c.getStatus().equals(ADDING)) {
|
||||
for (AddContactHook hook : addHooks)
|
||||
hook.addingContact(txn, c);
|
||||
db.setContactStatus(txn, c.getId(), ACTIVE);
|
||||
added.add(c.getId());
|
||||
} else if (c.getStatus().equals(REMOVING)) {
|
||||
for (RemoveContactHook hook : removeHooks)
|
||||
hook.removingContact(txn, c);
|
||||
db.removeContact(txn, c.getId());
|
||||
removed.add(c.getId());
|
||||
}
|
||||
}
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
for (ContactId c : added)
|
||||
eventBus.broadcast(new ContactAddedEvent(c));
|
||||
for (ContactId c : removed)
|
||||
eventBus.broadcast(new ContactRemovedEvent(c));
|
||||
return true;
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
@@ -90,24 +103,46 @@ class ContactManagerImpl implements ContactManager, Service,
|
||||
@Override
|
||||
public ContactId addContact(Author remote, AuthorId local)
|
||||
throws DbException {
|
||||
ContactId c = db.addContact(remote, local);
|
||||
Contact contact = db.getContact(c);
|
||||
for (AddContactHook hook : addHooks) hook.addingContact(contact);
|
||||
db.setContactStatus(c, ACTIVE);
|
||||
ContactId c;
|
||||
Transaction txn = db.startTransaction();
|
||||
try {
|
||||
c = db.addContact(txn, remote, local);
|
||||
Contact contact = db.getContact(txn, c);
|
||||
for (AddContactHook hook : addHooks)
|
||||
hook.addingContact(txn, contact);
|
||||
db.setContactStatus(txn, c, ACTIVE);
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
eventBus.broadcast(new ContactAddedEvent(c));
|
||||
return c;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Contact getContact(ContactId c) throws DbException {
|
||||
Contact contact = db.getContact(c);
|
||||
Contact contact;
|
||||
Transaction txn = db.startTransaction();
|
||||
try {
|
||||
contact = db.getContact(txn, c);
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
if (contact.getStatus().equals(ACTIVE)) return contact;
|
||||
throw new NoSuchContactException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Contact> getContacts() throws DbException {
|
||||
Collection<Contact> contacts = db.getContacts();
|
||||
Collection<Contact> contacts;
|
||||
Transaction txn = db.startTransaction();
|
||||
try {
|
||||
contacts = db.getContacts(txn);
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
// Filter out any contacts that are being added or removed
|
||||
List<Contact> active = new ArrayList<Contact>(contacts.size());
|
||||
for (Contact c : contacts)
|
||||
@@ -117,21 +152,30 @@ class ContactManagerImpl implements ContactManager, Service,
|
||||
|
||||
@Override
|
||||
public void removeContact(ContactId c) throws DbException {
|
||||
Contact contact = db.getContact(c);
|
||||
db.setContactStatus(c, REMOVING);
|
||||
for (RemoveContactHook hook : removeHooks)
|
||||
hook.removingContact(contact);
|
||||
db.removeContact(c);
|
||||
Transaction txn = db.startTransaction();
|
||||
try {
|
||||
removeContact(txn, c);
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
eventBus.broadcast(new ContactRemovedEvent(c));
|
||||
}
|
||||
|
||||
private void removeContact(Transaction txn, ContactId c)
|
||||
throws DbException {
|
||||
Contact contact = db.getContact(txn, c);
|
||||
db.setContactStatus(txn, c, REMOVING);
|
||||
for (RemoveContactHook hook : removeHooks)
|
||||
hook.removingContact(txn, contact);
|
||||
db.removeContact(txn, c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removingIdentity(LocalAuthor a) {
|
||||
public void removingIdentity(Transaction txn, LocalAuthor a)
|
||||
throws DbException {
|
||||
// Remove any contacts of the local pseudonym that's being removed
|
||||
try {
|
||||
for (ContactId c : db.getContacts(a.getId())) removeContact(c);
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
}
|
||||
for (ContactId c : db.getContacts(txn, a.getId()))
|
||||
removeContact(txn, c);
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,7 +4,6 @@ import com.google.inject.Inject;
|
||||
|
||||
import org.briarproject.api.FormatException;
|
||||
import org.briarproject.api.contact.Contact;
|
||||
import org.briarproject.api.contact.ContactManager;
|
||||
import org.briarproject.api.data.BdfDictionary;
|
||||
import org.briarproject.api.data.BdfReader;
|
||||
import org.briarproject.api.data.BdfReaderFactory;
|
||||
@@ -13,13 +12,13 @@ import org.briarproject.api.data.MetadataParser;
|
||||
import org.briarproject.api.db.DatabaseComponent;
|
||||
import org.briarproject.api.db.DbException;
|
||||
import org.briarproject.api.db.Metadata;
|
||||
import org.briarproject.api.db.Transaction;
|
||||
import org.briarproject.api.forum.Forum;
|
||||
import org.briarproject.api.forum.ForumManager;
|
||||
import org.briarproject.api.forum.ForumPost;
|
||||
import org.briarproject.api.forum.ForumPostHeader;
|
||||
import org.briarproject.api.identity.Author;
|
||||
import org.briarproject.api.identity.AuthorId;
|
||||
import org.briarproject.api.identity.IdentityManager;
|
||||
import org.briarproject.api.identity.LocalAuthor;
|
||||
import org.briarproject.api.sync.ClientId;
|
||||
import org.briarproject.api.sync.Group;
|
||||
@@ -37,7 +36,6 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static java.util.logging.Level.WARNING;
|
||||
@@ -59,22 +57,14 @@ class ForumManagerImpl implements ForumManager {
|
||||
Logger.getLogger(ForumManagerImpl.class.getName());
|
||||
|
||||
private final DatabaseComponent db;
|
||||
private final ContactManager contactManager;
|
||||
private final IdentityManager identityManager;
|
||||
private final BdfReaderFactory bdfReaderFactory;
|
||||
private final MetadataEncoder metadataEncoder;
|
||||
private final MetadataParser metadataParser;
|
||||
|
||||
/** Ensures isolation between database reads and writes. */
|
||||
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
|
||||
|
||||
@Inject
|
||||
ForumManagerImpl(DatabaseComponent db, ContactManager contactManager,
|
||||
IdentityManager identityManager, BdfReaderFactory bdfReaderFactory,
|
||||
ForumManagerImpl(DatabaseComponent db, BdfReaderFactory bdfReaderFactory,
|
||||
MetadataEncoder metadataEncoder, MetadataParser metadataParser) {
|
||||
this.db = db;
|
||||
this.contactManager = contactManager;
|
||||
this.identityManager = identityManager;
|
||||
this.bdfReaderFactory = bdfReaderFactory;
|
||||
this.metadataEncoder = metadataEncoder;
|
||||
this.metadataParser = metadataParser;
|
||||
@@ -87,7 +77,6 @@ class ForumManagerImpl implements ForumManager {
|
||||
|
||||
@Override
|
||||
public void addLocalPost(ForumPost p) throws DbException {
|
||||
lock.writeLock().lock();
|
||||
try {
|
||||
BdfDictionary d = new BdfDictionary();
|
||||
d.put("timestamp", p.getMessage().getTimestamp());
|
||||
@@ -105,45 +94,65 @@ class ForumManagerImpl implements ForumManager {
|
||||
d.put("local", true);
|
||||
d.put("read", true);
|
||||
Metadata meta = metadataEncoder.encode(d);
|
||||
db.addLocalMessage(p.getMessage(), CLIENT_ID, meta, true);
|
||||
Transaction txn = db.startTransaction();
|
||||
try {
|
||||
db.addLocalMessage(txn, p.getMessage(), CLIENT_ID, meta, true);
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
} catch (FormatException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
lock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Forum getForum(GroupId g) throws DbException {
|
||||
lock.readLock().lock();
|
||||
try {
|
||||
return parseForum(db.getGroup(g));
|
||||
Group group;
|
||||
Transaction txn = db.startTransaction();
|
||||
try {
|
||||
group = db.getGroup(txn, g);
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
return parseForum(group);
|
||||
} catch (FormatException e) {
|
||||
throw new DbException(e);
|
||||
} finally {
|
||||
lock.readLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Forum> getForums() throws DbException {
|
||||
lock.readLock().lock();
|
||||
try {
|
||||
Collection<Group> groups;
|
||||
Transaction txn = db.startTransaction();
|
||||
try {
|
||||
groups = db.getGroups(txn, CLIENT_ID);
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
List<Forum> forums = new ArrayList<Forum>();
|
||||
for (Group g : db.getGroups(CLIENT_ID)) forums.add(parseForum(g));
|
||||
for (Group g : groups) forums.add(parseForum(g));
|
||||
return Collections.unmodifiableList(forums);
|
||||
} catch (FormatException e) {
|
||||
throw new DbException(e);
|
||||
} finally {
|
||||
lock.readLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getPostBody(MessageId m) throws DbException {
|
||||
lock.readLock().lock();
|
||||
try {
|
||||
byte[] raw = db.getRawMessage(m);
|
||||
byte[] raw;
|
||||
Transaction txn = db.startTransaction();
|
||||
try {
|
||||
raw = db.getRawMessage(txn, m);
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(raw,
|
||||
MESSAGE_HEADER_LENGTH, raw.length - MESSAGE_HEADER_LENGTH);
|
||||
BdfReader r = bdfReaderFactory.createReader(in);
|
||||
@@ -164,74 +173,76 @@ class ForumManagerImpl implements ForumManager {
|
||||
} catch (IOException e) {
|
||||
// Shouldn't happen with ByteArrayInputStream
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
lock.readLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ForumPostHeader> getPostHeaders(GroupId g)
|
||||
throws DbException {
|
||||
lock.readLock().lock();
|
||||
Set<AuthorId> localAuthorIds = new HashSet<AuthorId>();
|
||||
Set<AuthorId> contactAuthorIds = new HashSet<AuthorId>();
|
||||
Map<MessageId, Metadata> metadata;
|
||||
Transaction txn = db.startTransaction();
|
||||
try {
|
||||
// Load the IDs of the user's identities
|
||||
Set<AuthorId> localAuthorIds = new HashSet<AuthorId>();
|
||||
for (LocalAuthor a : identityManager.getLocalAuthors())
|
||||
for (LocalAuthor a : db.getLocalAuthors(txn))
|
||||
localAuthorIds.add(a.getId());
|
||||
// Load the IDs of contacts' identities
|
||||
Set<AuthorId> contactAuthorIds = new HashSet<AuthorId>();
|
||||
for (Contact c : contactManager.getContacts())
|
||||
for (Contact c : db.getContacts(txn))
|
||||
contactAuthorIds.add(c.getAuthor().getId());
|
||||
// Load and parse the metadata
|
||||
Map<MessageId, Metadata> metadata = db.getMessageMetadata(g);
|
||||
Collection<ForumPostHeader> headers =
|
||||
new ArrayList<ForumPostHeader>();
|
||||
for (Entry<MessageId, Metadata> e : metadata.entrySet()) {
|
||||
MessageId messageId = e.getKey();
|
||||
Metadata meta = e.getValue();
|
||||
try {
|
||||
BdfDictionary d = metadataParser.parse(meta);
|
||||
long timestamp = d.getInteger("timestamp");
|
||||
Author author = null;
|
||||
Author.Status authorStatus = ANONYMOUS;
|
||||
BdfDictionary d1 = d.getDictionary("author", null);
|
||||
if (d1 != null) {
|
||||
AuthorId authorId = new AuthorId(d1.getRaw("id"));
|
||||
String name = d1.getString("name");
|
||||
byte[] publicKey = d1.getRaw("publicKey");
|
||||
author = new Author(authorId, name, publicKey);
|
||||
if (localAuthorIds.contains(authorId))
|
||||
authorStatus = VERIFIED;
|
||||
else if (contactAuthorIds.contains(authorId))
|
||||
authorStatus = VERIFIED;
|
||||
else authorStatus = UNKNOWN;
|
||||
}
|
||||
String contentType = d.getString("contentType");
|
||||
boolean read = d.getBoolean("read");
|
||||
headers.add(new ForumPostHeader(messageId, timestamp,
|
||||
author, authorStatus, contentType, read));
|
||||
} catch (FormatException ex) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, ex.toString(), ex);
|
||||
}
|
||||
}
|
||||
return headers;
|
||||
// Load the metadata
|
||||
metadata = db.getMessageMetadata(txn, g);
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
lock.readLock().unlock();
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
// Parse the metadata
|
||||
Collection<ForumPostHeader> headers = new ArrayList<ForumPostHeader>();
|
||||
for (Entry<MessageId, Metadata> e : metadata.entrySet()) {
|
||||
try {
|
||||
BdfDictionary d = metadataParser.parse(e.getValue());
|
||||
long timestamp = d.getInteger("timestamp");
|
||||
Author author = null;
|
||||
Author.Status authorStatus = ANONYMOUS;
|
||||
BdfDictionary d1 = d.getDictionary("author", null);
|
||||
if (d1 != null) {
|
||||
AuthorId authorId = new AuthorId(d1.getRaw("id"));
|
||||
String name = d1.getString("name");
|
||||
byte[] publicKey = d1.getRaw("publicKey");
|
||||
author = new Author(authorId, name, publicKey);
|
||||
if (localAuthorIds.contains(authorId))
|
||||
authorStatus = VERIFIED;
|
||||
else if (contactAuthorIds.contains(authorId))
|
||||
authorStatus = VERIFIED;
|
||||
else authorStatus = UNKNOWN;
|
||||
}
|
||||
String contentType = d.getString("contentType");
|
||||
boolean read = d.getBoolean("read");
|
||||
headers.add(new ForumPostHeader(e.getKey(), timestamp, author,
|
||||
authorStatus, contentType, read));
|
||||
} catch (FormatException ex) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, ex.toString(), ex);
|
||||
}
|
||||
}
|
||||
return headers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReadFlag(MessageId m, boolean read) throws DbException {
|
||||
lock.writeLock().lock();
|
||||
try {
|
||||
BdfDictionary d = new BdfDictionary();
|
||||
d.put("read", read);
|
||||
db.mergeMessageMetadata(m, metadataEncoder.encode(d));
|
||||
Metadata meta = metadataEncoder.encode(d);
|
||||
Transaction txn = db.startTransaction();
|
||||
try {
|
||||
db.mergeMessageMetadata(txn, m, meta);
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
} catch (FormatException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
lock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import com.google.inject.Inject;
|
||||
import org.briarproject.api.FormatException;
|
||||
import org.briarproject.api.contact.Contact;
|
||||
import org.briarproject.api.contact.ContactId;
|
||||
import org.briarproject.api.contact.ContactManager;
|
||||
import org.briarproject.api.contact.ContactManager.AddContactHook;
|
||||
import org.briarproject.api.contact.ContactManager.RemoveContactHook;
|
||||
import org.briarproject.api.data.BdfDictionary;
|
||||
@@ -18,6 +17,7 @@ import org.briarproject.api.data.MetadataParser;
|
||||
import org.briarproject.api.db.DatabaseComponent;
|
||||
import org.briarproject.api.db.DbException;
|
||||
import org.briarproject.api.db.Metadata;
|
||||
import org.briarproject.api.db.Transaction;
|
||||
import org.briarproject.api.forum.Forum;
|
||||
import org.briarproject.api.forum.ForumManager;
|
||||
import org.briarproject.api.forum.ForumSharingManager;
|
||||
@@ -45,10 +45,8 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static org.briarproject.api.forum.ForumConstants.FORUM_SALT_LENGTH;
|
||||
import static org.briarproject.api.forum.ForumConstants.MAX_FORUM_NAME_LENGTH;
|
||||
import static org.briarproject.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
|
||||
@@ -62,11 +60,7 @@ class ForumSharingManagerImpl implements ForumSharingManager, AddContactHook,
|
||||
|
||||
private static final byte[] LOCAL_GROUP_DESCRIPTOR = new byte[0];
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(ForumSharingManagerImpl.class.getName());
|
||||
|
||||
private final DatabaseComponent db;
|
||||
private final ContactManager contactManager;
|
||||
private final ForumManager forumManager;
|
||||
private final GroupFactory groupFactory;
|
||||
private final PrivateGroupFactory privateGroupFactory;
|
||||
@@ -79,18 +73,14 @@ class ForumSharingManagerImpl implements ForumSharingManager, AddContactHook,
|
||||
private final Clock clock;
|
||||
private final Group localGroup;
|
||||
|
||||
/** Ensures isolation between database reads and writes. */
|
||||
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
|
||||
|
||||
@Inject
|
||||
ForumSharingManagerImpl(DatabaseComponent db,
|
||||
ContactManager contactManager, ForumManager forumManager,
|
||||
GroupFactory groupFactory, PrivateGroupFactory privateGroupFactory,
|
||||
ForumManager forumManager, GroupFactory groupFactory,
|
||||
PrivateGroupFactory privateGroupFactory,
|
||||
MessageFactory messageFactory, BdfReaderFactory bdfReaderFactory,
|
||||
BdfWriterFactory bdfWriterFactory, MetadataEncoder metadataEncoder,
|
||||
MetadataParser metadataParser, SecureRandom random, Clock clock) {
|
||||
this.db = db;
|
||||
this.contactManager = contactManager;
|
||||
this.forumManager = forumManager;
|
||||
this.groupFactory = groupFactory;
|
||||
this.privateGroupFactory = privateGroupFactory;
|
||||
@@ -106,57 +96,39 @@ class ForumSharingManagerImpl implements ForumSharingManager, AddContactHook,
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addingContact(Contact c) {
|
||||
lock.writeLock().lock();
|
||||
public void addingContact(Transaction txn, Contact c) throws DbException {
|
||||
try {
|
||||
// Create a group to share with the contact
|
||||
Group g = getContactGroup(c);
|
||||
// Store the group and share it with the contact
|
||||
db.addGroup(g);
|
||||
db.setVisibleToContact(c.getId(), g.getId(), true);
|
||||
db.addGroup(txn, g);
|
||||
db.setVisibleToContact(txn, c.getId(), g.getId(), true);
|
||||
// Attach the contact ID to the group
|
||||
BdfDictionary d = new BdfDictionary();
|
||||
d.put("contactId", c.getId().getInt());
|
||||
db.mergeGroupMetadata(g.getId(), metadataEncoder.encode(d));
|
||||
db.mergeGroupMetadata(txn, g.getId(), metadataEncoder.encode(d));
|
||||
// Share any forums that are shared with all contacts
|
||||
List<Forum> shared = getForumsSharedWithAllContacts();
|
||||
storeMessage(g.getId(), shared, 0);
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
List<Forum> shared = getForumsSharedWithAllContacts(txn);
|
||||
storeMessage(txn, g.getId(), shared, 0);
|
||||
} catch (FormatException e) {
|
||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
} finally {
|
||||
lock.writeLock().unlock();
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removingContact(Contact c) {
|
||||
lock.writeLock().lock();
|
||||
try {
|
||||
db.removeGroup(getContactGroup(c));
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
} finally {
|
||||
lock.writeLock().unlock();
|
||||
}
|
||||
public void removingContact(Transaction txn, Contact c) throws DbException {
|
||||
db.removeGroup(txn, getContactGroup(c));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validatingMessage(Message m, ClientId c, Metadata meta) {
|
||||
public void validatingMessage(Transaction txn, Message m, ClientId c,
|
||||
Metadata meta) throws DbException {
|
||||
if (c.equals(CLIENT_ID)) {
|
||||
lock.writeLock().lock();
|
||||
try {
|
||||
ContactId contactId = getContactId(m.getGroupId());
|
||||
setForumVisibility(contactId, getVisibleForums(m));
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
ContactId contactId = getContactId(txn, m.getGroupId());
|
||||
setForumVisibility(txn, contactId, getVisibleForums(txn, m));
|
||||
} catch (FormatException e) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
} finally {
|
||||
lock.writeLock().unlock();
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -179,149 +151,162 @@ class ForumSharingManagerImpl implements ForumSharingManager, AddContactHook,
|
||||
|
||||
@Override
|
||||
public void addForum(Forum f) throws DbException {
|
||||
lock.writeLock().lock();
|
||||
Transaction txn = db.startTransaction();
|
||||
try {
|
||||
db.addGroup(f.getGroup());
|
||||
db.addGroup(txn, f.getGroup());
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
lock.writeLock().unlock();
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeForum(Forum f) throws DbException {
|
||||
lock.writeLock().lock();
|
||||
try {
|
||||
// Update the list of forums shared with each contact
|
||||
for (Contact c : contactManager.getContacts()) {
|
||||
Group contactGroup = getContactGroup(c);
|
||||
removeFromList(contactGroup.getId(), f);
|
||||
// Update the list shared with each contact
|
||||
Transaction txn = db.startTransaction();
|
||||
try {
|
||||
for (Contact c : db.getContacts(txn))
|
||||
removeFromList(txn, getContactGroup(c).getId(), f);
|
||||
db.removeGroup(txn, f.getGroup());
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
db.removeGroup(f.getGroup());
|
||||
} catch (IOException e) {
|
||||
throw new DbException(e);
|
||||
} finally {
|
||||
lock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Forum> getAvailableForums() throws DbException {
|
||||
lock.readLock().lock();
|
||||
try {
|
||||
// Get any forums we subscribe to
|
||||
Set<Group> subscribed = new HashSet<Group>(db.getGroups(
|
||||
forumManager.getClientId()));
|
||||
// Get all forums shared by contacts
|
||||
Set<Forum> available = new HashSet<Forum>();
|
||||
for (Contact c : contactManager.getContacts()) {
|
||||
Group g = getContactGroup(c);
|
||||
// Find the latest update version
|
||||
LatestUpdate latest = findLatest(g.getId(), false);
|
||||
if (latest != null) {
|
||||
// Retrieve and parse the latest update
|
||||
byte[] raw = db.getRawMessage(latest.messageId);
|
||||
for (Forum f : parseForumList(raw)) {
|
||||
if (!subscribed.contains(f.getGroup()))
|
||||
available.add(f);
|
||||
Transaction txn = db.startTransaction();
|
||||
try {
|
||||
// Get any forums we subscribe to
|
||||
Set<Group> subscribed = new HashSet<Group>(db.getGroups(txn,
|
||||
forumManager.getClientId()));
|
||||
// Get all forums shared by contacts
|
||||
for (Contact c : db.getContacts(txn)) {
|
||||
Group g = getContactGroup(c);
|
||||
// Find the latest update version
|
||||
LatestUpdate latest = findLatest(txn, g.getId(), false);
|
||||
if (latest != null) {
|
||||
// Retrieve and parse the latest update
|
||||
byte[] raw = db.getRawMessage(txn, latest.messageId);
|
||||
for (Forum f : parseForumList(raw)) {
|
||||
if (!subscribed.contains(f.getGroup()))
|
||||
available.add(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
return Collections.unmodifiableSet(available);
|
||||
} catch (IOException e) {
|
||||
throw new DbException(e);
|
||||
} finally {
|
||||
lock.readLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Contact> getSharedBy(GroupId g) throws DbException {
|
||||
lock.readLock().lock();
|
||||
try {
|
||||
List<Contact> subscribers = new ArrayList<Contact>();
|
||||
for (Contact c : contactManager.getContacts()) {
|
||||
Group contactGroup = getContactGroup(c);
|
||||
if (listContains(contactGroup.getId(), g, false))
|
||||
subscribers.add(c);
|
||||
Transaction txn = db.startTransaction();
|
||||
try {
|
||||
for (Contact c : db.getContacts(txn)) {
|
||||
if (listContains(txn, getContactGroup(c).getId(), g, false))
|
||||
subscribers.add(c);
|
||||
}
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
return Collections.unmodifiableList(subscribers);
|
||||
} catch (IOException e) {
|
||||
throw new DbException(e);
|
||||
} finally {
|
||||
lock.readLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ContactId> getSharedWith(GroupId g) throws DbException {
|
||||
lock.readLock().lock();
|
||||
try {
|
||||
List<ContactId> shared = new ArrayList<ContactId>();
|
||||
for (Contact c : contactManager.getContacts()) {
|
||||
Group contactGroup = getContactGroup(c);
|
||||
if (listContains(contactGroup.getId(), g, true))
|
||||
shared.add(c.getId());
|
||||
Transaction txn = db.startTransaction();
|
||||
try {
|
||||
for (Contact c : db.getContacts(txn)) {
|
||||
if (listContains(txn, getContactGroup(c).getId(), g, true))
|
||||
shared.add(c.getId());
|
||||
}
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
return Collections.unmodifiableList(shared);
|
||||
} catch (FormatException e) {
|
||||
throw new DbException(e);
|
||||
} finally {
|
||||
lock.readLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSharedWith(GroupId g, Collection<ContactId> shared)
|
||||
throws DbException {
|
||||
lock.writeLock().lock();
|
||||
try {
|
||||
// Retrieve the forum
|
||||
Forum f = parseForum(db.getGroup(g));
|
||||
// Remove the forum from the list of forums shared with all contacts
|
||||
removeFromList(localGroup.getId(), f);
|
||||
// Update the list of forums shared with each contact
|
||||
shared = new HashSet<ContactId>(shared);
|
||||
for (Contact c : contactManager.getContacts()) {
|
||||
Group contactGroup = getContactGroup(c);
|
||||
if (shared.contains(c.getId())) {
|
||||
if (addToList(contactGroup.getId(), f)) {
|
||||
// If the contact is sharing the forum, make it visible
|
||||
if (listContains(contactGroup.getId(), g, false))
|
||||
db.setVisibleToContact(c.getId(), g, true);
|
||||
Transaction txn = db.startTransaction();
|
||||
try {
|
||||
// Retrieve the forum
|
||||
Forum f = parseForum(db.getGroup(txn, g));
|
||||
// Remove the forum from the list shared with all contacts
|
||||
removeFromList(txn, localGroup.getId(), f);
|
||||
// Update the list shared with each contact
|
||||
shared = new HashSet<ContactId>(shared);
|
||||
for (Contact c : db.getContacts(txn)) {
|
||||
Group cg = getContactGroup(c);
|
||||
if (shared.contains(c.getId())) {
|
||||
if (addToList(txn, cg.getId(), f)) {
|
||||
if (listContains(txn, cg.getId(), g, false))
|
||||
db.setVisibleToContact(txn, c.getId(), g, true);
|
||||
}
|
||||
} else {
|
||||
removeFromList(txn, cg.getId(), f);
|
||||
db.setVisibleToContact(txn, c.getId(), g, false);
|
||||
}
|
||||
} else {
|
||||
removeFromList(contactGroup.getId(), f);
|
||||
db.setVisibleToContact(c.getId(), g, false);
|
||||
}
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
} catch (FormatException e) {
|
||||
throw new DbException(e);
|
||||
} finally {
|
||||
lock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSharedWithAll(GroupId g) throws DbException {
|
||||
lock.writeLock().lock();
|
||||
try {
|
||||
// Retrieve the forum
|
||||
Forum f = parseForum(db.getGroup(g));
|
||||
// Add the forum to the list of forums shared with all contacts
|
||||
addToList(localGroup.getId(), f);
|
||||
// Add the forum to the list of forums shared with each contact
|
||||
for (Contact c : contactManager.getContacts()) {
|
||||
Group contactGroup = getContactGroup(c);
|
||||
if (addToList(contactGroup.getId(), f)) {
|
||||
// If the contact is sharing the forum, make it visible
|
||||
if (listContains(contactGroup.getId(), g, false))
|
||||
db.setVisibleToContact(getContactId(g), g, true);
|
||||
Transaction txn = db.startTransaction();
|
||||
try {
|
||||
// Retrieve the forum
|
||||
Forum f = parseForum(db.getGroup(txn, g));
|
||||
// Add the forum to the list shared with all contacts
|
||||
addToList(txn, localGroup.getId(), f);
|
||||
// Add the forum to the list shared with each contact
|
||||
for (Contact c : db.getContacts(txn)) {
|
||||
Group cg = getContactGroup(c);
|
||||
if (addToList(txn, cg.getId(), f)) {
|
||||
if (listContains(txn, cg.getId(), g, false))
|
||||
db.setVisibleToContact(txn, c.getId(), g, true);
|
||||
}
|
||||
}
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
} catch (FormatException e) {
|
||||
throw new DbException(e);
|
||||
} finally {
|
||||
lock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -329,23 +314,21 @@ class ForumSharingManagerImpl implements ForumSharingManager, AddContactHook,
|
||||
return privateGroupFactory.createPrivateGroup(CLIENT_ID, c);
|
||||
}
|
||||
|
||||
// Locking: lock.writeLock
|
||||
private List<Forum> getForumsSharedWithAllContacts() throws DbException,
|
||||
FormatException {
|
||||
private List<Forum> getForumsSharedWithAllContacts(Transaction txn)
|
||||
throws DbException, FormatException {
|
||||
// Ensure the local group exists
|
||||
db.addGroup(localGroup);
|
||||
db.addGroup(txn, localGroup);
|
||||
// Find the latest update in the local group
|
||||
LatestUpdate latest = findLatest(localGroup.getId(), true);
|
||||
LatestUpdate latest = findLatest(txn, localGroup.getId(), true);
|
||||
if (latest == null) return Collections.emptyList();
|
||||
// Retrieve and parse the latest update
|
||||
return parseForumList(db.getRawMessage(latest.messageId));
|
||||
return parseForumList(db.getRawMessage(txn, latest.messageId));
|
||||
}
|
||||
|
||||
// Locking: lock.readLock
|
||||
private LatestUpdate findLatest(GroupId g, boolean local)
|
||||
private LatestUpdate findLatest(Transaction txn, GroupId g, boolean local)
|
||||
throws DbException, FormatException {
|
||||
LatestUpdate latest = null;
|
||||
Map<MessageId, Metadata> metadata = db.getMessageMetadata(g);
|
||||
Map<MessageId, Metadata> metadata = db.getMessageMetadata(txn, g);
|
||||
for (Entry<MessageId, Metadata> e : metadata.entrySet()) {
|
||||
BdfDictionary d = metadataParser.parse(e.getValue());
|
||||
if (d.getBoolean("local") != local) continue;
|
||||
@@ -384,16 +367,20 @@ class ForumSharingManagerImpl implements ForumSharingManager, AddContactHook,
|
||||
}
|
||||
}
|
||||
|
||||
// Locking: lock.writeLock
|
||||
private void storeMessage(GroupId g, List<Forum> forums, long version)
|
||||
throws DbException, FormatException {
|
||||
byte[] body = encodeForumList(forums, version);
|
||||
long now = clock.currentTimeMillis();
|
||||
Message m = messageFactory.createMessage(g, now, body);
|
||||
BdfDictionary d = new BdfDictionary();
|
||||
d.put("version", version);
|
||||
d.put("local", true);
|
||||
db.addLocalMessage(m, CLIENT_ID, metadataEncoder.encode(d), true);
|
||||
private void storeMessage(Transaction txn, GroupId g, List<Forum> forums,
|
||||
long version) throws DbException {
|
||||
try {
|
||||
byte[] body = encodeForumList(forums, version);
|
||||
long now = clock.currentTimeMillis();
|
||||
Message m = messageFactory.createMessage(g, now, body);
|
||||
BdfDictionary d = new BdfDictionary();
|
||||
d.put("version", version);
|
||||
d.put("local", true);
|
||||
db.addLocalMessage(txn, m, CLIENT_ID, metadataEncoder.encode(d),
|
||||
true);
|
||||
} catch (FormatException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] encodeForumList(List<Forum> forums, long version) {
|
||||
@@ -418,23 +405,21 @@ class ForumSharingManagerImpl implements ForumSharingManager, AddContactHook,
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
// Locking: lock.readLock
|
||||
private ContactId getContactId(GroupId contactGroupId) throws DbException,
|
||||
FormatException {
|
||||
Metadata meta = db.getGroupMetadata(contactGroupId);
|
||||
private ContactId getContactId(Transaction txn, GroupId contactGroupId)
|
||||
throws DbException, FormatException {
|
||||
Metadata meta = db.getGroupMetadata(txn, contactGroupId);
|
||||
BdfDictionary d = metadataParser.parse(meta);
|
||||
return new ContactId(d.getInteger("contactId").intValue());
|
||||
}
|
||||
|
||||
// Locking: lock.readLock
|
||||
private Set<GroupId> getVisibleForums(Message remoteUpdate)
|
||||
throws DbException, FormatException {
|
||||
private Set<GroupId> getVisibleForums(Transaction txn,
|
||||
Message remoteUpdate) throws DbException, FormatException {
|
||||
// Get the latest local update
|
||||
LatestUpdate local = findLatest(remoteUpdate.getGroupId(), true);
|
||||
LatestUpdate local = findLatest(txn, remoteUpdate.getGroupId(), true);
|
||||
// If there's no local update, no forums are visible
|
||||
if (local == null) return Collections.emptySet();
|
||||
// Intersect the sets of shared forums
|
||||
byte[] localRaw = db.getRawMessage(local.messageId);
|
||||
byte[] localRaw = db.getRawMessage(txn, local.messageId);
|
||||
Set<Forum> shared = new HashSet<Forum>(parseForumList(localRaw));
|
||||
shared.retainAll(parseForumList(remoteUpdate.getRaw()));
|
||||
// Forums in the intersection should be visible
|
||||
@@ -443,16 +428,15 @@ class ForumSharingManagerImpl implements ForumSharingManager, AddContactHook,
|
||||
return visible;
|
||||
}
|
||||
|
||||
// Locking: lock.writeLock
|
||||
private void setForumVisibility(ContactId c, Set<GroupId> visible)
|
||||
throws DbException {
|
||||
for (Group g : db.getGroups(forumManager.getClientId())) {
|
||||
boolean isVisible = db.isVisibleToContact(c, g.getId());
|
||||
private void setForumVisibility(Transaction txn, ContactId c,
|
||||
Set<GroupId> visible) throws DbException {
|
||||
for (Group g : db.getGroups(txn, forumManager.getClientId())) {
|
||||
boolean isVisible = db.isVisibleToContact(txn, c, g.getId());
|
||||
boolean shouldBeVisible = visible.contains(g.getId());
|
||||
if (isVisible && !shouldBeVisible)
|
||||
db.setVisibleToContact(c, g.getId(), false);
|
||||
db.setVisibleToContact(txn, c, g.getId(), false);
|
||||
else if (!isVisible && shouldBeVisible)
|
||||
db.setVisibleToContact(c, g.getId(), true);
|
||||
db.setVisibleToContact(txn, c, g.getId(), true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -491,38 +475,38 @@ class ForumSharingManagerImpl implements ForumSharingManager, AddContactHook,
|
||||
}
|
||||
}
|
||||
|
||||
// Locking: lock.readLock
|
||||
private boolean listContains(GroupId g, GroupId forum, boolean local)
|
||||
throws DbException, FormatException {
|
||||
LatestUpdate latest = findLatest(g, local);
|
||||
private boolean listContains(Transaction txn, GroupId g, GroupId forum,
|
||||
boolean local) throws DbException, FormatException {
|
||||
LatestUpdate latest = findLatest(txn, g, local);
|
||||
if (latest == null) return false;
|
||||
List<Forum> list = parseForumList(db.getRawMessage(latest.messageId));
|
||||
byte[] raw = db.getRawMessage(txn, latest.messageId);
|
||||
List<Forum> list = parseForumList(raw);
|
||||
for (Forum f : list) if (f.getId().equals(forum)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Locking: lock.writeLock
|
||||
private boolean addToList(GroupId g, Forum f) throws DbException,
|
||||
FormatException {
|
||||
LatestUpdate latest = findLatest(g, true);
|
||||
private boolean addToList(Transaction txn, GroupId g, Forum f)
|
||||
throws DbException, FormatException {
|
||||
LatestUpdate latest = findLatest(txn, g, true);
|
||||
if (latest == null) {
|
||||
storeMessage(g, Collections.singletonList(f), 0);
|
||||
storeMessage(txn, g, Collections.singletonList(f), 0);
|
||||
return true;
|
||||
}
|
||||
List<Forum> list = parseForumList(db.getRawMessage(latest.messageId));
|
||||
byte[] raw = db.getRawMessage(txn, latest.messageId);
|
||||
List<Forum> list = parseForumList(raw);
|
||||
if (list.contains(f)) return false;
|
||||
list.add(f);
|
||||
storeMessage(g, list, latest.version + 1);
|
||||
storeMessage(txn, g, list, latest.version + 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Locking: lock.writeLock
|
||||
private void removeFromList(GroupId g, Forum f) throws DbException,
|
||||
FormatException {
|
||||
LatestUpdate latest = findLatest(g, true);
|
||||
private void removeFromList(Transaction txn, GroupId g, Forum f)
|
||||
throws DbException, FormatException {
|
||||
LatestUpdate latest = findLatest(txn, g, true);
|
||||
if (latest == null) return;
|
||||
List<Forum> list = parseForumList(db.getRawMessage(latest.messageId));
|
||||
if (list.remove(f)) storeMessage(g, list, latest.version + 1);
|
||||
byte[] raw = db.getRawMessage(txn, latest.messageId);
|
||||
List<Forum> list = parseForumList(raw);
|
||||
if (list.remove(f)) storeMessage(txn, g, list, latest.version + 1);
|
||||
}
|
||||
|
||||
private static class LatestUpdate {
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.google.inject.Inject;
|
||||
import org.briarproject.api.db.DatabaseComponent;
|
||||
import org.briarproject.api.db.DbException;
|
||||
import org.briarproject.api.db.NoSuchLocalAuthorException;
|
||||
import org.briarproject.api.db.Transaction;
|
||||
import org.briarproject.api.event.EventBus;
|
||||
import org.briarproject.api.event.LocalAuthorAddedEvent;
|
||||
import org.briarproject.api.event.LocalAuthorRemovedEvent;
|
||||
@@ -47,19 +48,31 @@ class IdentityManagerImpl implements IdentityManager, Service {
|
||||
public boolean start() {
|
||||
// Finish adding/removing any partly added/removed pseudonyms
|
||||
try {
|
||||
for (LocalAuthor a : db.getLocalAuthors()) {
|
||||
if (a.getStatus().equals(ADDING)) {
|
||||
for (AddIdentityHook hook : addHooks)
|
||||
hook.addingIdentity(a);
|
||||
db.setLocalAuthorStatus(a.getId(), ACTIVE);
|
||||
eventBus.broadcast(new LocalAuthorAddedEvent(a.getId()));
|
||||
} else if (a.getStatus().equals(REMOVING)) {
|
||||
for (RemoveIdentityHook hook : removeHooks)
|
||||
hook.removingIdentity(a);
|
||||
db.removeLocalAuthor(a.getId());
|
||||
eventBus.broadcast(new LocalAuthorRemovedEvent(a.getId()));
|
||||
List<AuthorId> added = new ArrayList<AuthorId>();
|
||||
List<AuthorId> removed = new ArrayList<AuthorId>();
|
||||
Transaction txn = db.startTransaction();
|
||||
try {
|
||||
for (LocalAuthor a : db.getLocalAuthors(txn)) {
|
||||
if (a.getStatus().equals(ADDING)) {
|
||||
for (AddIdentityHook hook : addHooks)
|
||||
hook.addingIdentity(txn, a);
|
||||
db.setLocalAuthorStatus(txn, a.getId(), ACTIVE);
|
||||
added.add(a.getId());
|
||||
} else if (a.getStatus().equals(REMOVING)) {
|
||||
for (RemoveIdentityHook hook : removeHooks)
|
||||
hook.removingIdentity(txn, a);
|
||||
db.removeLocalAuthor(txn, a.getId());
|
||||
removed.add(a.getId());
|
||||
}
|
||||
}
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
for (AuthorId a : added)
|
||||
eventBus.broadcast(new LocalAuthorAddedEvent(a));
|
||||
for (AuthorId a : removed)
|
||||
eventBus.broadcast(new LocalAuthorRemovedEvent(a));
|
||||
return true;
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
@@ -84,22 +97,43 @@ class IdentityManagerImpl implements IdentityManager, Service {
|
||||
|
||||
@Override
|
||||
public void addLocalAuthor(LocalAuthor localAuthor) throws DbException {
|
||||
db.addLocalAuthor(localAuthor);
|
||||
for (AddIdentityHook hook : addHooks) hook.addingIdentity(localAuthor);
|
||||
db.setLocalAuthorStatus(localAuthor.getId(), ACTIVE);
|
||||
Transaction txn = db.startTransaction();
|
||||
try {
|
||||
db.addLocalAuthor(txn, localAuthor);
|
||||
for (AddIdentityHook hook : addHooks)
|
||||
hook.addingIdentity(txn, localAuthor);
|
||||
db.setLocalAuthorStatus(txn, localAuthor.getId(), ACTIVE);
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
eventBus.broadcast(new LocalAuthorAddedEvent(localAuthor.getId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalAuthor getLocalAuthor(AuthorId a) throws DbException {
|
||||
LocalAuthor author = db.getLocalAuthor(a);
|
||||
LocalAuthor author;
|
||||
Transaction txn = db.startTransaction();
|
||||
try {
|
||||
author = db.getLocalAuthor(txn, a);
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
if (author.getStatus().equals(ACTIVE)) return author;
|
||||
throw new NoSuchLocalAuthorException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<LocalAuthor> getLocalAuthors() throws DbException {
|
||||
Collection<LocalAuthor> authors = db.getLocalAuthors();
|
||||
Collection<LocalAuthor> authors;
|
||||
Transaction txn = db.startTransaction();
|
||||
try {
|
||||
authors = db.getLocalAuthors(txn);
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
// Filter out any pseudonyms that are being added or removed
|
||||
List<LocalAuthor> active = new ArrayList<LocalAuthor>(authors.size());
|
||||
for (LocalAuthor a : authors)
|
||||
@@ -109,11 +143,17 @@ class IdentityManagerImpl implements IdentityManager, Service {
|
||||
|
||||
@Override
|
||||
public void removeLocalAuthor(AuthorId a) throws DbException {
|
||||
LocalAuthor localAuthor = db.getLocalAuthor(a);
|
||||
db.setLocalAuthorStatus(a, REMOVING);
|
||||
for (RemoveIdentityHook hook : removeHooks)
|
||||
hook.removingIdentity(localAuthor);
|
||||
db.removeLocalAuthor(a);
|
||||
Transaction txn = db.startTransaction();
|
||||
try {
|
||||
LocalAuthor localAuthor = db.getLocalAuthor(txn, a);
|
||||
db.setLocalAuthorStatus(txn, a, REMOVING);
|
||||
for (RemoveIdentityHook hook : removeHooks)
|
||||
hook.removingIdentity(txn, localAuthor);
|
||||
db.removeLocalAuthor(txn, a);
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
eventBus.broadcast(new LocalAuthorRemovedEvent(a));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import com.google.inject.Inject;
|
||||
import org.briarproject.api.FormatException;
|
||||
import org.briarproject.api.contact.Contact;
|
||||
import org.briarproject.api.contact.ContactId;
|
||||
import org.briarproject.api.contact.ContactManager;
|
||||
import org.briarproject.api.contact.ContactManager.AddContactHook;
|
||||
import org.briarproject.api.contact.ContactManager.RemoveContactHook;
|
||||
import org.briarproject.api.data.BdfDictionary;
|
||||
@@ -16,7 +15,7 @@ import org.briarproject.api.data.MetadataParser;
|
||||
import org.briarproject.api.db.DatabaseComponent;
|
||||
import org.briarproject.api.db.DbException;
|
||||
import org.briarproject.api.db.Metadata;
|
||||
import org.briarproject.api.db.NoSuchContactException;
|
||||
import org.briarproject.api.db.Transaction;
|
||||
import org.briarproject.api.messaging.MessagingManager;
|
||||
import org.briarproject.api.messaging.PrivateMessage;
|
||||
import org.briarproject.api.messaging.PrivateMessageHeader;
|
||||
@@ -50,19 +49,17 @@ class MessagingManagerImpl implements MessagingManager, AddContactHook,
|
||||
Logger.getLogger(MessagingManagerImpl.class.getName());
|
||||
|
||||
private final DatabaseComponent db;
|
||||
private final ContactManager contactManager;
|
||||
private final PrivateGroupFactory privateGroupFactory;
|
||||
private final BdfReaderFactory bdfReaderFactory;
|
||||
private final MetadataEncoder metadataEncoder;
|
||||
private final MetadataParser metadataParser;
|
||||
|
||||
@Inject
|
||||
MessagingManagerImpl(DatabaseComponent db, ContactManager contactManager,
|
||||
MessagingManagerImpl(DatabaseComponent db,
|
||||
PrivateGroupFactory privateGroupFactory,
|
||||
BdfReaderFactory bdfReaderFactory, MetadataEncoder metadataEncoder,
|
||||
MetadataParser metadataParser) {
|
||||
this.db = db;
|
||||
this.contactManager = contactManager;
|
||||
this.privateGroupFactory = privateGroupFactory;
|
||||
this.bdfReaderFactory = bdfReaderFactory;
|
||||
this.metadataEncoder = metadataEncoder;
|
||||
@@ -70,19 +67,17 @@ class MessagingManagerImpl implements MessagingManager, AddContactHook,
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addingContact(Contact c) {
|
||||
public void addingContact(Transaction txn, Contact c) throws DbException {
|
||||
try {
|
||||
// Create a group to share with the contact
|
||||
Group g = getContactGroup(c);
|
||||
// Store the group and share it with the contact
|
||||
db.addGroup(g);
|
||||
db.setVisibleToContact(c.getId(), g.getId(), true);
|
||||
db.addGroup(txn, g);
|
||||
db.setVisibleToContact(txn, c.getId(), g.getId(), true);
|
||||
// Attach the contact ID to the group
|
||||
BdfDictionary d = new BdfDictionary();
|
||||
d.put("contactId", c.getId().getInt());
|
||||
db.mergeGroupMetadata(g.getId(), metadataEncoder.encode(d));
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
db.mergeGroupMetadata(txn, g.getId(), metadataEncoder.encode(d));
|
||||
} catch (FormatException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
@@ -93,12 +88,8 @@ class MessagingManagerImpl implements MessagingManager, AddContactHook,
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removingContact(Contact c) {
|
||||
try {
|
||||
db.removeGroup(getContactGroup(c));
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
}
|
||||
public void removingContact(Transaction txn, Contact c) throws DbException {
|
||||
db.removeGroup(txn, getContactGroup(c));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -108,15 +99,22 @@ class MessagingManagerImpl implements MessagingManager, AddContactHook,
|
||||
|
||||
@Override
|
||||
public void addLocalMessage(PrivateMessage m) throws DbException {
|
||||
BdfDictionary d = new BdfDictionary();
|
||||
d.put("timestamp", m.getMessage().getTimestamp());
|
||||
if (m.getParent() != null) d.put("parent", m.getParent().getBytes());
|
||||
d.put("contentType", m.getContentType());
|
||||
d.put("local", true);
|
||||
d.put("read", true);
|
||||
try {
|
||||
BdfDictionary d = new BdfDictionary();
|
||||
d.put("timestamp", m.getMessage().getTimestamp());
|
||||
if (m.getParent() != null)
|
||||
d.put("parent", m.getParent().getBytes());
|
||||
d.put("contentType", m.getContentType());
|
||||
d.put("local", true);
|
||||
d.put("read", true);
|
||||
Metadata meta = metadataEncoder.encode(d);
|
||||
db.addLocalMessage(m.getMessage(), CLIENT_ID, meta, true);
|
||||
Transaction txn = db.startTransaction();
|
||||
try {
|
||||
db.addLocalMessage(txn, m.getMessage(), CLIENT_ID, meta, true);
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
} catch (FormatException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
@@ -125,26 +123,48 @@ class MessagingManagerImpl implements MessagingManager, AddContactHook,
|
||||
@Override
|
||||
public ContactId getContactId(GroupId g) throws DbException {
|
||||
try {
|
||||
BdfDictionary d = metadataParser.parse(db.getGroupMetadata(g));
|
||||
long id = d.getInteger("contactId");
|
||||
return new ContactId((int) id);
|
||||
Metadata meta;
|
||||
Transaction txn = db.startTransaction();
|
||||
try {
|
||||
meta = db.getGroupMetadata(txn, g);
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
BdfDictionary d = metadataParser.parse(meta);
|
||||
return new ContactId(d.getInteger("contactId").intValue());
|
||||
} catch (FormatException e) {
|
||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
throw new NoSuchContactException();
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public GroupId getConversationId(ContactId c) throws DbException {
|
||||
return getContactGroup(contactManager.getContact(c)).getId();
|
||||
Contact contact;
|
||||
Transaction txn = db.startTransaction();
|
||||
try {
|
||||
contact = db.getContact(txn, c);
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
return getContactGroup(contact).getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<PrivateMessageHeader> getMessageHeaders(ContactId c)
|
||||
throws DbException {
|
||||
GroupId groupId = getConversationId(c);
|
||||
Map<MessageId, Metadata> metadata = db.getMessageMetadata(groupId);
|
||||
Collection<MessageStatus> statuses = db.getMessageStatus(c, groupId);
|
||||
GroupId g = getConversationId(c);
|
||||
Map<MessageId, Metadata> metadata;
|
||||
Collection<MessageStatus> statuses;
|
||||
Transaction txn = db.startTransaction();
|
||||
try {
|
||||
metadata = db.getMessageMetadata(txn, g);
|
||||
statuses = db.getMessageStatus(txn, c, g);
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
Collection<PrivateMessageHeader> headers =
|
||||
new ArrayList<PrivateMessageHeader>();
|
||||
for (MessageStatus s : statuses) {
|
||||
@@ -168,7 +188,14 @@ class MessagingManagerImpl implements MessagingManager, AddContactHook,
|
||||
|
||||
@Override
|
||||
public byte[] getMessageBody(MessageId m) throws DbException {
|
||||
byte[] raw = db.getRawMessage(m);
|
||||
byte[] raw;
|
||||
Transaction txn = db.startTransaction();
|
||||
try {
|
||||
raw = db.getRawMessage(txn, m);
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(raw,
|
||||
MESSAGE_HEADER_LENGTH, raw.length - MESSAGE_HEADER_LENGTH);
|
||||
BdfReader r = bdfReaderFactory.createReader(in);
|
||||
@@ -191,10 +218,17 @@ class MessagingManagerImpl implements MessagingManager, AddContactHook,
|
||||
|
||||
@Override
|
||||
public void setReadFlag(MessageId m, boolean read) throws DbException {
|
||||
BdfDictionary d = new BdfDictionary();
|
||||
d.put("read", read);
|
||||
try {
|
||||
db.mergeMessageMetadata(m, metadataEncoder.encode(d));
|
||||
BdfDictionary d = new BdfDictionary();
|
||||
d.put("read", read);
|
||||
Metadata meta = metadataEncoder.encode(d);
|
||||
Transaction txn = db.startTransaction();
|
||||
try {
|
||||
db.mergeMessageMetadata(txn, m, meta);
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
} catch (FormatException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ 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;
|
||||
@@ -27,6 +28,7 @@ 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;
|
||||
|
||||
@@ -60,6 +62,7 @@ class PluginManagerImpl implements PluginManager, Service {
|
||||
private final DatabaseComponent db;
|
||||
private final Poller poller;
|
||||
private final ConnectionManager connectionManager;
|
||||
private final SettingsManager settingsManager;
|
||||
private final TransportPropertyManager transportPropertyManager;
|
||||
private final UiCallback uiCallback;
|
||||
private final Map<TransportId, Plugin> plugins;
|
||||
@@ -72,6 +75,7 @@ class PluginManagerImpl implements PluginManager, Service {
|
||||
DuplexPluginConfig duplexPluginConfig, Clock clock,
|
||||
DatabaseComponent db, Poller poller,
|
||||
ConnectionManager connectionManager,
|
||||
SettingsManager settingsManager,
|
||||
TransportPropertyManager transportPropertyManager,
|
||||
UiCallback uiCallback) {
|
||||
this.ioExecutor = ioExecutor;
|
||||
@@ -82,6 +86,7 @@ class PluginManagerImpl implements PluginManager, Service {
|
||||
this.db = db;
|
||||
this.poller = poller;
|
||||
this.connectionManager = connectionManager;
|
||||
this.settingsManager = settingsManager;
|
||||
this.transportPropertyManager = transportPropertyManager;
|
||||
this.uiCallback = uiCallback;
|
||||
plugins = new ConcurrentHashMap<TransportId, Plugin>();
|
||||
@@ -181,7 +186,13 @@ class PluginManagerImpl implements PluginManager, Service {
|
||||
}
|
||||
try {
|
||||
long start = clock.currentTimeMillis();
|
||||
db.addTransport(id, plugin.getMaxLatency());
|
||||
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");
|
||||
@@ -244,7 +255,13 @@ class PluginManagerImpl implements PluginManager, Service {
|
||||
}
|
||||
try {
|
||||
long start = clock.currentTimeMillis();
|
||||
db.addTransport(id, plugin.getMaxLatency());
|
||||
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");
|
||||
@@ -319,7 +336,7 @@ class PluginManagerImpl implements PluginManager, Service {
|
||||
|
||||
public Settings getSettings() {
|
||||
try {
|
||||
return db.getSettings(id.getString());
|
||||
return settingsManager.getSettings(id.getString());
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
return new Settings();
|
||||
@@ -348,7 +365,7 @@ class PluginManagerImpl implements PluginManager, Service {
|
||||
|
||||
public void mergeSettings(Settings s) {
|
||||
try {
|
||||
db.mergeSettings(s, id.getString());
|
||||
settingsManager.mergeSettings(s, id.getString());
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import org.briarproject.api.FormatException;
|
||||
import org.briarproject.api.TransportId;
|
||||
import org.briarproject.api.contact.Contact;
|
||||
import org.briarproject.api.contact.ContactId;
|
||||
import org.briarproject.api.contact.ContactManager;
|
||||
import org.briarproject.api.contact.ContactManager.AddContactHook;
|
||||
import org.briarproject.api.contact.ContactManager.RemoveContactHook;
|
||||
import org.briarproject.api.data.BdfDictionary;
|
||||
@@ -21,6 +20,7 @@ import org.briarproject.api.db.DatabaseComponent;
|
||||
import org.briarproject.api.db.DbException;
|
||||
import org.briarproject.api.db.Metadata;
|
||||
import org.briarproject.api.db.NoSuchGroupException;
|
||||
import org.briarproject.api.db.Transaction;
|
||||
import org.briarproject.api.properties.TransportProperties;
|
||||
import org.briarproject.api.properties.TransportPropertyManager;
|
||||
import org.briarproject.api.sync.ClientId;
|
||||
@@ -41,10 +41,7 @@ import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static org.briarproject.api.properties.TransportPropertyConstants.MAX_PROPERTY_LENGTH;
|
||||
import static org.briarproject.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
|
||||
|
||||
@@ -57,11 +54,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
|
||||
|
||||
private static final byte[] LOCAL_GROUP_DESCRIPTOR = new byte[0];
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(TransportPropertyManagerImpl.class.getName());
|
||||
|
||||
private final DatabaseComponent db;
|
||||
private final ContactManager contactManager;
|
||||
private final PrivateGroupFactory privateGroupFactory;
|
||||
private final MessageFactory messageFactory;
|
||||
private final BdfReaderFactory bdfReaderFactory;
|
||||
@@ -71,18 +64,13 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
|
||||
private final Clock clock;
|
||||
private final Group localGroup;
|
||||
|
||||
/** Ensures isolation between database reads and writes. */
|
||||
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
|
||||
|
||||
@Inject
|
||||
TransportPropertyManagerImpl(DatabaseComponent db,
|
||||
ContactManager contactManager, GroupFactory groupFactory,
|
||||
PrivateGroupFactory privateGroupFactory,
|
||||
GroupFactory groupFactory, PrivateGroupFactory privateGroupFactory,
|
||||
MessageFactory messageFactory, BdfReaderFactory bdfReaderFactory,
|
||||
BdfWriterFactory bdfWriterFactory, MetadataEncoder metadataEncoder,
|
||||
MetadataParser metadataParser, Clock clock) {
|
||||
this.db = db;
|
||||
this.contactManager = contactManager;
|
||||
this.privateGroupFactory = privateGroupFactory;
|
||||
this.messageFactory = messageFactory;
|
||||
this.bdfReaderFactory = bdfReaderFactory;
|
||||
@@ -95,165 +83,171 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addingContact(Contact c) {
|
||||
lock.writeLock().lock();
|
||||
try {
|
||||
// Create a group to share with the contact
|
||||
Group g = getContactGroup(c);
|
||||
// Store the group and share it with the contact
|
||||
db.addGroup(g);
|
||||
db.setVisibleToContact(c.getId(), g.getId(), true);
|
||||
// Copy the latest local properties into the group
|
||||
DeviceId dev = db.getDeviceId();
|
||||
Map<TransportId, TransportProperties> local = getLocalProperties();
|
||||
for (Entry<TransportId, TransportProperties> e : local.entrySet()) {
|
||||
storeMessage(g.getId(), dev, e.getKey(), e.getValue(), 1, true,
|
||||
true);
|
||||
}
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
} catch (FormatException e) {
|
||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
} finally {
|
||||
lock.writeLock().unlock();
|
||||
public void addingContact(Transaction txn, Contact c) throws DbException {
|
||||
// Create a group to share with the contact
|
||||
Group g = getContactGroup(c);
|
||||
// Store the group and share it with the contact
|
||||
db.addGroup(txn, g);
|
||||
db.setVisibleToContact(txn, c.getId(), g.getId(), true);
|
||||
// Copy the latest local properties into the group
|
||||
DeviceId dev = db.getDeviceId(txn);
|
||||
Map<TransportId, TransportProperties> local = getLocalProperties();
|
||||
for (Entry<TransportId, TransportProperties> e : local.entrySet()) {
|
||||
storeMessage(txn, g.getId(), dev, e.getKey(), e.getValue(), 1,
|
||||
true, true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removingContact(Contact c) {
|
||||
lock.writeLock().lock();
|
||||
try {
|
||||
db.removeGroup(getContactGroup(c));
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
} finally {
|
||||
lock.writeLock().unlock();
|
||||
}
|
||||
public void removingContact(Transaction txn, Contact c) throws DbException {
|
||||
db.removeGroup(txn, getContactGroup(c));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRemoteProperties(ContactId c, DeviceId dev,
|
||||
Map<TransportId, TransportProperties> props) throws DbException {
|
||||
lock.writeLock().lock();
|
||||
Transaction txn = db.startTransaction();
|
||||
try {
|
||||
Group g = getContactGroup(contactManager.getContact(c));
|
||||
Group g = getContactGroup(db.getContact(txn, c));
|
||||
for (Entry<TransportId, TransportProperties> e : props.entrySet()) {
|
||||
storeMessage(g.getId(), dev, e.getKey(), e.getValue(), 0, false,
|
||||
false);
|
||||
storeMessage(txn, g.getId(), dev, e.getKey(), e.getValue(), 0,
|
||||
false, false);
|
||||
}
|
||||
} catch (FormatException e) {
|
||||
throw new DbException(e);
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
lock.writeLock().unlock();
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<TransportId, TransportProperties> getLocalProperties()
|
||||
throws DbException {
|
||||
lock.readLock().lock();
|
||||
try {
|
||||
// Find the latest local update for each transport
|
||||
Map<TransportId, LatestUpdate> latest =
|
||||
findLatest(localGroup.getId(), true);
|
||||
// Retrieve and parse the latest local properties
|
||||
Map<TransportId, TransportProperties> local =
|
||||
new HashMap<TransportId, TransportProperties>();
|
||||
for (Entry<TransportId, LatestUpdate> e : latest.entrySet()) {
|
||||
byte[] raw = db.getRawMessage(e.getValue().messageId);
|
||||
local.put(e.getKey(), parseProperties(raw));
|
||||
Transaction txn = db.startTransaction();
|
||||
try {
|
||||
// Find the latest local update for each transport
|
||||
Map<TransportId, LatestUpdate> latest = findLatest(txn,
|
||||
localGroup.getId(), true);
|
||||
// Retrieve and parse the latest local properties
|
||||
for (Entry<TransportId, LatestUpdate> e : latest.entrySet()) {
|
||||
byte[] raw = db.getRawMessage(txn, e.getValue().messageId);
|
||||
local.put(e.getKey(), parseProperties(raw));
|
||||
}
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
return Collections.unmodifiableMap(local);
|
||||
} catch (NoSuchGroupException e) {
|
||||
// Local group doesn't exist - there are no local properties
|
||||
return Collections.emptyMap();
|
||||
} catch (IOException e) {
|
||||
} catch (FormatException e) {
|
||||
throw new DbException(e);
|
||||
} finally {
|
||||
lock.readLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransportProperties getLocalProperties(TransportId t)
|
||||
throws DbException {
|
||||
lock.readLock().lock();
|
||||
try {
|
||||
// Find the latest local update
|
||||
LatestUpdate latest = findLatest(localGroup.getId(), t, true);
|
||||
if (latest == null) return null;
|
||||
// Retrieve and parse the latest local properties
|
||||
return parseProperties(db.getRawMessage(latest.messageId));
|
||||
TransportProperties p = null;
|
||||
Transaction txn = db.startTransaction();
|
||||
try {
|
||||
// Find the latest local update
|
||||
LatestUpdate latest = findLatest(txn, localGroup.getId(), t,
|
||||
true);
|
||||
if (latest != null) {
|
||||
// Retrieve and parse the latest local properties
|
||||
byte[] raw = db.getRawMessage(txn, latest.messageId);
|
||||
p = parseProperties(raw);
|
||||
}
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
return p;
|
||||
} catch (NoSuchGroupException e) {
|
||||
// Local group doesn't exist - there are no local properties
|
||||
return null;
|
||||
} catch (IOException e) {
|
||||
} catch (FormatException e) {
|
||||
throw new DbException(e);
|
||||
} finally {
|
||||
lock.readLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<ContactId, TransportProperties> getRemoteProperties(
|
||||
TransportId t) throws DbException {
|
||||
lock.readLock().lock();
|
||||
try {
|
||||
Map<ContactId, TransportProperties> remote =
|
||||
new HashMap<ContactId, TransportProperties>();
|
||||
for (Contact c : contactManager.getContacts()) {
|
||||
Group g = getContactGroup(c);
|
||||
// Find the latest remote update
|
||||
LatestUpdate latest = findLatest(g.getId(), t, false);
|
||||
if (latest != null) {
|
||||
// Retrieve and parse the latest remote properties
|
||||
byte[] raw = db.getRawMessage(latest.messageId);
|
||||
remote.put(c.getId(), parseProperties(raw));
|
||||
Transaction txn = db.startTransaction();
|
||||
try {
|
||||
for (Contact c : db.getContacts(txn)) {
|
||||
Group g = getContactGroup(c);
|
||||
// Find the latest remote update
|
||||
LatestUpdate latest = findLatest(txn, g.getId(), t, false);
|
||||
if (latest != null) {
|
||||
// Retrieve and parse the latest remote properties
|
||||
byte[] raw = db.getRawMessage(txn, latest.messageId);
|
||||
remote.put(c.getId(), parseProperties(raw));
|
||||
}
|
||||
}
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
return Collections.unmodifiableMap(remote);
|
||||
} catch (IOException e) {
|
||||
} catch (FormatException e) {
|
||||
throw new DbException(e);
|
||||
} finally {
|
||||
lock.readLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mergeLocalProperties(TransportId t, TransportProperties p)
|
||||
throws DbException {
|
||||
lock.writeLock().lock();
|
||||
try {
|
||||
// Create the local group if necessary
|
||||
db.addGroup(localGroup);
|
||||
// Merge the new properties with any existing properties
|
||||
TransportProperties merged;
|
||||
LatestUpdate latest = findLatest(localGroup.getId(), t, true);
|
||||
if (latest == null) {
|
||||
merged = p;
|
||||
} else {
|
||||
byte[] raw = db.getRawMessage(latest.messageId);
|
||||
TransportProperties old = parseProperties(raw);
|
||||
merged = new TransportProperties(old);
|
||||
merged.putAll(p);
|
||||
if (merged.equals(old)) return; // Unchanged
|
||||
Transaction txn = db.startTransaction();
|
||||
try {
|
||||
// Create the local group if necessary
|
||||
db.addGroup(txn, localGroup);
|
||||
// Merge the new properties with any existing properties
|
||||
TransportProperties merged;
|
||||
boolean changed;
|
||||
LatestUpdate latest = findLatest(txn, localGroup.getId(), t,
|
||||
true);
|
||||
if (latest == null) {
|
||||
merged = p;
|
||||
changed = true;
|
||||
} else {
|
||||
byte[] raw = db.getRawMessage(txn, latest.messageId);
|
||||
TransportProperties old = parseProperties(raw);
|
||||
merged = new TransportProperties(old);
|
||||
merged.putAll(p);
|
||||
changed = !merged.equals(old);
|
||||
}
|
||||
if (changed) {
|
||||
// Store the merged properties in the local group
|
||||
DeviceId dev = db.getDeviceId(txn);
|
||||
long version = latest == null ? 1 : latest.version + 1;
|
||||
storeMessage(txn, localGroup.getId(), dev, t, merged,
|
||||
version, true, false);
|
||||
// Store the merged properties in each contact's group
|
||||
for (Contact c : db.getContacts(txn)) {
|
||||
Group g = getContactGroup(c);
|
||||
latest = findLatest(txn, g.getId(), t, true);
|
||||
version = latest == null ? 1 : latest.version + 1;
|
||||
storeMessage(txn, g.getId(), dev, t, merged, version,
|
||||
true, true);
|
||||
}
|
||||
}
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
// Store the merged properties in the local group
|
||||
DeviceId dev = db.getDeviceId();
|
||||
long version = latest == null ? 1 : latest.version + 1;
|
||||
storeMessage(localGroup.getId(), dev, t, merged, version, true,
|
||||
false);
|
||||
// Store the merged properties in each contact's group
|
||||
for (Contact c : contactManager.getContacts()) {
|
||||
Group g = getContactGroup(c);
|
||||
latest = findLatest(g.getId(), t, true);
|
||||
version = latest == null ? 1 : latest.version + 1;
|
||||
storeMessage(g.getId(), dev, t, merged, version, true, true);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
} catch (FormatException e) {
|
||||
throw new DbException(e);
|
||||
} finally {
|
||||
lock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,18 +255,22 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
|
||||
return privateGroupFactory.createPrivateGroup(CLIENT_ID, c);
|
||||
}
|
||||
|
||||
// Locking: lock.writeLock
|
||||
private void storeMessage(GroupId g, DeviceId dev, TransportId t,
|
||||
TransportProperties p, long version, boolean local, boolean shared)
|
||||
throws DbException, FormatException {
|
||||
byte[] body = encodeProperties(dev, t, p, version);
|
||||
long now = clock.currentTimeMillis();
|
||||
Message m = messageFactory.createMessage(g, now, body);
|
||||
BdfDictionary d = new BdfDictionary();
|
||||
d.put("transportId", t.getString());
|
||||
d.put("version", version);
|
||||
d.put("local", local);
|
||||
db.addLocalMessage(m, CLIENT_ID, metadataEncoder.encode(d), shared);
|
||||
private void storeMessage(Transaction txn, GroupId g, DeviceId dev,
|
||||
TransportId t, TransportProperties p, long version, boolean local,
|
||||
boolean shared) throws DbException {
|
||||
try {
|
||||
byte[] body = encodeProperties(dev, t, p, version);
|
||||
long now = clock.currentTimeMillis();
|
||||
Message m = messageFactory.createMessage(g, now, body);
|
||||
BdfDictionary d = new BdfDictionary();
|
||||
d.put("transportId", t.getString());
|
||||
d.put("version", version);
|
||||
d.put("local", local);
|
||||
Metadata meta = metadataEncoder.encode(d);
|
||||
db.addLocalMessage(txn, m, CLIENT_ID, meta, shared);
|
||||
} catch (FormatException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] encodeProperties(DeviceId dev, TransportId t,
|
||||
@@ -293,12 +291,11 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
// Locking: lock.readLock
|
||||
private Map<TransportId, LatestUpdate> findLatest(GroupId g, boolean local)
|
||||
throws DbException, FormatException {
|
||||
private Map<TransportId, LatestUpdate> findLatest(Transaction txn,
|
||||
GroupId g, boolean local) throws DbException, FormatException {
|
||||
Map<TransportId, LatestUpdate> latestUpdates =
|
||||
new HashMap<TransportId, LatestUpdate>();
|
||||
Map<MessageId, Metadata> metadata = db.getMessageMetadata(g);
|
||||
Map<MessageId, Metadata> metadata = db.getMessageMetadata(txn, g);
|
||||
for (Entry<MessageId, Metadata> e : metadata.entrySet()) {
|
||||
BdfDictionary d = metadataParser.parse(e.getValue());
|
||||
if (d.getBoolean("local") == local) {
|
||||
@@ -312,11 +309,10 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
|
||||
return latestUpdates;
|
||||
}
|
||||
|
||||
// Locking: lock.readLock
|
||||
private LatestUpdate findLatest(GroupId g, TransportId t, boolean local)
|
||||
throws DbException, FormatException {
|
||||
private LatestUpdate findLatest(Transaction txn, GroupId g, TransportId t,
|
||||
boolean local) throws DbException, FormatException {
|
||||
LatestUpdate latest = null;
|
||||
Map<MessageId, Metadata> metadata = db.getMessageMetadata(g);
|
||||
Map<MessageId, Metadata> metadata = db.getMessageMetadata(txn, g);
|
||||
for (Entry<MessageId, Metadata> e : metadata.entrySet()) {
|
||||
BdfDictionary d = metadataParser.parse(e.getValue());
|
||||
if (d.getString("transportId").equals(t.getString())
|
||||
@@ -330,25 +326,32 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
|
||||
}
|
||||
|
||||
private TransportProperties parseProperties(byte[] raw)
|
||||
throws IOException {
|
||||
throws FormatException {
|
||||
TransportProperties p = new TransportProperties();
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(raw,
|
||||
MESSAGE_HEADER_LENGTH, raw.length - MESSAGE_HEADER_LENGTH);
|
||||
BdfReader r = bdfReaderFactory.createReader(in);
|
||||
r.readListStart();
|
||||
r.skipRaw(); // Device ID
|
||||
r.skipString(); // Transport ID
|
||||
r.skipInteger(); // Version
|
||||
r.readDictionaryStart();
|
||||
while (!r.hasDictionaryEnd()) {
|
||||
String key = r.readString(MAX_PROPERTY_LENGTH);
|
||||
String value = r.readString(MAX_PROPERTY_LENGTH);
|
||||
p.put(key, value);
|
||||
try {
|
||||
r.readListStart();
|
||||
r.skipRaw(); // Device ID
|
||||
r.skipString(); // Transport ID
|
||||
r.skipInteger(); // Version
|
||||
r.readDictionaryStart();
|
||||
while (!r.hasDictionaryEnd()) {
|
||||
String key = r.readString(MAX_PROPERTY_LENGTH);
|
||||
String value = r.readString(MAX_PROPERTY_LENGTH);
|
||||
p.put(key, value);
|
||||
}
|
||||
r.readDictionaryEnd();
|
||||
r.readListEnd();
|
||||
if (!r.eof()) throw new FormatException();
|
||||
return p;
|
||||
} catch (FormatException e) {
|
||||
throw e;
|
||||
} catch (IOException e) {
|
||||
// Shouldn't happen with ByteArrayInputStream
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
r.readDictionaryEnd();
|
||||
r.readListEnd();
|
||||
if (!r.eof()) throw new FormatException();
|
||||
return p;
|
||||
}
|
||||
|
||||
private static class LatestUpdate {
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.google.inject.Inject;
|
||||
|
||||
import org.briarproject.api.db.DatabaseComponent;
|
||||
import org.briarproject.api.db.DbException;
|
||||
import org.briarproject.api.db.Transaction;
|
||||
import org.briarproject.api.settings.Settings;
|
||||
import org.briarproject.api.settings.SettingsManager;
|
||||
|
||||
@@ -18,11 +19,24 @@ class SettingsManagerImpl implements SettingsManager {
|
||||
|
||||
@Override
|
||||
public Settings getSettings(String namespace) throws DbException {
|
||||
return db.getSettings(namespace);
|
||||
Transaction txn = db.startTransaction();
|
||||
try {
|
||||
Settings s = db.getSettings(txn, namespace);
|
||||
txn.setComplete();
|
||||
return s;
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mergeSettings(Settings s, String namespace) throws DbException {
|
||||
db.mergeSettings(s, namespace);
|
||||
Transaction txn = db.startTransaction();
|
||||
try {
|
||||
db.mergeSettings(txn, s, namespace);
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ 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.ContactRemovedEvent;
|
||||
import org.briarproject.api.event.Event;
|
||||
import org.briarproject.api.event.EventBus;
|
||||
@@ -50,8 +51,8 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
|
||||
|
||||
private static final ThrowingRunnable<IOException> CLOSE =
|
||||
new ThrowingRunnable<IOException>() {
|
||||
public void run() {}
|
||||
};
|
||||
public void run() {}
|
||||
};
|
||||
|
||||
private final DatabaseComponent db;
|
||||
private final Executor dbExecutor;
|
||||
@@ -178,7 +179,14 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
|
||||
public void run() {
|
||||
if (interrupted) return;
|
||||
try {
|
||||
Ack a = db.generateAck(contactId, MAX_MESSAGE_IDS);
|
||||
Ack a;
|
||||
Transaction txn = db.startTransaction();
|
||||
try {
|
||||
a = db.generateAck(txn, contactId, MAX_MESSAGE_IDS);
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Generated ack: " + (a != null));
|
||||
if (a != null) writerTasks.add(new WriteAck(a));
|
||||
@@ -212,8 +220,15 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
|
||||
public void run() {
|
||||
if (interrupted) return;
|
||||
try {
|
||||
Collection<byte[]> b = db.generateRequestedBatch(contactId,
|
||||
MAX_PACKET_PAYLOAD_LENGTH, maxLatency);
|
||||
Collection<byte[]> b;
|
||||
Transaction txn = db.startTransaction();
|
||||
try {
|
||||
b = db.generateRequestedBatch(txn, contactId,
|
||||
MAX_PACKET_PAYLOAD_LENGTH, maxLatency);
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Generated batch: " + (b != null));
|
||||
if (b != null) writerTasks.add(new WriteBatch(b));
|
||||
@@ -247,8 +262,15 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
|
||||
public void run() {
|
||||
if (interrupted) return;
|
||||
try {
|
||||
Offer o = db.generateOffer(contactId, MAX_MESSAGE_IDS,
|
||||
maxLatency);
|
||||
Offer o;
|
||||
Transaction txn = db.startTransaction();
|
||||
try {
|
||||
o = db.generateOffer(txn, contactId, MAX_MESSAGE_IDS,
|
||||
maxLatency);
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Generated offer: " + (o != null));
|
||||
if (o != null) writerTasks.add(new WriteOffer(o));
|
||||
@@ -282,7 +304,14 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
|
||||
public void run() {
|
||||
if (interrupted) return;
|
||||
try {
|
||||
Request r = db.generateRequest(contactId, MAX_MESSAGE_IDS);
|
||||
Request r;
|
||||
Transaction txn = db.startTransaction();
|
||||
try {
|
||||
r = db.generateRequest(txn, contactId, MAX_MESSAGE_IDS);
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Generated request: " + (r != null));
|
||||
if (r != null) writerTasks.add(new WriteRequest(r));
|
||||
|
||||
@@ -5,6 +5,7 @@ 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.ContactRemovedEvent;
|
||||
import org.briarproject.api.event.Event;
|
||||
import org.briarproject.api.event.EventBus;
|
||||
@@ -24,7 +25,9 @@ import java.util.logging.Logger;
|
||||
|
||||
import static java.util.logging.Level.WARNING;
|
||||
|
||||
/** An incoming {@link org.briarproject.api.sync.SyncSession SyncSession}. */
|
||||
/**
|
||||
* An incoming {@link org.briarproject.api.sync.SyncSession SyncSession}.
|
||||
*/
|
||||
class IncomingSession implements SyncSession, EventListener {
|
||||
|
||||
private static final Logger LOG =
|
||||
@@ -103,7 +106,13 @@ class IncomingSession implements SyncSession, EventListener {
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
db.receiveAck(contactId, ack);
|
||||
Transaction txn = db.startTransaction();
|
||||
try {
|
||||
db.receiveAck(txn, contactId, ack);
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
interrupt();
|
||||
@@ -121,7 +130,13 @@ class IncomingSession implements SyncSession, EventListener {
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
db.receiveMessage(contactId, message);
|
||||
Transaction txn = db.startTransaction();
|
||||
try {
|
||||
db.receiveMessage(txn, contactId, message);
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
interrupt();
|
||||
@@ -139,7 +154,13 @@ class IncomingSession implements SyncSession, EventListener {
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
db.receiveOffer(contactId, offer);
|
||||
Transaction txn = db.startTransaction();
|
||||
try {
|
||||
db.receiveOffer(txn, contactId, offer);
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
interrupt();
|
||||
@@ -157,7 +178,13 @@ class IncomingSession implements SyncSession, EventListener {
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
db.receiveRequest(contactId, request);
|
||||
Transaction txn = db.startTransaction();
|
||||
try {
|
||||
db.receiveRequest(txn, contactId, request);
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
interrupt();
|
||||
|
||||
@@ -4,6 +4,7 @@ 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.ContactRemovedEvent;
|
||||
import org.briarproject.api.event.Event;
|
||||
import org.briarproject.api.event.EventBus;
|
||||
@@ -40,8 +41,8 @@ class SimplexOutgoingSession implements SyncSession, EventListener {
|
||||
|
||||
private static final ThrowingRunnable<IOException> CLOSE =
|
||||
new ThrowingRunnable<IOException>() {
|
||||
public void run() {}
|
||||
};
|
||||
public void run() {}
|
||||
};
|
||||
|
||||
private final DatabaseComponent db;
|
||||
private final Executor dbExecutor;
|
||||
@@ -119,7 +120,14 @@ class SimplexOutgoingSession implements SyncSession, EventListener {
|
||||
public void run() {
|
||||
if (interrupted) return;
|
||||
try {
|
||||
Ack a = db.generateAck(contactId, MAX_MESSAGE_IDS);
|
||||
Ack a;
|
||||
Transaction txn = db.startTransaction();
|
||||
try {
|
||||
a = db.generateAck(txn, contactId, MAX_MESSAGE_IDS);
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Generated ack: " + (a != null));
|
||||
if (a == null) decrementOutstandingQueries();
|
||||
@@ -154,8 +162,15 @@ class SimplexOutgoingSession implements SyncSession, EventListener {
|
||||
public void run() {
|
||||
if (interrupted) return;
|
||||
try {
|
||||
Collection<byte[]> b = db.generateBatch(contactId,
|
||||
MAX_PACKET_PAYLOAD_LENGTH, maxLatency);
|
||||
Collection<byte[]> b;
|
||||
Transaction txn = db.startTransaction();
|
||||
try {
|
||||
b = db.generateBatch(txn, contactId,
|
||||
MAX_PACKET_PAYLOAD_LENGTH, maxLatency);
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Generated batch: " + (b != null));
|
||||
if (b == null) decrementOutstandingQueries();
|
||||
|
||||
@@ -9,7 +9,7 @@ import org.briarproject.api.db.DatabaseExecutor;
|
||||
import org.briarproject.api.db.DbException;
|
||||
import org.briarproject.api.db.Metadata;
|
||||
import org.briarproject.api.db.NoSuchGroupException;
|
||||
import org.briarproject.api.db.NoSuchMessageException;
|
||||
import org.briarproject.api.db.Transaction;
|
||||
import org.briarproject.api.event.Event;
|
||||
import org.briarproject.api.event.EventListener;
|
||||
import org.briarproject.api.event.MessageAddedEvent;
|
||||
@@ -82,14 +82,17 @@ class ValidationManagerImpl implements ValidationManager, Service,
|
||||
public void run() {
|
||||
try {
|
||||
// TODO: Don't do all of this in a single DB task
|
||||
for (MessageId id : db.getMessagesToValidate(c)) {
|
||||
try {
|
||||
Message m = parseMessage(id, db.getRawMessage(id));
|
||||
Group g = db.getGroup(m.getGroupId());
|
||||
Transaction txn = db.startTransaction();
|
||||
try {
|
||||
for (MessageId id : db.getMessagesToValidate(txn, c)) {
|
||||
byte[] raw = db.getRawMessage(txn, id);
|
||||
Message m = parseMessage(id, raw);
|
||||
Group g = db.getGroup(txn, m.getGroupId());
|
||||
validateMessage(m, g);
|
||||
} catch (NoSuchMessageException e) {
|
||||
LOG.info("Message removed before validation");
|
||||
}
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
@@ -127,17 +130,21 @@ class ValidationManagerImpl implements ValidationManager, Service,
|
||||
dbExecutor.execute(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
if (meta == null) {
|
||||
db.setMessageValid(m, c, false);
|
||||
} else {
|
||||
for (ValidationHook hook : hooks)
|
||||
hook.validatingMessage(m, c, meta);
|
||||
db.mergeMessageMetadata(m.getId(), meta);
|
||||
db.setMessageValid(m, c, true);
|
||||
db.setMessageShared(m, true);
|
||||
Transaction txn = db.startTransaction();
|
||||
try {
|
||||
if (meta == null) {
|
||||
db.setMessageValid(txn, m, c, false);
|
||||
} else {
|
||||
for (ValidationHook hook : hooks)
|
||||
hook.validatingMessage(txn, m, c, meta);
|
||||
db.mergeMessageMetadata(txn, m.getId(), meta);
|
||||
db.setMessageValid(txn, m, c, true);
|
||||
db.setMessageShared(txn, m, true);
|
||||
}
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
} catch (NoSuchMessageException e) {
|
||||
LOG.info("Message removed during validation");
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
@@ -159,7 +166,13 @@ class ValidationManagerImpl implements ValidationManager, Service,
|
||||
dbExecutor.execute(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
validateMessage(m, db.getGroup(m.getGroupId()));
|
||||
Transaction txn = db.startTransaction();
|
||||
try {
|
||||
validateMessage(m, db.getGroup(txn, m.getGroupId()));
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
} catch (NoSuchGroupException e) {
|
||||
LOG.info("Group removed before validation");
|
||||
} catch (DbException e) {
|
||||
|
||||
@@ -7,6 +7,7 @@ import org.briarproject.api.crypto.SecretKey;
|
||||
import org.briarproject.api.db.DatabaseComponent;
|
||||
import org.briarproject.api.db.DatabaseExecutor;
|
||||
import org.briarproject.api.db.DbException;
|
||||
import org.briarproject.api.db.Transaction;
|
||||
import org.briarproject.api.event.ContactRemovedEvent;
|
||||
import org.briarproject.api.event.Event;
|
||||
import org.briarproject.api.event.EventListener;
|
||||
@@ -55,7 +56,14 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
|
||||
@Override
|
||||
public boolean start() {
|
||||
try {
|
||||
Map<TransportId, Integer> latencies = db.getTransportLatencies();
|
||||
Map<TransportId, Integer> latencies;
|
||||
Transaction txn = db.startTransaction();
|
||||
try {
|
||||
latencies = db.getTransportLatencies(txn);
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
for (Entry<TransportId, Integer> e : latencies.entrySet())
|
||||
addTransport(e.getKey(), e.getValue());
|
||||
} catch (DbException e) {
|
||||
|
||||
@@ -7,6 +7,7 @@ import org.briarproject.api.crypto.CryptoComponent;
|
||||
import org.briarproject.api.crypto.SecretKey;
|
||||
import org.briarproject.api.db.DatabaseComponent;
|
||||
import org.briarproject.api.db.DbException;
|
||||
import org.briarproject.api.db.Transaction;
|
||||
import org.briarproject.api.system.Clock;
|
||||
import org.briarproject.api.system.Timer;
|
||||
import org.briarproject.api.transport.StreamContext;
|
||||
@@ -66,7 +67,13 @@ class TransportKeyManager extends TimerTask {
|
||||
// Load the transport keys from the DB
|
||||
Map<ContactId, TransportKeys> loaded;
|
||||
try {
|
||||
loaded = db.getTransportKeys(transportId);
|
||||
Transaction txn = db.startTransaction();
|
||||
try {
|
||||
loaded = db.getTransportKeys(txn, transportId);
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
return;
|
||||
@@ -90,7 +97,13 @@ class TransportKeyManager extends TimerTask {
|
||||
for (Entry<ContactId, TransportKeys> e : current.entrySet())
|
||||
addKeys(e.getKey(), new MutableTransportKeys(e.getValue()));
|
||||
// Write any rotated keys back to the DB
|
||||
db.updateTransportKeys(rotated);
|
||||
Transaction txn = db.startTransaction();
|
||||
try {
|
||||
db.updateTransportKeys(txn, rotated);
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
} finally {
|
||||
@@ -135,7 +148,13 @@ class TransportKeyManager extends TimerTask {
|
||||
// Initialise mutable state for the contact
|
||||
addKeys(c, new MutableTransportKeys(k));
|
||||
// Write the keys back to the DB
|
||||
db.addTransportKeys(c, k);
|
||||
Transaction txn = db.startTransaction();
|
||||
try {
|
||||
db.addTransportKeys(txn, c, k);
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
} finally {
|
||||
@@ -171,8 +190,14 @@ class TransportKeyManager extends TimerTask {
|
||||
outKeys.getStreamCounter());
|
||||
// Increment the stream counter and write it back to the DB
|
||||
outKeys.incrementStreamCounter();
|
||||
db.incrementStreamCounter(c, transportId,
|
||||
outKeys.getRotationPeriod());
|
||||
Transaction txn = db.startTransaction();
|
||||
try {
|
||||
db.incrementStreamCounter(txn, c, transportId,
|
||||
outKeys.getRotationPeriod());
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
return ctx;
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
@@ -210,9 +235,15 @@ class TransportKeyManager extends TimerTask {
|
||||
inContexts.remove(new Bytes(removeTag));
|
||||
}
|
||||
// Write the window back to the DB
|
||||
db.setReorderingWindow(tagCtx.contactId, transportId,
|
||||
inKeys.getRotationPeriod(), window.getBase(),
|
||||
window.getBitmap());
|
||||
Transaction txn = db.startTransaction();
|
||||
try {
|
||||
db.setReorderingWindow(txn, tagCtx.contactId, transportId,
|
||||
inKeys.getRotationPeriod(), window.getBase(),
|
||||
window.getBitmap());
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
return ctx;
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
@@ -249,7 +280,13 @@ class TransportKeyManager extends TimerTask {
|
||||
for (Entry<ContactId, TransportKeys> e : current.entrySet())
|
||||
addKeys(e.getKey(), new MutableTransportKeys(e.getValue()));
|
||||
// Write any rotated keys back to the DB
|
||||
db.updateTransportKeys(rotated);
|
||||
Transaction txn = db.startTransaction();
|
||||
try {
|
||||
db.updateTransportKeys(txn, rotated);
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
} finally {
|
||||
|
||||
@@ -14,6 +14,7 @@ import org.briarproject.api.db.NoSuchLocalAuthorException;
|
||||
import org.briarproject.api.db.NoSuchMessageException;
|
||||
import org.briarproject.api.db.NoSuchTransportException;
|
||||
import org.briarproject.api.db.StorageStatus;
|
||||
import org.briarproject.api.db.Transaction;
|
||||
import org.briarproject.api.event.EventBus;
|
||||
import org.briarproject.api.event.GroupAddedEvent;
|
||||
import org.briarproject.api.event.GroupRemovedEvent;
|
||||
@@ -50,11 +51,13 @@ import org.junit.Test;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
|
||||
import static org.briarproject.api.sync.SyncConstants.MAX_GROUP_DESCRIPTOR_LENGTH;
|
||||
import static org.briarproject.api.sync.ValidationManager.Validity.UNKNOWN;
|
||||
import static org.briarproject.api.sync.ValidationManager.Validity.VALID;
|
||||
import static org.briarproject.api.transport.TransportConstants.REORDERING_WINDOW_SIZE;
|
||||
import static org.briarproject.db.DatabaseConstants.MAX_OFFERED_MESSAGES;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
@@ -121,14 +124,14 @@ public class DatabaseComponentImplTest extends BriarTestCase {
|
||||
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
|
||||
final EventBus eventBus = context.mock(EventBus.class);
|
||||
context.checking(new Expectations() {{
|
||||
exactly(9).of(database).startTransaction();
|
||||
will(returnValue(txn));
|
||||
exactly(9).of(database).commitTransaction(txn);
|
||||
// open()
|
||||
oneOf(database).open();
|
||||
will(returnValue(false));
|
||||
oneOf(shutdown).addShutdownHook(with(any(Runnable.class)));
|
||||
will(returnValue(shutdownHandle));
|
||||
// startTransaction()
|
||||
oneOf(database).startTransaction();
|
||||
will(returnValue(txn));
|
||||
// addLocalAuthor()
|
||||
oneOf(database).containsLocalAuthor(txn, localAuthorId);
|
||||
will(returnValue(false));
|
||||
@@ -171,6 +174,8 @@ public class DatabaseComponentImplTest extends BriarTestCase {
|
||||
oneOf(database).containsLocalAuthor(txn, localAuthorId);
|
||||
will(returnValue(true));
|
||||
oneOf(database).removeLocalAuthor(txn, localAuthorId);
|
||||
// endTransaction()
|
||||
oneOf(database).commitTransaction(txn);
|
||||
// close()
|
||||
oneOf(shutdown).removeShutdownHook(shutdownHandle);
|
||||
oneOf(database).close();
|
||||
@@ -179,15 +184,21 @@ public class DatabaseComponentImplTest extends BriarTestCase {
|
||||
shutdown);
|
||||
|
||||
assertFalse(db.open());
|
||||
db.addLocalAuthor(localAuthor);
|
||||
assertEquals(contactId, db.addContact(author, localAuthorId));
|
||||
assertEquals(Collections.singletonList(contact), db.getContacts());
|
||||
db.addGroup(group); // First time - listeners called
|
||||
db.addGroup(group); // Second time - not called
|
||||
assertEquals(Collections.singletonList(group), db.getGroups(clientId));
|
||||
db.removeGroup(group);
|
||||
db.removeContact(contactId);
|
||||
db.removeLocalAuthor(localAuthorId);
|
||||
Transaction transaction = db.startTransaction();
|
||||
db.addLocalAuthor(transaction, localAuthor);
|
||||
assertEquals(contactId,
|
||||
db.addContact(transaction, author, localAuthorId));
|
||||
assertEquals(Collections.singletonList(contact),
|
||||
db.getContacts(transaction));
|
||||
db.addGroup(transaction, group); // First time - listeners called
|
||||
db.addGroup(transaction, group); // Second time - not called
|
||||
assertEquals(Collections.singletonList(group),
|
||||
db.getGroups(transaction, clientId));
|
||||
db.removeGroup(transaction, group);
|
||||
db.removeContact(transaction, contactId);
|
||||
db.removeLocalAuthor(transaction, localAuthorId);
|
||||
transaction.setComplete();
|
||||
db.endTransaction(transaction);
|
||||
db.close();
|
||||
|
||||
context.assertIsSatisfied();
|
||||
@@ -211,11 +222,14 @@ public class DatabaseComponentImplTest extends BriarTestCase {
|
||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||
shutdown);
|
||||
|
||||
Transaction transaction = db.startTransaction();
|
||||
try {
|
||||
db.addLocalMessage(message, clientId, metadata, true);
|
||||
db.addLocalMessage(transaction, message, clientId, metadata, true);
|
||||
fail();
|
||||
} catch (NoSuchGroupException expected) {
|
||||
// Expected
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
context.assertIsSatisfied();
|
||||
@@ -253,7 +267,13 @@ public class DatabaseComponentImplTest extends BriarTestCase {
|
||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||
shutdown);
|
||||
|
||||
db.addLocalMessage(message, clientId, metadata, true);
|
||||
Transaction transaction = db.startTransaction();
|
||||
try {
|
||||
db.addLocalMessage(transaction, message, clientId, metadata, true);
|
||||
transaction.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
@@ -277,126 +297,178 @@ public class DatabaseComponentImplTest extends BriarTestCase {
|
||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||
shutdown);
|
||||
|
||||
Transaction transaction = db.startTransaction();
|
||||
try {
|
||||
db.addTransportKeys(contactId, createTransportKeys());
|
||||
db.addTransportKeys(transaction, contactId, createTransportKeys());
|
||||
fail();
|
||||
} catch (NoSuchContactException expected) {
|
||||
// Expected
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
transaction = db.startTransaction();
|
||||
try {
|
||||
db.generateAck(contactId, 123);
|
||||
db.generateAck(transaction, contactId, 123);
|
||||
fail();
|
||||
} catch (NoSuchContactException expected) {
|
||||
// Expected
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
transaction = db.startTransaction();
|
||||
try {
|
||||
db.generateBatch(contactId, 123, 456);
|
||||
db.generateBatch(transaction, contactId, 123, 456);
|
||||
fail();
|
||||
} catch (NoSuchContactException expected) {
|
||||
// Expected
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
transaction = db.startTransaction();
|
||||
try {
|
||||
db.generateOffer(contactId, 123, 456);
|
||||
db.generateOffer(transaction, contactId, 123, 456);
|
||||
fail();
|
||||
} catch (NoSuchContactException expected) {
|
||||
// Expected
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
transaction = db.startTransaction();
|
||||
try {
|
||||
db.generateRequest(contactId, 123);
|
||||
db.generateRequest(transaction, contactId, 123);
|
||||
fail();
|
||||
} catch (NoSuchContactException expected) {
|
||||
// Expected
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
transaction = db.startTransaction();
|
||||
try {
|
||||
db.getContact(contactId);
|
||||
db.getContact(transaction, contactId);
|
||||
fail();
|
||||
} catch (NoSuchContactException expected) {
|
||||
// Expected
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
transaction = db.startTransaction();
|
||||
try {
|
||||
db.getMessageStatus(contactId, groupId);
|
||||
db.getMessageStatus(transaction, contactId, groupId);
|
||||
fail();
|
||||
} catch (NoSuchContactException expected) {
|
||||
// Expected
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
transaction = db.startTransaction();
|
||||
try {
|
||||
db.getMessageStatus(contactId, messageId);
|
||||
db.getMessageStatus(transaction, contactId, messageId);
|
||||
fail();
|
||||
} catch (NoSuchContactException expected) {
|
||||
// Expected
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
transaction = db.startTransaction();
|
||||
try {
|
||||
db.incrementStreamCounter(contactId, transportId, 0);
|
||||
db.incrementStreamCounter(transaction, contactId, transportId, 0);
|
||||
fail();
|
||||
} catch (NoSuchContactException expected) {
|
||||
// Expected
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
transaction = db.startTransaction();
|
||||
try {
|
||||
db.isVisibleToContact(contactId, groupId);
|
||||
db.isVisibleToContact(transaction, contactId, groupId);
|
||||
fail();
|
||||
} catch (NoSuchContactException expected) {
|
||||
// Expected
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
transaction = db.startTransaction();
|
||||
try {
|
||||
Ack a = new Ack(Collections.singletonList(messageId));
|
||||
db.receiveAck(contactId, a);
|
||||
db.receiveAck(transaction, contactId, a);
|
||||
fail();
|
||||
} catch (NoSuchContactException expected) {
|
||||
// Expected
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
transaction = db.startTransaction();
|
||||
try {
|
||||
db.receiveMessage(contactId, message);
|
||||
db.receiveMessage(transaction, contactId, message);
|
||||
fail();
|
||||
} catch (NoSuchContactException expected) {
|
||||
// Expected
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
transaction = db.startTransaction();
|
||||
try {
|
||||
Offer o = new Offer(Collections.singletonList(messageId));
|
||||
db.receiveOffer(contactId, o);
|
||||
db.receiveOffer(transaction, contactId, o);
|
||||
fail();
|
||||
} catch (NoSuchContactException expected) {
|
||||
// Expected
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
transaction = db.startTransaction();
|
||||
try {
|
||||
Request r = new Request(Collections.singletonList(messageId));
|
||||
db.receiveRequest(contactId, r);
|
||||
db.receiveRequest(transaction, contactId, r);
|
||||
fail();
|
||||
} catch (NoSuchContactException expected) {
|
||||
// Expected
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
transaction = db.startTransaction();
|
||||
try {
|
||||
db.removeContact(contactId);
|
||||
db.removeContact(transaction, contactId);
|
||||
fail();
|
||||
} catch (NoSuchContactException expected) {
|
||||
// Expected
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
transaction = db.startTransaction();
|
||||
try {
|
||||
db.setReorderingWindow(contactId, transportId, 0, 0, new byte[4]);
|
||||
db.setReorderingWindow(transaction, contactId, transportId, 0, 0,
|
||||
new byte[REORDERING_WINDOW_SIZE / 8]);
|
||||
fail();
|
||||
} catch (NoSuchContactException expected) {
|
||||
// Expected
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
transaction = db.startTransaction();
|
||||
try {
|
||||
db.setVisibleToContact(contactId, groupId, true);
|
||||
db.setVisibleToContact(transaction, contactId, groupId, true);
|
||||
fail();
|
||||
} catch (NoSuchContactException expected) {
|
||||
// Expected
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
context.assertIsSatisfied();
|
||||
@@ -421,25 +493,34 @@ public class DatabaseComponentImplTest extends BriarTestCase {
|
||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||
shutdown);
|
||||
|
||||
Transaction transaction = db.startTransaction();
|
||||
try {
|
||||
db.addContact(author, localAuthorId);
|
||||
db.addContact(transaction, author, localAuthorId);
|
||||
fail();
|
||||
} catch (NoSuchLocalAuthorException expected) {
|
||||
// Expected
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
transaction = db.startTransaction();
|
||||
try {
|
||||
db.getLocalAuthor(localAuthorId);
|
||||
db.getLocalAuthor(transaction, localAuthorId);
|
||||
fail();
|
||||
} catch (NoSuchLocalAuthorException expected) {
|
||||
// Expected
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
transaction = db.startTransaction();
|
||||
try {
|
||||
db.removeLocalAuthor(localAuthorId);
|
||||
db.removeLocalAuthor(transaction, localAuthorId);
|
||||
fail();
|
||||
} catch (NoSuchLocalAuthorException expected) {
|
||||
// Expected
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
context.assertIsSatisfied();
|
||||
@@ -468,53 +549,74 @@ public class DatabaseComponentImplTest extends BriarTestCase {
|
||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||
shutdown);
|
||||
|
||||
Transaction transaction = db.startTransaction();
|
||||
try {
|
||||
db.getGroup(groupId);
|
||||
db.getGroup(transaction, groupId);
|
||||
fail();
|
||||
} catch (NoSuchGroupException expected) {
|
||||
// Expected
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
transaction = db.startTransaction();
|
||||
try {
|
||||
db.getGroupMetadata(groupId);
|
||||
db.getGroupMetadata(transaction, groupId);
|
||||
fail();
|
||||
} catch (NoSuchGroupException expected) {
|
||||
// Expected
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
transaction = db.startTransaction();
|
||||
try {
|
||||
db.getMessageStatus(contactId, groupId);
|
||||
db.getMessageStatus(transaction, contactId, groupId);
|
||||
fail();
|
||||
} catch (NoSuchGroupException expected) {
|
||||
// Expected
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
transaction = db.startTransaction();
|
||||
try {
|
||||
db.isVisibleToContact(contactId, groupId);
|
||||
db.isVisibleToContact(transaction, contactId, groupId);
|
||||
fail();
|
||||
} catch (NoSuchGroupException expected) {
|
||||
// Expected
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
transaction = db.startTransaction();
|
||||
try {
|
||||
db.mergeGroupMetadata(groupId, metadata);
|
||||
db.mergeGroupMetadata(transaction, groupId, metadata);
|
||||
fail();
|
||||
} catch (NoSuchGroupException expected) {
|
||||
// Expected
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
transaction = db.startTransaction();
|
||||
try {
|
||||
db.removeGroup(group);
|
||||
db.removeGroup(transaction, group);
|
||||
fail();
|
||||
} catch (NoSuchGroupException expected) {
|
||||
// Expected
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
transaction = db.startTransaction();
|
||||
try {
|
||||
db.setVisibleToContact(contactId, groupId, true);
|
||||
db.setVisibleToContact(transaction, contactId, groupId, true);
|
||||
fail();
|
||||
} catch (NoSuchGroupException expected) {
|
||||
// Expected
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
context.assertIsSatisfied();
|
||||
@@ -542,60 +644,84 @@ public class DatabaseComponentImplTest extends BriarTestCase {
|
||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||
shutdown);
|
||||
|
||||
Transaction transaction = db.startTransaction();
|
||||
try {
|
||||
db.deleteMessage(messageId);
|
||||
db.deleteMessage(transaction, messageId);
|
||||
fail();
|
||||
} catch (NoSuchMessageException expected) {
|
||||
// Expected
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
transaction = db.startTransaction();
|
||||
try {
|
||||
db.deleteMessageMetadata(messageId);
|
||||
db.deleteMessageMetadata(transaction, messageId);
|
||||
fail();
|
||||
} catch (NoSuchMessageException expected) {
|
||||
// Expected
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
transaction = db.startTransaction();
|
||||
try {
|
||||
db.getRawMessage(messageId);
|
||||
db.getRawMessage(transaction, messageId);
|
||||
fail();
|
||||
} catch (NoSuchMessageException expected) {
|
||||
// Expected
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
transaction = db.startTransaction();
|
||||
try {
|
||||
db.getMessageMetadata(messageId);
|
||||
db.getMessageMetadata(transaction, messageId);
|
||||
fail();
|
||||
} catch (NoSuchMessageException expected) {
|
||||
// Expected
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
transaction = db.startTransaction();
|
||||
try {
|
||||
db.getMessageStatus(contactId, messageId);
|
||||
db.getMessageStatus(transaction, contactId, messageId);
|
||||
fail();
|
||||
} catch (NoSuchMessageException expected) {
|
||||
// Expected
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
transaction = db.startTransaction();
|
||||
try {
|
||||
db.mergeMessageMetadata(messageId, metadata);
|
||||
db.mergeMessageMetadata(transaction, messageId, metadata);
|
||||
fail();
|
||||
} catch (NoSuchMessageException expected) {
|
||||
// Expected
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
transaction = db.startTransaction();
|
||||
try {
|
||||
db.setMessageShared(message, true);
|
||||
db.setMessageShared(transaction, message, true);
|
||||
fail();
|
||||
} catch (NoSuchMessageException expected) {
|
||||
// Expected
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
transaction = db.startTransaction();
|
||||
try {
|
||||
db.setMessageValid(message, clientId, true);
|
||||
db.setMessageValid(transaction, message, clientId, true);
|
||||
fail();
|
||||
} catch (NoSuchMessageException expected) {
|
||||
// Expected
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
context.assertIsSatisfied();
|
||||
@@ -610,22 +736,21 @@ public class DatabaseComponentImplTest extends BriarTestCase {
|
||||
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
|
||||
final EventBus eventBus = context.mock(EventBus.class);
|
||||
context.checking(new Expectations() {{
|
||||
// addLocalAuthor()
|
||||
// startTransaction()
|
||||
oneOf(database).startTransaction();
|
||||
will(returnValue(txn));
|
||||
// addLocalAuthor()
|
||||
oneOf(database).containsLocalAuthor(txn, localAuthorId);
|
||||
will(returnValue(false));
|
||||
oneOf(database).addLocalAuthor(txn, localAuthor);
|
||||
oneOf(database).commitTransaction(txn);
|
||||
// addContact()
|
||||
oneOf(database).startTransaction();
|
||||
will(returnValue(txn));
|
||||
oneOf(database).containsLocalAuthor(txn, localAuthorId);
|
||||
will(returnValue(true));
|
||||
oneOf(database).containsContact(txn, authorId, localAuthorId);
|
||||
will(returnValue(false));
|
||||
oneOf(database).addContact(txn, author, localAuthorId);
|
||||
will(returnValue(contactId));
|
||||
// endTransaction()
|
||||
oneOf(database).commitTransaction(txn);
|
||||
// Check whether the transport is in the DB (which it's not)
|
||||
exactly(4).of(database).startTransaction();
|
||||
@@ -639,35 +764,55 @@ public class DatabaseComponentImplTest extends BriarTestCase {
|
||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||
shutdown);
|
||||
|
||||
db.addLocalAuthor(localAuthor);
|
||||
assertEquals(contactId, db.addContact(author, localAuthorId));
|
||||
|
||||
Transaction transaction = db.startTransaction();
|
||||
try {
|
||||
db.getTransportKeys(transportId);
|
||||
fail();
|
||||
} catch (NoSuchTransportException expected) {
|
||||
// Expected
|
||||
db.addLocalAuthor(transaction, localAuthor);
|
||||
assertEquals(contactId,
|
||||
db.addContact(transaction, author, localAuthorId));
|
||||
transaction.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
transaction = db.startTransaction();
|
||||
try {
|
||||
db.incrementStreamCounter(contactId, transportId, 0);
|
||||
db.getTransportKeys(transaction, transportId);
|
||||
fail();
|
||||
} catch (NoSuchTransportException expected) {
|
||||
// Expected
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
transaction = db.startTransaction();
|
||||
try {
|
||||
db.removeTransport(transportId);
|
||||
db.incrementStreamCounter(transaction, contactId, transportId, 0);
|
||||
fail();
|
||||
} catch (NoSuchTransportException expected) {
|
||||
// Expected
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
transaction = db.startTransaction();
|
||||
try {
|
||||
db.setReorderingWindow(contactId, transportId, 0, 0, new byte[4]);
|
||||
db.removeTransport(transaction, transportId);
|
||||
fail();
|
||||
} catch (NoSuchTransportException expected) {
|
||||
// Expected
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
transaction = db.startTransaction();
|
||||
try {
|
||||
db.setReorderingWindow(transaction, contactId, transportId, 0, 0,
|
||||
new byte[REORDERING_WINDOW_SIZE / 8]);
|
||||
fail();
|
||||
} catch (NoSuchTransportException expected) {
|
||||
// Expected
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
context.assertIsSatisfied();
|
||||
@@ -695,8 +840,14 @@ public class DatabaseComponentImplTest extends BriarTestCase {
|
||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||
shutdown);
|
||||
|
||||
Ack a = db.generateAck(contactId, 123);
|
||||
assertEquals(messagesToAck, a.getMessageIds());
|
||||
Transaction transaction = db.startTransaction();
|
||||
try {
|
||||
Ack a = db.generateAck(transaction, contactId, 123);
|
||||
assertEquals(messagesToAck, a.getMessageIds());
|
||||
transaction.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
@@ -733,8 +884,14 @@ public class DatabaseComponentImplTest extends BriarTestCase {
|
||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||
shutdown);
|
||||
|
||||
assertEquals(messages, db.generateBatch(contactId, size * 2,
|
||||
maxLatency));
|
||||
Transaction transaction = db.startTransaction();
|
||||
try {
|
||||
assertEquals(messages, db.generateBatch(transaction, contactId,
|
||||
size * 2, maxLatency));
|
||||
transaction.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
@@ -764,8 +921,14 @@ public class DatabaseComponentImplTest extends BriarTestCase {
|
||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||
shutdown);
|
||||
|
||||
Offer o = db.generateOffer(contactId, 123, maxLatency);
|
||||
assertEquals(ids, o.getMessageIds());
|
||||
Transaction transaction = db.startTransaction();
|
||||
try {
|
||||
Offer o = db.generateOffer(transaction, contactId, 123, maxLatency);
|
||||
assertEquals(ids, o.getMessageIds());
|
||||
transaction.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
@@ -792,8 +955,14 @@ public class DatabaseComponentImplTest extends BriarTestCase {
|
||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||
shutdown);
|
||||
|
||||
Request r = db.generateRequest(contactId, 123);
|
||||
assertEquals(ids, r.getMessageIds());
|
||||
Transaction transaction = db.startTransaction();
|
||||
try {
|
||||
Request r = db.generateRequest(transaction, contactId, 123);
|
||||
assertEquals(ids, r.getMessageIds());
|
||||
transaction.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
@@ -831,8 +1000,14 @@ public class DatabaseComponentImplTest extends BriarTestCase {
|
||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||
shutdown);
|
||||
|
||||
assertEquals(messages, db.generateRequestedBatch(contactId, size * 2,
|
||||
maxLatency));
|
||||
Transaction transaction = db.startTransaction();
|
||||
try {
|
||||
assertEquals(messages, db.generateRequestedBatch(transaction,
|
||||
contactId, size * 2, maxLatency));
|
||||
transaction.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
@@ -858,7 +1033,14 @@ public class DatabaseComponentImplTest extends BriarTestCase {
|
||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||
shutdown);
|
||||
|
||||
db.receiveAck(contactId, new Ack(Collections.singletonList(messageId)));
|
||||
Transaction transaction = db.startTransaction();
|
||||
try {
|
||||
Ack a = new Ack(Collections.singletonList(messageId));
|
||||
db.receiveAck(transaction, contactId, a);
|
||||
transaction.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
@@ -896,7 +1078,13 @@ public class DatabaseComponentImplTest extends BriarTestCase {
|
||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||
shutdown);
|
||||
|
||||
db.receiveMessage(contactId, message);
|
||||
Transaction transaction = db.startTransaction();
|
||||
try {
|
||||
db.receiveMessage(transaction, contactId, message);
|
||||
transaction.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
@@ -926,7 +1114,13 @@ public class DatabaseComponentImplTest extends BriarTestCase {
|
||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||
shutdown);
|
||||
|
||||
db.receiveMessage(contactId, message);
|
||||
Transaction transaction = db.startTransaction();
|
||||
try {
|
||||
db.receiveMessage(transaction, contactId, message);
|
||||
transaction.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
@@ -952,7 +1146,13 @@ public class DatabaseComponentImplTest extends BriarTestCase {
|
||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||
shutdown);
|
||||
|
||||
db.receiveMessage(contactId, message);
|
||||
Transaction transaction = db.startTransaction();
|
||||
try {
|
||||
db.receiveMessage(transaction, contactId, message);
|
||||
transaction.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
@@ -998,9 +1198,16 @@ public class DatabaseComponentImplTest extends BriarTestCase {
|
||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||
shutdown);
|
||||
|
||||
Offer o = new Offer(Arrays.asList(messageId, messageId1, messageId2,
|
||||
messageId3));
|
||||
db.receiveOffer(contactId, o);
|
||||
Transaction transaction = db.startTransaction();
|
||||
try {
|
||||
Offer o = new Offer(Arrays.asList(messageId, messageId1,
|
||||
messageId2, messageId3));
|
||||
db.receiveOffer(transaction, contactId, o);
|
||||
transaction.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@@ -1026,8 +1233,14 @@ public class DatabaseComponentImplTest extends BriarTestCase {
|
||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||
shutdown);
|
||||
|
||||
db.receiveRequest(contactId, new Request(Collections.singletonList(
|
||||
messageId)));
|
||||
Transaction transaction = db.startTransaction();
|
||||
try {
|
||||
Request r = new Request(Collections.singletonList(messageId));
|
||||
db.receiveRequest(transaction, contactId, r);
|
||||
transaction.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
@@ -1056,7 +1269,13 @@ public class DatabaseComponentImplTest extends BriarTestCase {
|
||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||
shutdown);
|
||||
|
||||
db.setVisibleToContact(contactId, groupId, true);
|
||||
Transaction transaction = db.startTransaction();
|
||||
try {
|
||||
db.setVisibleToContact(transaction, contactId, groupId, true);
|
||||
transaction.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
@@ -1083,45 +1302,56 @@ public class DatabaseComponentImplTest extends BriarTestCase {
|
||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||
shutdown);
|
||||
|
||||
db.setVisibleToContact(contactId, groupId, true);
|
||||
Transaction transaction = db.startTransaction();
|
||||
try {
|
||||
db.setVisibleToContact(transaction, contactId, groupId, true);
|
||||
transaction.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransportKeys() throws Exception {
|
||||
final TransportKeys keys = createTransportKeys();
|
||||
final TransportKeys transportKeys = createTransportKeys();
|
||||
final Map<ContactId, TransportKeys> keys = Collections.singletonMap(
|
||||
contactId, transportKeys);
|
||||
Mockery context = new Mockery();
|
||||
@SuppressWarnings("unchecked")
|
||||
final Database<Object> database = context.mock(Database.class);
|
||||
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
|
||||
final EventBus eventBus = context.mock(EventBus.class);
|
||||
context.checking(new Expectations() {{
|
||||
// updateTransportKeys()
|
||||
// startTransaction()
|
||||
oneOf(database).startTransaction();
|
||||
will(returnValue(txn));
|
||||
// updateTransportKeys()
|
||||
oneOf(database).containsContact(txn, contactId);
|
||||
will(returnValue(true));
|
||||
oneOf(database).containsTransport(txn, transportId);
|
||||
will(returnValue(true));
|
||||
oneOf(database).updateTransportKeys(txn,
|
||||
Collections.singletonMap(contactId, keys));
|
||||
oneOf(database).commitTransaction(txn);
|
||||
oneOf(database).updateTransportKeys(txn, keys);
|
||||
// getTransportKeys()
|
||||
oneOf(database).startTransaction();
|
||||
will(returnValue(txn));
|
||||
oneOf(database).containsTransport(txn, transportId);
|
||||
will(returnValue(true));
|
||||
oneOf(database).getTransportKeys(txn, transportId);
|
||||
will(returnValue(Collections.singletonMap(contactId, keys)));
|
||||
will(returnValue(keys));
|
||||
// endTransaction()
|
||||
oneOf(database).commitTransaction(txn);
|
||||
}});
|
||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||
shutdown);
|
||||
|
||||
db.updateTransportKeys(Collections.singletonMap(contactId, keys));
|
||||
assertEquals(Collections.singletonMap(contactId, keys),
|
||||
db.getTransportKeys(transportId));
|
||||
Transaction transaction = db.startTransaction();
|
||||
try {
|
||||
db.updateTransportKeys(transaction, keys);
|
||||
assertEquals(keys, db.getTransportKeys(transaction, transportId));
|
||||
transaction.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
@@ -1162,29 +1392,34 @@ public class DatabaseComponentImplTest extends BriarTestCase {
|
||||
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
|
||||
final EventBus eventBus = context.mock(EventBus.class);
|
||||
context.checking(new Expectations() {{
|
||||
// mergeSettings()
|
||||
// startTransaction()
|
||||
oneOf(database).startTransaction();
|
||||
will(returnValue(txn));
|
||||
// mergeSettings()
|
||||
oneOf(database).getSettings(txn, "namespace");
|
||||
will(returnValue(before));
|
||||
oneOf(database).mergeSettings(txn, update, "namespace");
|
||||
oneOf(database).commitTransaction(txn);
|
||||
oneOf(eventBus).broadcast(with(any(SettingsUpdatedEvent.class)));
|
||||
// mergeSettings() again
|
||||
oneOf(database).startTransaction();
|
||||
will(returnValue(txn));
|
||||
oneOf(database).getSettings(txn, "namespace");
|
||||
will(returnValue(merged));
|
||||
// endTransaction()
|
||||
oneOf(database).commitTransaction(txn);
|
||||
}});
|
||||
|
||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||
shutdown);
|
||||
|
||||
// First merge should broadcast an event
|
||||
db.mergeSettings(update, "namespace");
|
||||
// Second merge should not broadcast an event
|
||||
db.mergeSettings(update, "namespace");
|
||||
Transaction transaction = db.startTransaction();
|
||||
try {
|
||||
// First merge should broadcast an event
|
||||
db.mergeSettings(transaction, update, "namespace");
|
||||
// Second merge should not broadcast an event
|
||||
db.mergeSettings(transaction, update, "namespace");
|
||||
transaction.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(transaction);
|
||||
}
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ 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.duplex.DuplexPlugin;
|
||||
@@ -14,6 +15,7 @@ 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;
|
||||
@@ -46,6 +48,8 @@ public class PluginManagerImplTest extends BriarTestCase {
|
||||
final Poller poller = context.mock(Poller.class);
|
||||
final ConnectionManager connectionManager =
|
||||
context.mock(ConnectionManager.class);
|
||||
final SettingsManager settingsManager =
|
||||
context.mock(SettingsManager.class);
|
||||
final TransportPropertyManager transportPropertyManager =
|
||||
context.mock(TransportPropertyManager.class);
|
||||
final UiCallback uiCallback = context.mock(UiCallback.class);
|
||||
@@ -55,18 +59,21 @@ public class PluginManagerImplTest extends BriarTestCase {
|
||||
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");
|
||||
@@ -82,7 +89,10 @@ public class PluginManagerImplTest extends BriarTestCase {
|
||||
will(returnValue(simplexPlugin)); // Created
|
||||
oneOf(simplexPlugin).getMaxLatency();
|
||||
will(returnValue(simplexLatency));
|
||||
oneOf(db).addTransport(simplexId, 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();
|
||||
@@ -96,7 +106,11 @@ public class PluginManagerImplTest extends BriarTestCase {
|
||||
will(returnValue(simplexFailPlugin)); // Created
|
||||
oneOf(simplexFailPlugin).getMaxLatency();
|
||||
will(returnValue(simplexFailLatency));
|
||||
oneOf(db).addTransport(simplexFailId, 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
|
||||
@@ -109,7 +123,10 @@ public class PluginManagerImplTest extends BriarTestCase {
|
||||
will(returnValue(duplexPlugin)); // Created
|
||||
oneOf(duplexPlugin).getMaxLatency();
|
||||
will(returnValue(duplexLatency));
|
||||
oneOf(db).addTransport(duplexId, 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();
|
||||
@@ -128,7 +145,8 @@ public class PluginManagerImplTest extends BriarTestCase {
|
||||
}});
|
||||
PluginManagerImpl p = new PluginManagerImpl(ioExecutor, eventBus,
|
||||
simplexPluginConfig, duplexPluginConfig, clock, db, poller,
|
||||
connectionManager, transportPropertyManager, uiCallback);
|
||||
connectionManager, settingsManager, transportPropertyManager,
|
||||
uiCallback);
|
||||
|
||||
// Two plugins should be started and stopped
|
||||
assertTrue(p.start());
|
||||
|
||||
@@ -13,6 +13,7 @@ import org.briarproject.api.contact.ContactManager;
|
||||
import org.briarproject.api.crypto.SecretKey;
|
||||
import org.briarproject.api.db.DatabaseComponent;
|
||||
import org.briarproject.api.db.StorageStatus;
|
||||
import org.briarproject.api.db.Transaction;
|
||||
import org.briarproject.api.event.Event;
|
||||
import org.briarproject.api.event.EventBus;
|
||||
import org.briarproject.api.event.EventListener;
|
||||
@@ -120,7 +121,13 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
|
||||
lifecycleManager.startServices();
|
||||
lifecycleManager.waitForStartup();
|
||||
// Add a transport
|
||||
db.addTransport(transportId, MAX_LATENCY);
|
||||
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,
|
||||
@@ -185,7 +192,13 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
|
||||
lifecycleManager.startServices();
|
||||
lifecycleManager.waitForStartup();
|
||||
// Add a transport
|
||||
db.addTransport(transportId, MAX_LATENCY);
|
||||
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,
|
||||
|
||||
@@ -5,6 +5,7 @@ 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;
|
||||
import org.briarproject.api.event.EventBus;
|
||||
import org.briarproject.api.sync.Ack;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
@@ -49,16 +50,24 @@ public class SimplexOutgoingSessionTest extends BriarTestCase {
|
||||
final SimplexOutgoingSession session = new SimplexOutgoingSession(db,
|
||||
dbExecutor, eventBus, contactId, transportId, maxLatency,
|
||||
packetWriter);
|
||||
final Transaction noAckTxn = new Transaction(null);
|
||||
final Transaction noMsgTxn = new Transaction(null);
|
||||
context.checking(new Expectations() {{
|
||||
// Add listener
|
||||
oneOf(eventBus).addListener(session);
|
||||
// No acks to send
|
||||
oneOf(db).generateAck(contactId, MAX_MESSAGE_IDS);
|
||||
oneOf(db).startTransaction();
|
||||
will(returnValue(noAckTxn));
|
||||
oneOf(db).generateAck(noAckTxn, contactId, MAX_MESSAGE_IDS);
|
||||
will(returnValue(null));
|
||||
oneOf(db).endTransaction(noAckTxn);
|
||||
// No messages to send
|
||||
oneOf(db).generateBatch(with(contactId), with(any(int.class)),
|
||||
with(maxLatency));
|
||||
oneOf(db).startTransaction();
|
||||
will(returnValue(noMsgTxn));
|
||||
oneOf(db).generateBatch(with(noMsgTxn), with(contactId),
|
||||
with(any(int.class)), with(maxLatency));
|
||||
will(returnValue(null));
|
||||
oneOf(db).endTransaction(noMsgTxn);
|
||||
// Flush the output stream
|
||||
oneOf(packetWriter).flush();
|
||||
// Remove listener
|
||||
@@ -75,25 +84,41 @@ public class SimplexOutgoingSessionTest extends BriarTestCase {
|
||||
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);
|
||||
context.checking(new Expectations() {{
|
||||
// Add listener
|
||||
oneOf(eventBus).addListener(session);
|
||||
// One ack to send
|
||||
oneOf(db).generateAck(contactId, MAX_MESSAGE_IDS);
|
||||
oneOf(db).startTransaction();
|
||||
will(returnValue(ackTxn));
|
||||
oneOf(db).generateAck(ackTxn, contactId, MAX_MESSAGE_IDS);
|
||||
will(returnValue(ack));
|
||||
oneOf(db).endTransaction(ackTxn);
|
||||
oneOf(packetWriter).writeAck(ack);
|
||||
// No more acks
|
||||
oneOf(db).generateAck(contactId, MAX_MESSAGE_IDS);
|
||||
will(returnValue(null));
|
||||
// One message to send
|
||||
oneOf(db).generateBatch(with(contactId), with(any(int.class)),
|
||||
with(maxLatency));
|
||||
oneOf(db).startTransaction();
|
||||
will(returnValue(msgTxn));
|
||||
oneOf(db).generateBatch(with(msgTxn), with(contactId),
|
||||
with(any(int.class)), with(maxLatency));
|
||||
will(returnValue(Arrays.asList(raw)));
|
||||
oneOf(db).endTransaction(msgTxn);
|
||||
oneOf(packetWriter).writeMessage(raw);
|
||||
// No more messages
|
||||
oneOf(db).generateBatch(with(contactId), with(any(int.class)),
|
||||
with(maxLatency));
|
||||
// No more acks
|
||||
oneOf(db).startTransaction();
|
||||
will(returnValue(noAckTxn));
|
||||
oneOf(db).generateAck(noAckTxn, contactId, MAX_MESSAGE_IDS);
|
||||
will(returnValue(null));
|
||||
oneOf(db).endTransaction(noAckTxn);
|
||||
// No more messages
|
||||
oneOf(db).startTransaction();
|
||||
will(returnValue(noMsgTxn));
|
||||
oneOf(db).generateBatch(with(noMsgTxn), with(contactId),
|
||||
with(any(int.class)), with(maxLatency));
|
||||
will(returnValue(null));
|
||||
oneOf(db).endTransaction(noMsgTxn);
|
||||
// Flush the output stream
|
||||
oneOf(packetWriter).flush();
|
||||
// Remove listener
|
||||
|
||||
Reference in New Issue
Block a user