From 623707af0f8e874ab93d50e8ad0f60b9645bbc21 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Fri, 5 Feb 2016 17:49:49 +0000 Subject: [PATCH 01/11] Removed database locking. --- .../src/org/briarproject/db/Database.java | 158 +- .../db/DatabaseComponentImpl.java | 1285 +++++++---------- .../src/org/briarproject/db/JdbcDatabase.java | 12 - 3 files changed, 506 insertions(+), 949 deletions(-) diff --git a/briar-core/src/org/briarproject/db/Database.java b/briar-core/src/org/briarproject/db/Database.java index 934156308..30c824038 100644 --- a/briar-core/src/org/briarproject/db/Database.java +++ b/briar-core/src/org/briarproject/db/Database.java @@ -30,27 +30,23 @@ import java.util.Map; * obtained by calling {@link #startTransaction()}. Every transaction must be * terminated by calling either {@link #abortTransaction(T)} or * {@link #commitTransaction(T)}, even if an exception is thrown. - *

- * Read-write locking is provided by the DatabaseComponent implementation. */ interface Database { /** * Opens the database and returns true if the database already existed. - *

- * Locking: write. */ boolean open() throws DbException; /** * Prevents new transactions from starting, waits for all current * transactions to finish, and closes the database. - *

- * Locking: write. */ 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. + */ T startTransaction() throws DbException; /** @@ -65,59 +61,39 @@ interface Database { */ void commitTransaction(T txn) throws DbException; - /** - * Returns the number of transactions started since the transaction count - * was last reset. - */ - int getTransactionCount(); - - /** Resets the transaction count. */ - void resetTransactionCount(); - /** * Stores a contact associated with the given local and remote pseudonyms, * and returns an ID for the contact. - *

- * Locking: write. */ ContactId addContact(T txn, Author remote, AuthorId local) throws DbException; /** * Stores a group. - *

- * Locking: write. */ void addGroup(T txn, Group g) throws DbException; /** * Stores a local pseudonym. - *

- * Locking: write. */ void addLocalAuthor(T txn, LocalAuthor a) throws DbException; /** * Stores a message. - *

- * Locking: write. */ void addMessage(T txn, Message m, Validity validity, boolean shared) throws DbException; /** * Records that a message has been offered by the given contact. - *

- * Locking: write. */ void addOfferedMessage(T txn, ContactId c, MessageId m) throws DbException; /** * Initialises the status of the given message with respect to the given * contact. - *

- * Locking: write. - * @param ack whether the message needs to be acknowledged. + * + * @param ack whether the message needs to be acknowledged. * @param seen whether the contact has seen the message. */ void addStatus(T txn, ContactId c, MessageId m, boolean ack, boolean seen) @@ -125,76 +101,56 @@ interface Database { /** * Stores a transport. - *

- * Locking: write. */ void addTransport(T txn, TransportId t, int maxLatency) throws DbException; /** * Stores transport keys for a newly added contact. - *

- * Locking: write. */ void addTransportKeys(T txn, ContactId c, TransportKeys k) throws DbException; /** * Makes a group visible to the given contact. - *

- * Locking: write. */ void addVisibility(T txn, ContactId c, GroupId g) throws DbException; /** * Returns true if the database contains the given contact for the given * local pseudonym. - *

- * Locking: read. */ boolean containsContact(T txn, AuthorId remote, AuthorId local) throws DbException; /** * Returns true if the database contains the given contact. - *

- * Locking: read. */ boolean containsContact(T txn, ContactId c) throws DbException; /** * Returns true if the database contains the given group. - *

- * Locking: read. */ boolean containsGroup(T txn, GroupId g) throws DbException; /** * Returns true if the database contains the given local pseudonym. - *

- * Locking: read. */ boolean containsLocalAuthor(T txn, AuthorId a) throws DbException; /** * Returns true if the database contains the given message. - *

- * Locking: read. */ boolean containsMessage(T txn, MessageId m) throws DbException; /** * Returns true if the database contains the given transport. - *

- * Locking: read. */ boolean containsTransport(T txn, TransportId t) throws DbException; /** * Returns true if the database contains the given group and the group is * visible to the given contact. - *

- * Locking: read. */ boolean containsVisibleGroup(T txn, ContactId c, GroupId g) throws DbException; @@ -202,16 +158,12 @@ interface Database { /** * Returns true if the database contains the given message and the message * is visible to the given contact. - *

- * Locking: read. */ boolean containsVisibleMessage(T txn, ContactId c, MessageId m) throws DbException; /** * Returns the number of messages offered by the given contact. - *

- * Locking: read. */ int countOfferedMessages(T txn, ContactId c) throws DbException; @@ -234,36 +186,26 @@ interface Database { /** * Returns the contact with the given ID. - *

- * Locking: read. */ Contact getContact(T txn, ContactId c) throws DbException; /** * Returns the IDs of all contacts. - *

- * Locking: read. */ Collection getContactIds(T txn) throws DbException; /** * Returns all contacts. - *

- * Locking: read. */ Collection getContacts(T txn) throws DbException; /** * Returns all contacts associated with the given local pseudonym. - *

- * Locking: read. */ Collection getContacts(T txn, AuthorId a) throws DbException; /** * Returns the unique ID for this device. - *

- * Locking: read. */ DeviceId getDeviceId(T txn) throws DbException; @@ -276,59 +218,43 @@ interface Database { /** * Returns the group with the given ID. - *

- * Locking: read. */ Group getGroup(T txn, GroupId g) throws DbException; /** * Returns the metadata for the given group. - *

- * Locking: read. */ Metadata getGroupMetadata(T txn, GroupId g) throws DbException; /** * Returns all groups belonging to the given client. - *

- * Locking: read. */ Collection getGroups(T txn, ClientId c) throws DbException; /** * Returns the local pseudonym with the given ID. - *

- * Locking: read. */ LocalAuthor getLocalAuthor(T txn, AuthorId a) throws DbException; /** * Returns all local pseudonyms. - *

- * Locking: read. */ Collection getLocalAuthors(T txn) throws DbException; /** * Returns the metadata for all messages in the given group. - *

- * Locking: read. */ Map getMessageMetadata(T txn, GroupId g) throws DbException; /** * Returns the metadata for the given message. - *

- * Locking: read. */ Metadata getMessageMetadata(T txn, MessageId m) throws DbException; /** * Returns the status of all messages in the given group with respect * to the given contact. - *

- * Locking: read */ Collection getMessageStatus(T txn, ContactId c, GroupId g) throws DbException; @@ -336,8 +262,6 @@ interface Database { /** * Returns the status of the given message with respect to the given * contact. - *

- * Locking: read */ MessageStatus getMessageStatus(T txn, ContactId c, MessageId m) throws DbException; @@ -345,8 +269,6 @@ interface Database { /** * Returns the IDs of some messages received from the given contact that * need to be acknowledged, up to the given number of messages. - *

- * Locking: read. */ Collection getMessagesToAck(T txn, ContactId c, int maxMessages) throws DbException; @@ -354,8 +276,6 @@ interface Database { /** * Returns the IDs of some messages that are eligible to be offered to the * given contact, up to the given number of messages. - *

- * Locking: read. */ Collection getMessagesToOffer(T txn, ContactId c, int maxMessages) throws DbException; @@ -363,8 +283,6 @@ interface Database { /** * Returns the IDs of some messages that are eligible to be sent to the * given contact, up to the given total length. - *

- * Locking: read. */ Collection getMessagesToSend(T txn, ContactId c, int maxLength) throws DbException; @@ -372,8 +290,6 @@ interface Database { /** * Returns the IDs of some messages that are eligible to be requested from * the given contact, up to the given number of messages. - *

- * Locking: read. */ Collection getMessagesToRequest(T txn, ContactId c, int maxMessages) throws DbException; @@ -381,16 +297,12 @@ interface Database { /** * Returns the IDs of any messages that need to be validated by the given * client. - *

- * Locking: read. */ Collection getMessagesToValidate(T txn, ClientId c) throws DbException; /** * Returns the message with the given ID, in serialised form. - *

- * Locking: read. */ byte[] getRawMessage(T txn, MessageId m) throws DbException; @@ -398,46 +310,34 @@ interface Database { * Returns the IDs of some messages that are eligible to be sent to the * given contact and have been requested by the contact, up to the given * total length. - *

- * Locking: read. */ Collection getRequestedMessagesToSend(T txn, ContactId c, int maxLength) throws DbException; /** * Returns all settings in the given namespace. - *

- * Locking: read. */ Settings getSettings(T txn, String namespace) throws DbException; /** * Returns all transport keys for the given transport. - *

- * Locking: read. */ Map getTransportKeys(T txn, TransportId t) throws DbException; /** * Returns the maximum latencies in milliseconds of all transports. - *

- * Locking: read. */ Map getTransportLatencies(T txn) throws DbException; /** * Returns the IDs of all contacts to which the given group is visible. - *

- * Locking: read. */ Collection getVisibility(T txn, GroupId g) throws DbException; /** * Increments the outgoing stream counter for the given contact and * transport in the given rotation period. - *

- * Locking: write. */ void incrementStreamCounter(T txn, ContactId c, TransportId t, long rotationPeriod) throws DbException; @@ -445,8 +345,6 @@ interface Database { /** * Marks the given messages as not needing to be acknowledged to the * given contact. - *

- * Locking: write. */ void lowerAckFlag(T txn, ContactId c, Collection acked) throws DbException; @@ -454,8 +352,6 @@ interface Database { /** * Marks the given messages as not having been requested by the given * contact. - *

- * Locking: write. */ void lowerRequestedFlag(T txn, ContactId c, Collection requested) throws DbException; @@ -463,8 +359,6 @@ interface Database { /* * Merges the given metadata with the existing metadata for the given * group. - *

- * Locking: write. */ void mergeGroupMetadata(T txn, GroupId g, Metadata meta) throws DbException; @@ -472,8 +366,6 @@ interface Database { /* * Merges the given metadata with the existing metadata for the given * message. - *

- * Locking: write. */ void mergeMessageMetadata(T txn, MessageId m, Metadata meta) throws DbException; @@ -481,65 +373,47 @@ interface Database { /** * Merges the given settings with the existing settings in the given * namespace. - *

- * Locking: write. */ void mergeSettings(T txn, Settings s, String namespace) throws DbException; /** * Marks a message as needing to be acknowledged to the given contact. - *

- * Locking: write. */ void raiseAckFlag(T txn, ContactId c, MessageId m) throws DbException; /** * Marks a message as having been requested by the given contact. - *

- * Locking: write. */ void raiseRequestedFlag(T txn, ContactId c, MessageId m) throws DbException; /** * Marks a message as having been seen by the given contact. - *

- * Locking: write. */ void raiseSeenFlag(T txn, ContactId c, MessageId m) throws DbException; /** * Removes a contact from the database. - *

- * Locking: write. */ void removeContact(T txn, ContactId c) throws DbException; /** * Removes a group (and all associated state) from the database. - *

- * Locking: write. */ void removeGroup(T txn, GroupId g) throws DbException; /** * Removes a local pseudonym (and all associated state) from the database. - *

- * Locking: write. */ void removeLocalAuthor(T txn, AuthorId a) throws DbException; /** * Removes a message (and all associated state) from the database. - *

- * Locking: write. */ void removeMessage(T txn, MessageId m) throws DbException; /** * Removes an offered message that was offered by the given contact, or * returns false if there is no such message. - *

- * Locking: write. */ boolean removeOfferedMessage(T txn, ContactId c, MessageId m) throws DbException; @@ -547,70 +421,52 @@ interface Database { /** * Removes the given offered messages that were offered by the given * contact. - *

- * Locking: write. */ void removeOfferedMessages(T txn, ContactId c, Collection requested) throws DbException; /** * Removes a transport (and all associated state) from the database. - *

- * Locking: write. */ void removeTransport(T txn, TransportId t) throws DbException; /** * Makes a group invisible to the given contact. - *

- * Locking: write. */ void removeVisibility(T txn, ContactId c, GroupId g) throws DbException; /** * Resets the transmission count and expiry time of the given message with * respect to the given contact. - *

- * Locking: write. */ void resetExpiryTime(T txn, ContactId c, MessageId m) throws DbException; /** * Sets the status of the given contact. - *

- * Locking: write. */ void setContactStatus(T txn, ContactId c, StorageStatus s) throws DbException; /** * Sets the status of the given local pseudonym. - *

- * Locking: write. */ void setLocalAuthorStatus(T txn, AuthorId a, StorageStatus s) throws DbException; /** * Marks the given message as shared or unshared. - *

- * Locking: write. */ void setMessageShared(T txn, MessageId m, boolean shared) throws DbException; /** * Marks the given message as valid or invalid. - *

- * Locking: write. */ void setMessageValid(T txn, MessageId m, boolean valid) throws DbException; /** * Sets the reordering window for the given contact and transport in the * given rotation period. - *

- * Locking: write. */ void setReorderingWindow(T txn, ContactId c, TransportId t, long rotationPeriod, long base, byte[] bitmap) throws DbException; @@ -619,16 +475,12 @@ interface Database { * Updates the transmission count and expiry time of the given message * with respect to the given contact, using the latency of the transport * over which it was sent. - *

- * Locking: write. */ void updateExpiryTime(T txn, ContactId c, MessageId m, int maxLatency) throws DbException; /** * Stores the given transport keys, deleting any keys they have replaced. - *

- * Locking: write. */ void updateTransportKeys(T txn, Map keys) throws DbException; diff --git a/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java b/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java index 0a974ed8e..dcf88660a 100644 --- a/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java +++ b/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java @@ -55,7 +55,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; -import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Logger; import javax.inject.Inject; @@ -79,12 +79,9 @@ class DatabaseComponentImpl implements DatabaseComponent { private final Database db; private final EventBus eventBus; private final ShutdownManager shutdown; + private final AtomicBoolean closed = new AtomicBoolean(false); - private final ReentrantReadWriteLock lock = - new ReentrantReadWriteLock(true); - - private boolean open = false; // Locking: lock.writeLock - private int shutdownHandle = -1; // Locking: lock.writeLock + private volatile int shutdownHandle = -1; @Inject DatabaseComponentImpl(Database db, EventBus eventBus, @@ -97,9 +94,7 @@ class DatabaseComponentImpl implements DatabaseComponent { public boolean open() throws DbException { Runnable shutdownHook = new Runnable() { public void run() { - lock.writeLock().lock(); try { - shutdownHandle = -1; close(); } catch (DbException e) { if (LOG.isLoggable(WARNING)) @@ -107,118 +102,81 @@ class DatabaseComponentImpl implements DatabaseComponent { } catch (IOException e) { if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - } finally { - lock.writeLock().unlock(); } } }; - lock.writeLock().lock(); - try { - if (open) throw new IllegalStateException(); - open = true; - boolean reopened = db.open(); - shutdownHandle = shutdown.addShutdownHook(shutdownHook); - return reopened; - } finally { - lock.writeLock().unlock(); - } + boolean reopened = db.open(); + shutdownHandle = shutdown.addShutdownHook(shutdownHook); + return reopened; } public void close() throws DbException, IOException { - lock.writeLock().lock(); - try { - if (!open) return; - open = false; - if (shutdownHandle != -1) - shutdown.removeShutdownHook(shutdownHandle); - db.close(); - } finally { - lock.writeLock().unlock(); - } + if (closed.getAndSet(true)) return; + shutdown.removeShutdownHook(shutdownHandle); + db.close(); } public ContactId addContact(Author remote, AuthorId local) throws DbException { - lock.writeLock().lock(); + T txn = db.startTransaction(); try { - T txn = db.startTransaction(); - try { - if (!db.containsLocalAuthor(txn, local)) - throw new NoSuchLocalAuthorException(); - if (db.containsContact(txn, remote.getId(), local)) - throw new ContactExistsException(); - ContactId c = db.addContact(txn, remote, local); - db.commitTransaction(txn); - return c; - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } - } finally { - lock.writeLock().unlock(); + if (!db.containsLocalAuthor(txn, local)) + throw new NoSuchLocalAuthorException(); + if (db.containsContact(txn, remote.getId(), local)) + throw new ContactExistsException(); + ContactId c = db.addContact(txn, remote, local); + db.commitTransaction(txn); + return c; + } catch (DbException e) { + db.abortTransaction(txn); + throw e; } } public void addGroup(Group g) throws DbException { boolean added = false; - lock.writeLock().lock(); + T txn = db.startTransaction(); try { - T txn = db.startTransaction(); - try { - if (!db.containsGroup(txn, g.getId())) { - db.addGroup(txn, g); - added = true; - } - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; + if (!db.containsGroup(txn, g.getId())) { + db.addGroup(txn, g); + added = true; } - } finally { - lock.writeLock().unlock(); + db.commitTransaction(txn); + } catch (DbException e) { + db.abortTransaction(txn); + throw e; } if (added) eventBus.broadcast(new GroupAddedEvent(g)); } public void addLocalAuthor(LocalAuthor a) throws DbException { - lock.writeLock().lock(); + T txn = db.startTransaction(); try { - T txn = db.startTransaction(); - try { - if (!db.containsLocalAuthor(txn, a.getId())) { - db.addLocalAuthor(txn, a); - } - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } - } finally { - lock.writeLock().unlock(); + if (!db.containsLocalAuthor(txn, a.getId())) + db.addLocalAuthor(txn, a); + db.commitTransaction(txn); + } catch (DbException e) { + db.abortTransaction(txn); + throw e; } } public void addLocalMessage(Message m, ClientId c, Metadata meta, boolean shared) throws DbException { boolean added = false; - lock.writeLock().lock(); + T txn = db.startTransaction(); try { - T txn = db.startTransaction(); - try { - if (!db.containsGroup(txn, m.getGroupId())) - throw new NoSuchGroupException(); - if (!db.containsMessage(txn, m.getId())) { - addMessage(txn, m, VALID, shared, null); - added = true; - } - db.mergeMessageMetadata(txn, m.getId(), meta); - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; + if (!db.containsGroup(txn, m.getGroupId())) + throw new NoSuchGroupException(); + if (!db.containsMessage(txn, m.getId())) { + addMessage(txn, m, VALID, shared, null); + added = true; } - } finally { - lock.writeLock().unlock(); + db.mergeMessageMetadata(txn, m.getId(), meta); + db.commitTransaction(txn); + } catch (DbException e) { + db.abortTransaction(txn); + throw e; } if (added) { eventBus.broadcast(new MessageAddedEvent(m, null)); @@ -229,8 +187,7 @@ class DatabaseComponentImpl implements DatabaseComponent { /** * Stores a message and initialises its status with respect to each contact. - *

- * Locking: write. + * * @param sender null for a locally generated message. */ private void addMessage(T txn, Message m, Validity validity, boolean shared, @@ -253,43 +210,33 @@ class DatabaseComponentImpl implements DatabaseComponent { public void addTransport(TransportId t, int maxLatency) throws DbException { boolean added = false; - lock.writeLock().lock(); + T txn = db.startTransaction(); try { - T txn = db.startTransaction(); - try { - if (!db.containsTransport(txn, t)) { - db.addTransport(txn, t, maxLatency); - added = true; - } - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; + if (!db.containsTransport(txn, t)) { + db.addTransport(txn, t, maxLatency); + added = true; } - } finally { - lock.writeLock().unlock(); + db.commitTransaction(txn); + } catch (DbException e) { + db.abortTransaction(txn); + throw e; } if (added) eventBus.broadcast(new TransportAddedEvent(t, maxLatency)); } public void addTransportKeys(ContactId c, TransportKeys k) throws DbException { - lock.writeLock().lock(); + T txn = db.startTransaction(); try { - T txn = db.startTransaction(); - try { - if (!db.containsContact(txn, c)) - throw new NoSuchContactException(); - if (!db.containsTransport(txn, k.getTransportId())) - throw new NoSuchTransportException(); - db.addTransportKeys(txn, c, k); - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } - } finally { - lock.writeLock().unlock(); + if (!db.containsContact(txn, c)) + throw new NoSuchContactException(); + if (!db.containsTransport(txn, k.getTransportId())) + throw new NoSuchTransportException(); + db.addTransportKeys(txn, c, k); + db.commitTransaction(txn); + } catch (DbException e) { + db.abortTransaction(txn); + throw e; } } @@ -329,21 +276,16 @@ class DatabaseComponentImpl implements DatabaseComponent { public Ack generateAck(ContactId c, int maxMessages) throws DbException { Collection ids; - lock.writeLock().lock(); + T txn = db.startTransaction(); try { - T txn = db.startTransaction(); - try { - if (!db.containsContact(txn, c)) - throw new NoSuchContactException(); - ids = db.getMessagesToAck(txn, c, maxMessages); - if (!ids.isEmpty()) db.lowerAckFlag(txn, c, ids); - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } - } finally { - lock.writeLock().unlock(); + if (!db.containsContact(txn, c)) + throw new NoSuchContactException(); + ids = db.getMessagesToAck(txn, c, maxMessages); + if (!ids.isEmpty()) db.lowerAckFlag(txn, c, ids); + db.commitTransaction(txn); + } catch (DbException e) { + db.abortTransaction(txn); + throw e; } if (ids.isEmpty()) return null; return new Ack(ids); @@ -353,25 +295,20 @@ class DatabaseComponentImpl implements DatabaseComponent { int maxLatency) throws DbException { Collection ids; List messages = new ArrayList(); - lock.writeLock().lock(); + T txn = db.startTransaction(); try { - T txn = db.startTransaction(); - try { - if (!db.containsContact(txn, c)) - throw new NoSuchContactException(); - ids = db.getMessagesToSend(txn, c, maxLength); - for (MessageId m : ids) { - messages.add(db.getRawMessage(txn, m)); - db.updateExpiryTime(txn, c, m, maxLatency); - } - if (!ids.isEmpty()) db.lowerRequestedFlag(txn, c, ids); - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; + if (!db.containsContact(txn, c)) + throw new NoSuchContactException(); + ids = db.getMessagesToSend(txn, c, maxLength); + for (MessageId m : ids) { + messages.add(db.getRawMessage(txn, m)); + db.updateExpiryTime(txn, c, m, maxLatency); } - } finally { - lock.writeLock().unlock(); + if (!ids.isEmpty()) db.lowerRequestedFlag(txn, c, ids); + db.commitTransaction(txn); + } catch (DbException e) { + db.abortTransaction(txn); + throw e; } if (messages.isEmpty()) return null; if (!ids.isEmpty()) eventBus.broadcast(new MessagesSentEvent(c, ids)); @@ -381,22 +318,16 @@ class DatabaseComponentImpl implements DatabaseComponent { public Offer generateOffer(ContactId c, int maxMessages, int maxLatency) throws DbException { Collection ids; - lock.writeLock().lock(); + T txn = db.startTransaction(); try { - T txn = db.startTransaction(); - try { - if (!db.containsContact(txn, c)) - throw new NoSuchContactException(); - ids = db.getMessagesToOffer(txn, c, maxMessages); - for (MessageId m : ids) - db.updateExpiryTime(txn, c, m, maxLatency); - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } - } finally { - lock.writeLock().unlock(); + if (!db.containsContact(txn, c)) + throw new NoSuchContactException(); + ids = db.getMessagesToOffer(txn, c, maxMessages); + for (MessageId m : ids) db.updateExpiryTime(txn, c, m, maxLatency); + db.commitTransaction(txn); + } catch (DbException e) { + db.abortTransaction(txn); + throw e; } if (ids.isEmpty()) return null; return new Offer(ids); @@ -405,21 +336,16 @@ class DatabaseComponentImpl implements DatabaseComponent { public Request generateRequest(ContactId c, int maxMessages) throws DbException { Collection ids; - lock.writeLock().lock(); + T txn = db.startTransaction(); try { - T txn = db.startTransaction(); - try { - if (!db.containsContact(txn, c)) - throw new NoSuchContactException(); - ids = db.getMessagesToRequest(txn, c, maxMessages); - if (!ids.isEmpty()) db.removeOfferedMessages(txn, c, ids); - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } - } finally { - lock.writeLock().unlock(); + if (!db.containsContact(txn, c)) + throw new NoSuchContactException(); + ids = db.getMessagesToRequest(txn, c, maxMessages); + if (!ids.isEmpty()) db.removeOfferedMessages(txn, c, ids); + db.commitTransaction(txn); + } catch (DbException e) { + db.abortTransaction(txn); + throw e; } if (ids.isEmpty()) return null; return new Request(ids); @@ -429,25 +355,20 @@ class DatabaseComponentImpl implements DatabaseComponent { int maxLatency) throws DbException { Collection ids; List messages = new ArrayList(); - lock.writeLock().lock(); + T txn = db.startTransaction(); try { - T txn = db.startTransaction(); - try { - if (!db.containsContact(txn, c)) - throw new NoSuchContactException(); - ids = db.getRequestedMessagesToSend(txn, c, maxLength); - for (MessageId m : ids) { - messages.add(db.getRawMessage(txn, m)); - db.updateExpiryTime(txn, c, m, maxLatency); - } - if (!ids.isEmpty()) db.lowerRequestedFlag(txn, c, ids); - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; + if (!db.containsContact(txn, c)) + throw new NoSuchContactException(); + ids = db.getRequestedMessagesToSend(txn, c, maxLength); + for (MessageId m : ids) { + messages.add(db.getRawMessage(txn, m)); + db.updateExpiryTime(txn, c, m, maxLatency); } - } finally { - lock.writeLock().unlock(); + if (!ids.isEmpty()) db.lowerRequestedFlag(txn, c, ids); + db.commitTransaction(txn); + } catch (DbException e) { + db.abortTransaction(txn); + throw e; } if (messages.isEmpty()) return null; if (!ids.isEmpty()) eventBus.broadcast(new MessagesSentEvent(c, ids)); @@ -455,518 +376,384 @@ class DatabaseComponentImpl implements DatabaseComponent { } public Contact getContact(ContactId c) throws DbException { - lock.readLock().lock(); + T txn = db.startTransaction(); try { - T txn = db.startTransaction(); - try { - if (!db.containsContact(txn, c)) - throw new NoSuchContactException(); - Contact contact = db.getContact(txn, c); - db.commitTransaction(txn); - return contact; - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } - } finally { - lock.readLock().unlock(); + if (!db.containsContact(txn, c)) + throw new NoSuchContactException(); + Contact contact = db.getContact(txn, c); + db.commitTransaction(txn); + return contact; + } catch (DbException e) { + db.abortTransaction(txn); + throw e; } } public Collection getContacts() throws DbException { - lock.readLock().lock(); + T txn = db.startTransaction(); try { - T txn = db.startTransaction(); - try { - Collection contacts = db.getContacts(txn); - db.commitTransaction(txn); - return contacts; - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } - } finally { - lock.readLock().unlock(); + Collection contacts = db.getContacts(txn); + db.commitTransaction(txn); + return contacts; + } catch (DbException e) { + db.abortTransaction(txn); + throw e; } } public Collection getContacts(AuthorId a) throws DbException { - lock.readLock().lock(); + T txn = db.startTransaction(); try { - T txn = db.startTransaction(); - try { - if (!db.containsLocalAuthor(txn, a)) - throw new NoSuchLocalAuthorException(); - Collection contacts = db.getContacts(txn, a); - db.commitTransaction(txn); - return contacts; - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } - } finally { - lock.readLock().unlock(); + if (!db.containsLocalAuthor(txn, a)) + throw new NoSuchLocalAuthorException(); + Collection contacts = db.getContacts(txn, a); + db.commitTransaction(txn); + return contacts; + } catch (DbException e) { + db.abortTransaction(txn); + throw e; } } public DeviceId getDeviceId() throws DbException { - lock.readLock().lock(); + T txn = db.startTransaction(); try { - T txn = db.startTransaction(); - try { - DeviceId id = db.getDeviceId(txn); - db.commitTransaction(txn); - return id; - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } - } finally { - lock.readLock().unlock(); + DeviceId id = db.getDeviceId(txn); + db.commitTransaction(txn); + return id; + } catch (DbException e) { + db.abortTransaction(txn); + throw e; } } public Group getGroup(GroupId g) throws DbException { - lock.readLock().lock(); + T txn = db.startTransaction(); try { - T txn = db.startTransaction(); - try { - if (!db.containsGroup(txn, g)) - throw new NoSuchGroupException(); - Group group = db.getGroup(txn, g); - db.commitTransaction(txn); - return group; - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } - } finally { - lock.readLock().unlock(); + if (!db.containsGroup(txn, g)) + throw new NoSuchGroupException(); + Group group = db.getGroup(txn, g); + db.commitTransaction(txn); + return group; + } catch (DbException e) { + db.abortTransaction(txn); + throw e; } } public Metadata getGroupMetadata(GroupId g) throws DbException { - lock.readLock().lock(); + T txn = db.startTransaction(); try { - T txn = db.startTransaction(); - try { - if (!db.containsGroup(txn, g)) - throw new NoSuchGroupException(); - Metadata metadata = db.getGroupMetadata(txn, g); - db.commitTransaction(txn); - return metadata; - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } - } finally { - lock.readLock().unlock(); + if (!db.containsGroup(txn, g)) + throw new NoSuchGroupException(); + Metadata metadata = db.getGroupMetadata(txn, g); + db.commitTransaction(txn); + return metadata; + } catch (DbException e) { + db.abortTransaction(txn); + throw e; } } public Collection getGroups(ClientId c) throws DbException { - lock.readLock().lock(); + T txn = db.startTransaction(); try { - T txn = db.startTransaction(); - try { - Collection groups = db.getGroups(txn, c); - db.commitTransaction(txn); - return groups; - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } - } finally { - lock.readLock().unlock(); + Collection groups = db.getGroups(txn, c); + db.commitTransaction(txn); + return groups; + } catch (DbException e) { + db.abortTransaction(txn); + throw e; } } public LocalAuthor getLocalAuthor(AuthorId a) throws DbException { - lock.readLock().lock(); + T txn = db.startTransaction(); try { - T txn = db.startTransaction(); - try { - if (!db.containsLocalAuthor(txn, a)) - throw new NoSuchLocalAuthorException(); - LocalAuthor localAuthor = db.getLocalAuthor(txn, a); - db.commitTransaction(txn); - return localAuthor; - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } - } finally { - lock.readLock().unlock(); + if (!db.containsLocalAuthor(txn, a)) + throw new NoSuchLocalAuthorException(); + LocalAuthor localAuthor = db.getLocalAuthor(txn, a); + db.commitTransaction(txn); + return localAuthor; + } catch (DbException e) { + db.abortTransaction(txn); + throw e; } } public Collection getLocalAuthors() throws DbException { - lock.readLock().lock(); + T txn = db.startTransaction(); try { - T txn = db.startTransaction(); - try { - Collection authors = db.getLocalAuthors(txn); - db.commitTransaction(txn); - return authors; - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } - } finally { - lock.readLock().unlock(); + Collection authors = db.getLocalAuthors(txn); + db.commitTransaction(txn); + return authors; + } catch (DbException e) { + db.abortTransaction(txn); + throw e; } } public Collection getMessagesToValidate(ClientId c) throws DbException { - lock.readLock().lock(); + T txn = db.startTransaction(); try { - T txn = db.startTransaction(); - try { - Collection ids = db.getMessagesToValidate(txn, c); - db.commitTransaction(txn); - return ids; - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } - } finally { - lock.readLock().unlock(); + Collection ids = db.getMessagesToValidate(txn, c); + db.commitTransaction(txn); + return ids; + } catch (DbException e) { + db.abortTransaction(txn); + throw e; } } public byte[] getRawMessage(MessageId m) throws DbException { - lock.readLock().lock(); + T txn = db.startTransaction(); try { - T txn = db.startTransaction(); - try { - if (!db.containsMessage(txn, m)) - throw new NoSuchMessageException(); - byte[] raw = db.getRawMessage(txn, m); - db.commitTransaction(txn); - return raw; - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } - } finally { - lock.readLock().unlock(); + if (!db.containsMessage(txn, m)) + throw new NoSuchMessageException(); + byte[] raw = db.getRawMessage(txn, m); + db.commitTransaction(txn); + return raw; + } catch (DbException e) { + db.abortTransaction(txn); + throw e; } } public Map getMessageMetadata(GroupId g) throws DbException { - lock.readLock().lock(); + T txn = db.startTransaction(); try { - T txn = db.startTransaction(); - try { - if (!db.containsGroup(txn, g)) - throw new NoSuchGroupException(); - Map metadata = - db.getMessageMetadata(txn, g); - db.commitTransaction(txn); - return metadata; - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } - } finally { - lock.readLock().unlock(); + if (!db.containsGroup(txn, g)) + throw new NoSuchGroupException(); + Map metadata = db.getMessageMetadata(txn, g); + db.commitTransaction(txn); + return metadata; + } catch (DbException e) { + db.abortTransaction(txn); + throw e; } } public Metadata getMessageMetadata(MessageId m) throws DbException { - lock.readLock().lock(); + T txn = db.startTransaction(); try { - T txn = db.startTransaction(); - try { - if (!db.containsMessage(txn, m)) - throw new NoSuchMessageException(); - Metadata metadata = db.getMessageMetadata(txn, m); - db.commitTransaction(txn); - return metadata; - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } - } finally { - lock.readLock().unlock(); + if (!db.containsMessage(txn, m)) + throw new NoSuchMessageException(); + Metadata metadata = db.getMessageMetadata(txn, m); + db.commitTransaction(txn); + return metadata; + } catch (DbException e) { + db.abortTransaction(txn); + throw e; } } public Collection getMessageStatus(ContactId c, GroupId g) throws DbException { - lock.readLock().lock(); + T txn = db.startTransaction(); try { - T txn = db.startTransaction(); - try { - if (!db.containsContact(txn, c)) - throw new NoSuchContactException(); - if (!db.containsGroup(txn, g)) - throw new NoSuchGroupException(); - Collection statuses = - db.getMessageStatus(txn, c, g); - db.commitTransaction(txn); - return statuses; - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } - } finally { - lock.readLock().unlock(); + if (!db.containsContact(txn, c)) + throw new NoSuchContactException(); + if (!db.containsGroup(txn, g)) + throw new NoSuchGroupException(); + Collection statuses = db.getMessageStatus(txn, c, g); + db.commitTransaction(txn); + return statuses; + } catch (DbException e) { + db.abortTransaction(txn); + throw e; } } public MessageStatus getMessageStatus(ContactId c, MessageId m) throws DbException { - lock.readLock().lock(); + T txn = db.startTransaction(); try { - T txn = db.startTransaction(); - try { - if (!db.containsContact(txn, c)) - throw new NoSuchContactException(); - if (!db.containsMessage(txn, m)) - throw new NoSuchMessageException(); - MessageStatus status = db.getMessageStatus(txn, c, m); - db.commitTransaction(txn); - return status; - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } - } finally { - lock.readLock().unlock(); + if (!db.containsContact(txn, c)) + throw new NoSuchContactException(); + if (!db.containsMessage(txn, m)) + throw new NoSuchMessageException(); + MessageStatus status = db.getMessageStatus(txn, c, m); + db.commitTransaction(txn); + return status; + } catch (DbException e) { + db.abortTransaction(txn); + throw e; } } public Settings getSettings(String namespace) throws DbException { - lock.readLock().lock(); + T txn = db.startTransaction(); try { - T txn = db.startTransaction(); - try { - Settings s = db.getSettings(txn, namespace); - db.commitTransaction(txn); - return s; - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } - } finally { - lock.readLock().unlock(); + Settings s = db.getSettings(txn, namespace); + db.commitTransaction(txn); + return s; + } catch (DbException e) { + db.abortTransaction(txn); + throw e; } } public Map getTransportKeys(TransportId t) throws DbException { - lock.readLock().lock(); + T txn = db.startTransaction(); try { - T txn = db.startTransaction(); - try { - if (!db.containsTransport(txn, t)) - throw new NoSuchTransportException(); - Map keys = - db.getTransportKeys(txn, t); - db.commitTransaction(txn); - return keys; - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } - } finally { - lock.readLock().unlock(); + if (!db.containsTransport(txn, t)) + throw new NoSuchTransportException(); + Map keys = db.getTransportKeys(txn, t); + db.commitTransaction(txn); + return keys; + } catch (DbException e) { + db.abortTransaction(txn); + throw e; } } public Map getTransportLatencies() throws DbException { - lock.readLock().lock(); + T txn = db.startTransaction(); try { - T txn = db.startTransaction(); - try { - Map latencies = - db.getTransportLatencies(txn); - db.commitTransaction(txn); - return latencies; - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } - } finally { - lock.readLock().unlock(); + Map latencies = db.getTransportLatencies(txn); + db.commitTransaction(txn); + return latencies; + } catch (DbException e) { + db.abortTransaction(txn); + throw e; } } public Collection getVisibility(GroupId g) throws DbException { - lock.readLock().lock(); + T txn = db.startTransaction(); try { - T txn = db.startTransaction(); - try { - if (!db.containsGroup(txn, g)) - throw new NoSuchGroupException(); - Collection visible = db.getVisibility(txn, g); - db.commitTransaction(txn); - return visible; - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } - } finally { - lock.readLock().unlock(); + if (!db.containsGroup(txn, g)) + throw new NoSuchGroupException(); + Collection visible = db.getVisibility(txn, g); + db.commitTransaction(txn); + return visible; + } catch (DbException e) { + db.abortTransaction(txn); + throw e; } } public void incrementStreamCounter(ContactId c, TransportId t, long rotationPeriod) throws DbException { - lock.writeLock().lock(); + T txn = db.startTransaction(); try { - T txn = db.startTransaction(); - try { - if (!db.containsContact(txn, c)) - throw new NoSuchContactException(); - if (!db.containsTransport(txn, t)) - throw new NoSuchTransportException(); - db.incrementStreamCounter(txn, c, t, rotationPeriod); - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } - } finally { - lock.writeLock().unlock(); + if (!db.containsContact(txn, c)) + throw new NoSuchContactException(); + if (!db.containsTransport(txn, t)) + throw new NoSuchTransportException(); + db.incrementStreamCounter(txn, c, t, rotationPeriod); + db.commitTransaction(txn); + } catch (DbException e) { + db.abortTransaction(txn); + throw e; } } public boolean isVisibleToContact(ContactId c, GroupId g) throws DbException { - lock.readLock().lock(); + T txn = db.startTransaction(); try { - T txn = db.startTransaction(); - try { - if (!db.containsContact(txn, c)) - throw new NoSuchContactException(); - if (!db.containsGroup(txn, g)) - throw new NoSuchGroupException(); - boolean visible = db.containsVisibleGroup(txn, c, g); - db.commitTransaction(txn); - return visible; - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } - } finally { - lock.readLock().unlock(); + if (!db.containsContact(txn, c)) + throw new NoSuchContactException(); + if (!db.containsGroup(txn, g)) + throw new NoSuchGroupException(); + boolean visible = db.containsVisibleGroup(txn, c, g); + db.commitTransaction(txn); + return visible; + } catch (DbException e) { + db.abortTransaction(txn); + throw e; } } public void mergeGroupMetadata(GroupId g, Metadata meta) throws DbException { - lock.writeLock().lock(); + T txn = db.startTransaction(); try { - T txn = db.startTransaction(); - try { - if (!db.containsGroup(txn, g)) - throw new NoSuchGroupException(); - db.mergeGroupMetadata(txn, g, meta); - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } - } finally { - lock.writeLock().unlock(); + if (!db.containsGroup(txn, g)) + throw new NoSuchGroupException(); + db.mergeGroupMetadata(txn, g, meta); + db.commitTransaction(txn); + } catch (DbException e) { + db.abortTransaction(txn); + throw e; } } public void mergeMessageMetadata(MessageId m, Metadata meta) throws DbException { - lock.writeLock().lock(); + T txn = db.startTransaction(); try { - T txn = db.startTransaction(); - try { - if (!db.containsMessage(txn, m)) - throw new NoSuchMessageException(); - db.mergeMessageMetadata(txn, m, meta); - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } - } finally { - lock.writeLock().unlock(); + if (!db.containsMessage(txn, m)) + throw new NoSuchMessageException(); + db.mergeMessageMetadata(txn, m, meta); + db.commitTransaction(txn); + } catch (DbException e) { + db.abortTransaction(txn); + throw e; } } public void mergeSettings(Settings s, String namespace) throws DbException { boolean changed = false; - lock.writeLock().lock(); + T txn = db.startTransaction(); try { - T txn = db.startTransaction(); - try { - Settings old = db.getSettings(txn, namespace); - Settings merged = new Settings(); - merged.putAll(old); - merged.putAll(s); - if (!merged.equals(old)) { - db.mergeSettings(txn, s, namespace); - changed = true; - } - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; + Settings old = db.getSettings(txn, namespace); + Settings merged = new Settings(); + merged.putAll(old); + merged.putAll(s); + if (!merged.equals(old)) { + db.mergeSettings(txn, s, namespace); + changed = true; } - } finally { - lock.writeLock().unlock(); + db.commitTransaction(txn); + } catch (DbException e) { + db.abortTransaction(txn); + throw e; } if (changed) eventBus.broadcast(new SettingsUpdatedEvent(namespace)); } public void receiveAck(ContactId c, Ack a) throws DbException { Collection acked = new ArrayList(); - lock.writeLock().lock(); + T txn = db.startTransaction(); try { - T txn = db.startTransaction(); - try { - if (!db.containsContact(txn, c)) - throw new NoSuchContactException(); - for (MessageId m : a.getMessageIds()) { - if (db.containsVisibleMessage(txn, c, m)) { - db.raiseSeenFlag(txn, c, m); - acked.add(m); - } + if (!db.containsContact(txn, c)) + throw new NoSuchContactException(); + for (MessageId m : a.getMessageIds()) { + if (db.containsVisibleMessage(txn, c, m)) { + db.raiseSeenFlag(txn, c, m); + acked.add(m); } - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; } - } finally { - lock.writeLock().unlock(); + db.commitTransaction(txn); + } catch (DbException e) { + db.abortTransaction(txn); + throw e; } eventBus.broadcast(new MessagesAckedEvent(c, acked)); } public void receiveMessage(ContactId c, Message m) throws DbException { boolean duplicate, visible; - lock.writeLock().lock(); + T txn = db.startTransaction(); try { - T txn = db.startTransaction(); - try { - if (!db.containsContact(txn, c)) - throw new NoSuchContactException(); - duplicate = db.containsMessage(txn, m.getId()); - visible = db.containsVisibleGroup(txn, c, m.getGroupId()); - if (visible) { - if (!duplicate) addMessage(txn, m, UNKNOWN, false, c); - db.raiseAckFlag(txn, c, m.getId()); - } - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; + if (!db.containsContact(txn, c)) + throw new NoSuchContactException(); + duplicate = db.containsMessage(txn, m.getId()); + visible = db.containsVisibleGroup(txn, c, m.getGroupId()); + if (visible) { + if (!duplicate) addMessage(txn, m, UNKNOWN, false, c); + db.raiseAckFlag(txn, c, m.getId()); } - } finally { - lock.writeLock().unlock(); + db.commitTransaction(txn); + } catch (DbException e) { + db.abortTransaction(txn); + throw e; } if (visible) { if (!duplicate) eventBus.broadcast(new MessageAddedEvent(m, c)); @@ -976,31 +763,26 @@ class DatabaseComponentImpl implements DatabaseComponent { public void receiveOffer(ContactId c, Offer o) throws DbException { boolean ack = false, request = false; - lock.writeLock().lock(); + T txn = db.startTransaction(); try { - T txn = db.startTransaction(); - try { - if (!db.containsContact(txn, c)) - throw new NoSuchContactException(); - int count = db.countOfferedMessages(txn, c); - for (MessageId m : o.getMessageIds()) { - if (db.containsVisibleMessage(txn, c, m)) { - db.raiseSeenFlag(txn, c, m); - db.raiseAckFlag(txn, c, m); - ack = true; - } else if (count < MAX_OFFERED_MESSAGES) { - db.addOfferedMessage(txn, c, m); - request = true; - count++; - } + if (!db.containsContact(txn, c)) + throw new NoSuchContactException(); + int count = db.countOfferedMessages(txn, c); + for (MessageId m : o.getMessageIds()) { + if (db.containsVisibleMessage(txn, c, m)) { + db.raiseSeenFlag(txn, c, m); + db.raiseAckFlag(txn, c, m); + ack = true; + } else if (count < MAX_OFFERED_MESSAGES) { + db.addOfferedMessage(txn, c, m); + request = true; + count++; } - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; } - } finally { - lock.writeLock().unlock(); + db.commitTransaction(txn); + } catch (DbException e) { + db.abortTransaction(txn); + throw e; } if (ack) eventBus.broadcast(new MessageToAckEvent(c)); if (request) eventBus.broadcast(new MessageToRequestEvent(c)); @@ -1008,239 +790,184 @@ class DatabaseComponentImpl implements DatabaseComponent { public void receiveRequest(ContactId c, Request r) throws DbException { boolean requested = false; - lock.writeLock().lock(); + T txn = db.startTransaction(); try { - T txn = db.startTransaction(); - try { - if (!db.containsContact(txn, c)) - throw new NoSuchContactException(); - for (MessageId m : r.getMessageIds()) { - if (db.containsVisibleMessage(txn, c, m)) { - db.raiseRequestedFlag(txn, c, m); - db.resetExpiryTime(txn, c, m); - requested = true; - } + if (!db.containsContact(txn, c)) + throw new NoSuchContactException(); + for (MessageId m : r.getMessageIds()) { + if (db.containsVisibleMessage(txn, c, m)) { + db.raiseRequestedFlag(txn, c, m); + db.resetExpiryTime(txn, c, m); + requested = true; } - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; } - } finally { - lock.writeLock().unlock(); + db.commitTransaction(txn); + } catch (DbException e) { + db.abortTransaction(txn); + throw e; } if (requested) eventBus.broadcast(new MessageRequestedEvent(c)); } public void removeContact(ContactId c) throws DbException { - lock.writeLock().lock(); + T txn = db.startTransaction(); try { - T txn = db.startTransaction(); - try { - if (!db.containsContact(txn, c)) - throw new NoSuchContactException(); - db.removeContact(txn, c); - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } - } finally { - lock.writeLock().unlock(); + if (!db.containsContact(txn, c)) + throw new NoSuchContactException(); + db.removeContact(txn, c); + db.commitTransaction(txn); + } catch (DbException e) { + db.abortTransaction(txn); + throw e; } } public void removeGroup(Group g) throws DbException { Collection affected; - lock.writeLock().lock(); + T txn = db.startTransaction(); try { - T txn = db.startTransaction(); - try { - GroupId id = g.getId(); - if (!db.containsGroup(txn, id)) - throw new NoSuchGroupException(); - affected = db.getVisibility(txn, id); - db.removeGroup(txn, id); - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } - } finally { - lock.writeLock().unlock(); + GroupId id = g.getId(); + if (!db.containsGroup(txn, id)) + throw new NoSuchGroupException(); + affected = db.getVisibility(txn, id); + db.removeGroup(txn, id); + db.commitTransaction(txn); + } catch (DbException e) { + db.abortTransaction(txn); + throw e; } eventBus.broadcast(new GroupRemovedEvent(g)); eventBus.broadcast(new GroupVisibilityUpdatedEvent(affected)); } public void removeLocalAuthor(AuthorId a) throws DbException { - lock.writeLock().lock(); + T txn = db.startTransaction(); try { - T txn = db.startTransaction(); - try { - if (!db.containsLocalAuthor(txn, a)) - throw new NoSuchLocalAuthorException(); - db.removeLocalAuthor(txn, a); - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } - } finally { - lock.writeLock().unlock(); + if (!db.containsLocalAuthor(txn, a)) + throw new NoSuchLocalAuthorException(); + db.removeLocalAuthor(txn, a); + db.commitTransaction(txn); + } catch (DbException e) { + db.abortTransaction(txn); + throw e; } } public void removeTransport(TransportId t) throws DbException { - lock.writeLock().lock(); + T txn = db.startTransaction(); try { - T txn = db.startTransaction(); - try { - if (!db.containsTransport(txn, t)) - throw new NoSuchTransportException(); - db.removeTransport(txn, t); - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } - } finally { - lock.writeLock().unlock(); + if (!db.containsTransport(txn, t)) + throw new NoSuchTransportException(); + db.removeTransport(txn, t); + db.commitTransaction(txn); + } catch (DbException e) { + db.abortTransaction(txn); + throw e; } eventBus.broadcast(new TransportRemovedEvent(t)); } public void setContactStatus(ContactId c, StorageStatus s) throws DbException { - lock.writeLock().lock(); + T txn = db.startTransaction(); try { - T txn = db.startTransaction(); - try { - if (!db.containsContact(txn, c)) - throw new NoSuchContactException(); - db.setContactStatus(txn, c, s); - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } - } finally { - lock.writeLock().unlock(); + if (!db.containsContact(txn, c)) + throw new NoSuchContactException(); + db.setContactStatus(txn, c, s); + db.commitTransaction(txn); + } catch (DbException e) { + db.abortTransaction(txn); + throw e; } } public void setLocalAuthorStatus(AuthorId a, StorageStatus s) throws DbException { - lock.writeLock().lock(); + T txn = db.startTransaction(); try { - T txn = db.startTransaction(); - try { - if (!db.containsLocalAuthor(txn, a)) - throw new NoSuchLocalAuthorException(); - db.setLocalAuthorStatus(txn, a, s); - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } - } finally { - lock.writeLock().unlock(); + if (!db.containsLocalAuthor(txn, a)) + throw new NoSuchLocalAuthorException(); + db.setLocalAuthorStatus(txn, a, s); + db.commitTransaction(txn); + } catch (DbException e) { + db.abortTransaction(txn); + throw e; } } public void setMessageShared(Message m, boolean shared) throws DbException { - lock.writeLock().lock(); + T txn = db.startTransaction(); try { - T txn = db.startTransaction(); - try { - if (!db.containsMessage(txn, m.getId())) - throw new NoSuchMessageException(); - db.setMessageShared(txn, m.getId(), shared); - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } - } finally { - lock.writeLock().unlock(); + if (!db.containsMessage(txn, m.getId())) + throw new NoSuchMessageException(); + db.setMessageShared(txn, m.getId(), shared); + db.commitTransaction(txn); + } catch (DbException e) { + db.abortTransaction(txn); + throw e; } if (shared) eventBus.broadcast(new MessageSharedEvent(m)); } public void setMessageValid(Message m, ClientId c, boolean valid) throws DbException { - lock.writeLock().lock(); + T txn = db.startTransaction(); try { - T txn = db.startTransaction(); - try { - if (!db.containsMessage(txn, m.getId())) - throw new NoSuchMessageException(); - db.setMessageValid(txn, m.getId(), valid); - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } - } finally { - lock.writeLock().unlock(); + if (!db.containsMessage(txn, m.getId())) + throw new NoSuchMessageException(); + db.setMessageValid(txn, m.getId(), valid); + db.commitTransaction(txn); + } catch (DbException e) { + db.abortTransaction(txn); + throw e; } eventBus.broadcast(new MessageValidatedEvent(m, c, false, valid)); } public void setReorderingWindow(ContactId c, TransportId t, long rotationPeriod, long base, byte[] bitmap) throws DbException { - lock.writeLock().lock(); + T txn = db.startTransaction(); try { - T txn = db.startTransaction(); - try { - if (!db.containsContact(txn, c)) - throw new NoSuchContactException(); - if (!db.containsTransport(txn, t)) - throw new NoSuchTransportException(); - db.setReorderingWindow(txn, c, t, rotationPeriod, base, bitmap); - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } - } finally { - lock.writeLock().unlock(); + if (!db.containsContact(txn, c)) + throw new NoSuchContactException(); + if (!db.containsTransport(txn, t)) + throw new NoSuchTransportException(); + db.setReorderingWindow(txn, c, t, rotationPeriod, base, bitmap); + db.commitTransaction(txn); + } catch (DbException e) { + db.abortTransaction(txn); + throw e; } } public void setVisibility(GroupId g, Collection visible) throws DbException { Collection affected = new ArrayList(); - lock.writeLock().lock(); + T txn = db.startTransaction(); try { - T txn = db.startTransaction(); - try { - if (!db.containsGroup(txn, g)) - throw new NoSuchGroupException(); - // Use HashSets for O(1) lookups, O(n) overall running time - Collection now = new HashSet(visible); - Collection before = db.getVisibility(txn, g); - before = new HashSet(before); - // Set the group's visibility for each current contact - for (ContactId c : db.getContactIds(txn)) { - boolean wasBefore = before.contains(c); - boolean isNow = now.contains(c); - if (!wasBefore && isNow) { - db.addVisibility(txn, c, g); - affected.add(c); - } else if (wasBefore && !isNow) { - db.removeVisibility(txn, c, g); - affected.add(c); - } + if (!db.containsGroup(txn, g)) + throw new NoSuchGroupException(); + // Use HashSets for O(1) lookups, O(n) overall running time + Collection now = new HashSet(visible); + Collection before = db.getVisibility(txn, g); + before = new HashSet(before); + // Set the group's visibility for each current contact + for (ContactId c : db.getContactIds(txn)) { + boolean wasBefore = before.contains(c); + boolean isNow = now.contains(c); + if (!wasBefore && isNow) { + db.addVisibility(txn, c, g); + affected.add(c); + } else if (wasBefore && !isNow) { + db.removeVisibility(txn, c, g); + affected.add(c); } - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; } - } finally { - lock.writeLock().unlock(); + db.commitTransaction(txn); + } catch (DbException e) { + db.abortTransaction(txn); + throw e; } if (!affected.isEmpty()) eventBus.broadcast(new GroupVisibilityUpdatedEvent(affected)); @@ -1249,24 +976,19 @@ class DatabaseComponentImpl implements DatabaseComponent { public void setVisibleToContact(ContactId c, GroupId g, boolean visible) throws DbException { boolean wasVisible = false; - lock.writeLock().lock(); + T txn = db.startTransaction(); try { - T txn = db.startTransaction(); - try { - if (!db.containsContact(txn, c)) - throw new NoSuchContactException(); - if (!db.containsGroup(txn, g)) - throw new NoSuchGroupException(); - wasVisible = db.containsVisibleGroup(txn, c, g); - if (visible && !wasVisible) db.addVisibility(txn, c, g); - else if (!visible && wasVisible) db.removeVisibility(txn, c, g); - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } - } finally { - lock.writeLock().unlock(); + if (!db.containsContact(txn, c)) + throw new NoSuchContactException(); + if (!db.containsGroup(txn, g)) + throw new NoSuchGroupException(); + wasVisible = db.containsVisibleGroup(txn, c, g); + if (visible && !wasVisible) db.addVisibility(txn, c, g); + else if (!visible && wasVisible) db.removeVisibility(txn, c, g); + db.commitTransaction(txn); + } catch (DbException e) { + db.abortTransaction(txn); + throw e; } if (visible != wasVisible) { eventBus.broadcast(new GroupVisibilityUpdatedEvent( @@ -1276,28 +998,23 @@ class DatabaseComponentImpl implements DatabaseComponent { public void updateTransportKeys(Map keys) throws DbException { - lock.writeLock().lock(); + T txn = db.startTransaction(); try { - T txn = db.startTransaction(); - try { - Map filtered = - new HashMap(); - for (Entry e : keys.entrySet()) { - ContactId c = e.getKey(); - TransportKeys k = e.getValue(); - if (db.containsContact(txn, c) - && db.containsTransport(txn, k.getTransportId())) { - filtered.put(c, k); - } + Map filtered = + new HashMap(); + for (Entry e : keys.entrySet()) { + ContactId c = e.getKey(); + TransportKeys k = e.getValue(); + if (db.containsContact(txn, c) + && db.containsTransport(txn, k.getTransportId())) { + filtered.put(c, k); } - db.updateTransportKeys(txn, filtered); - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; } - } finally { - lock.writeLock().unlock(); + db.updateTransportKeys(txn, filtered); + db.commitTransaction(txn); + } catch (DbException e) { + db.abortTransaction(txn); + throw e; } } } diff --git a/briar-core/src/org/briarproject/db/JdbcDatabase.java b/briar-core/src/org/briarproject/db/JdbcDatabase.java index ac583594b..b1b536d7f 100644 --- a/briar-core/src/org/briarproject/db/JdbcDatabase.java +++ b/briar-core/src/org/briarproject/db/JdbcDatabase.java @@ -41,7 +41,6 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; -import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @@ -228,8 +227,6 @@ abstract class JdbcDatabase implements Database { private final LinkedList connections = new LinkedList(); // Locking: connectionsLock - private final AtomicInteger transactionCount = new AtomicInteger(0); - private int openConnections = 0; // Locking: connectionsLock private boolean closed = false; // Locking: connectionsLock @@ -369,7 +366,6 @@ abstract class JdbcDatabase implements Database { } catch (SQLException e) { throw new DbException(e); } - transactionCount.incrementAndGet(); return txn; } @@ -418,14 +414,6 @@ abstract class JdbcDatabase implements Database { } } - public int getTransactionCount() { - return transactionCount.get(); - } - - public void resetTransactionCount() { - transactionCount.set(0); - } - protected void closeAllConnections() throws SQLException { boolean interrupted = false; connectionsLock.lock(); From 675ce4bfef29a5a7f576557b28904bd5a62faa61 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Mon, 8 Feb 2016 11:44:57 +0000 Subject: [PATCH 02/11] Removed direct calls to DB's contact/identity methods. --- .../api/contact/ContactManager.java | 4 ++-- .../api/identity/IdentityManager.java | 4 ++-- .../contact/ContactManagerImpl.java | 16 ++++++++++------ .../briarproject/forum/ForumManagerImpl.java | 9 ++++++--- .../forum/ForumSharingManagerImpl.java | 12 ++++++------ .../identity/IdentityManagerImpl.java | 18 ++++++++++-------- .../messaging/MessagingManagerImpl.java | 12 ++++++------ .../TransportPropertyManagerImpl.java | 12 ++++++------ .../contact/ContactManagerImplTest.java | 14 ++++++++++++++ .../identity/IdentityManagerImplTest.java | 14 ++++++++++++++ 10 files changed, 76 insertions(+), 39 deletions(-) create mode 100644 briar-tests/src/org/briarproject/contact/ContactManagerImplTest.java create mode 100644 briar-tests/src/org/briarproject/identity/IdentityManagerImplTest.java diff --git a/briar-api/src/org/briarproject/api/contact/ContactManager.java b/briar-api/src/org/briarproject/api/contact/ContactManager.java index 08c9dc46a..3b311d66d 100644 --- a/briar-api/src/org/briarproject/api/contact/ContactManager.java +++ b/briar-api/src/org/briarproject/api/contact/ContactManager.java @@ -30,10 +30,10 @@ public interface ContactManager { void removeContact(ContactId c) throws DbException; interface AddContactHook { - void addingContact(ContactId c); + void addingContact(Contact c); } interface RemoveContactHook { - void removingContact(ContactId c); + void removingContact(Contact c); } } diff --git a/briar-api/src/org/briarproject/api/identity/IdentityManager.java b/briar-api/src/org/briarproject/api/identity/IdentityManager.java index 9a5a030ab..e872d845e 100644 --- a/briar-api/src/org/briarproject/api/identity/IdentityManager.java +++ b/briar-api/src/org/briarproject/api/identity/IdentityManager.java @@ -25,10 +25,10 @@ public interface IdentityManager { void removeLocalAuthor(AuthorId a) throws DbException; interface AddIdentityHook { - void addingIdentity(AuthorId a); + void addingIdentity(LocalAuthor a); } interface RemoveIdentityHook { - void removingIdentity(AuthorId a); + void removingIdentity(LocalAuthor a); } } diff --git a/briar-core/src/org/briarproject/contact/ContactManagerImpl.java b/briar-core/src/org/briarproject/contact/ContactManagerImpl.java index fa1c09621..b8f9553c8 100644 --- a/briar-core/src/org/briarproject/contact/ContactManagerImpl.java +++ b/briar-core/src/org/briarproject/contact/ContactManagerImpl.java @@ -14,6 +14,7 @@ import org.briarproject.api.event.EventBus; import org.briarproject.api.identity.Author; import org.briarproject.api.identity.AuthorId; import org.briarproject.api.identity.IdentityManager.RemoveIdentityHook; +import org.briarproject.api.identity.LocalAuthor; import org.briarproject.api.lifecycle.Service; import java.util.ArrayList; @@ -54,12 +55,12 @@ class ContactManagerImpl implements ContactManager, Service, for (Contact c : db.getContacts()) { if (c.getStatus().equals(ADDING)) { for (AddContactHook hook : addHooks) - hook.addingContact(c.getId()); + 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.getId()); + hook.removingContact(c); db.removeContact(c.getId()); eventBus.broadcast(new ContactRemovedEvent(c.getId())); } @@ -90,7 +91,8 @@ class ContactManagerImpl implements ContactManager, Service, public ContactId addContact(Author remote, AuthorId local) throws DbException { ContactId c = db.addContact(remote, local); - for (AddContactHook hook : addHooks) hook.addingContact(c); + Contact contact = db.getContact(c); + for (AddContactHook hook : addHooks) hook.addingContact(contact); db.setContactStatus(c, ACTIVE); eventBus.broadcast(new ContactAddedEvent(c)); return c; @@ -115,17 +117,19 @@ 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(c); + for (RemoveContactHook hook : removeHooks) + hook.removingContact(contact); db.removeContact(c); eventBus.broadcast(new ContactRemovedEvent(c)); } @Override - public void removingIdentity(AuthorId a) { + public void removingIdentity(LocalAuthor a) { // Remove any contacts of the local pseudonym that's being removed try { - for (ContactId c : db.getContacts(a)) removeContact(c); + for (ContactId c : db.getContacts(a.getId())) removeContact(c); } catch (DbException e) { if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); } diff --git a/briar-core/src/org/briarproject/forum/ForumManagerImpl.java b/briar-core/src/org/briarproject/forum/ForumManagerImpl.java index 0e2c581c8..6820afb47 100644 --- a/briar-core/src/org/briarproject/forum/ForumManagerImpl.java +++ b/briar-core/src/org/briarproject/forum/ForumManagerImpl.java @@ -19,6 +19,7 @@ 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; @@ -59,6 +60,7 @@ class ForumManagerImpl implements ForumManager { private final DatabaseComponent db; private final ContactManager contactManager; + private final IdentityManager identityManager; private final BdfReaderFactory bdfReaderFactory; private final MetadataEncoder metadataEncoder; private final MetadataParser metadataParser; @@ -68,10 +70,11 @@ class ForumManagerImpl implements ForumManager { @Inject ForumManagerImpl(DatabaseComponent db, ContactManager contactManager, - BdfReaderFactory bdfReaderFactory, MetadataEncoder metadataEncoder, - MetadataParser metadataParser) { + IdentityManager identityManager, BdfReaderFactory bdfReaderFactory, + MetadataEncoder metadataEncoder, MetadataParser metadataParser) { this.db = db; this.contactManager = contactManager; + this.identityManager = identityManager; this.bdfReaderFactory = bdfReaderFactory; this.metadataEncoder = metadataEncoder; this.metadataParser = metadataParser; @@ -173,7 +176,7 @@ class ForumManagerImpl implements ForumManager { try { // Load the IDs of the user's identities Set localAuthorIds = new HashSet(); - for (LocalAuthor a : db.getLocalAuthors()) + for (LocalAuthor a : identityManager.getLocalAuthors()) localAuthorIds.add(a.getId()); // Load the IDs of contacts' identities Set contactAuthorIds = new HashSet(); diff --git a/briar-core/src/org/briarproject/forum/ForumSharingManagerImpl.java b/briar-core/src/org/briarproject/forum/ForumSharingManagerImpl.java index 5218e5388..abfb7f6c9 100644 --- a/briar-core/src/org/briarproject/forum/ForumSharingManagerImpl.java +++ b/briar-core/src/org/briarproject/forum/ForumSharingManagerImpl.java @@ -106,17 +106,17 @@ class ForumSharingManagerImpl implements ForumSharingManager, AddContactHook, } @Override - public void addingContact(ContactId c) { + public void addingContact(Contact c) { lock.writeLock().lock(); try { // Create a group to share with the contact - Group g = getContactGroup(db.getContact(c)); + Group g = getContactGroup(c); // Store the group and share it with the contact db.addGroup(g); - db.setVisibility(g.getId(), Collections.singletonList(c)); + db.setVisibility(g.getId(), Collections.singletonList(c.getId())); // Attach the contact ID to the group BdfDictionary d = new BdfDictionary(); - d.put("contactId", c.getInt()); + d.put("contactId", c.getId().getInt()); db.mergeGroupMetadata(g.getId(), metadataEncoder.encode(d)); // Share any forums that are shared with all contacts List shared = getForumsSharedWithAllContacts(); @@ -131,10 +131,10 @@ class ForumSharingManagerImpl implements ForumSharingManager, AddContactHook, } @Override - public void removingContact(ContactId c) { + public void removingContact(Contact c) { lock.writeLock().lock(); try { - db.removeGroup(getContactGroup(db.getContact(c))); + db.removeGroup(getContactGroup(c)); } catch (DbException e) { if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); } finally { diff --git a/briar-core/src/org/briarproject/identity/IdentityManagerImpl.java b/briar-core/src/org/briarproject/identity/IdentityManagerImpl.java index a51ce0238..71b34eac9 100644 --- a/briar-core/src/org/briarproject/identity/IdentityManagerImpl.java +++ b/briar-core/src/org/briarproject/identity/IdentityManagerImpl.java @@ -50,12 +50,12 @@ class IdentityManagerImpl implements IdentityManager, Service { for (LocalAuthor a : db.getLocalAuthors()) { if (a.getStatus().equals(ADDING)) { for (AddIdentityHook hook : addHooks) - hook.addingIdentity(a.getId()); + 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.getId()); + hook.removingIdentity(a); db.removeLocalAuthor(a.getId()); eventBus.broadcast(new LocalAuthorRemovedEvent(a.getId())); } @@ -83,11 +83,11 @@ class IdentityManagerImpl implements IdentityManager, Service { } @Override - public void addLocalAuthor(LocalAuthor a) throws DbException { - db.addLocalAuthor(a); - for (AddIdentityHook hook : addHooks) hook.addingIdentity(a.getId()); - db.setLocalAuthorStatus(a.getId(), ACTIVE); - eventBus.broadcast(new LocalAuthorAddedEvent(a.getId())); + public void addLocalAuthor(LocalAuthor localAuthor) throws DbException { + db.addLocalAuthor(localAuthor); + for (AddIdentityHook hook : addHooks) hook.addingIdentity(localAuthor); + db.setLocalAuthorStatus(localAuthor.getId(), ACTIVE); + eventBus.broadcast(new LocalAuthorAddedEvent(localAuthor.getId())); } @Override @@ -109,8 +109,10 @@ 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(a); + for (RemoveIdentityHook hook : removeHooks) + hook.removingIdentity(localAuthor); db.removeLocalAuthor(a); eventBus.broadcast(new LocalAuthorRemovedEvent(a)); } diff --git a/briar-core/src/org/briarproject/messaging/MessagingManagerImpl.java b/briar-core/src/org/briarproject/messaging/MessagingManagerImpl.java index 65f043289..a5514fc04 100644 --- a/briar-core/src/org/briarproject/messaging/MessagingManagerImpl.java +++ b/briar-core/src/org/briarproject/messaging/MessagingManagerImpl.java @@ -71,16 +71,16 @@ class MessagingManagerImpl implements MessagingManager, AddContactHook, } @Override - public void addingContact(ContactId c) { + public void addingContact(Contact c) { try { // Create a group to share with the contact - Group g = getContactGroup(db.getContact(c)); + Group g = getContactGroup(c); // Store the group and share it with the contact db.addGroup(g); - db.setVisibility(g.getId(), Collections.singletonList(c)); + db.setVisibility(g.getId(), Collections.singletonList(c.getId())); // Attach the contact ID to the group BdfDictionary d = new BdfDictionary(); - d.put("contactId", c.getInt()); + 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); @@ -94,9 +94,9 @@ class MessagingManagerImpl implements MessagingManager, AddContactHook, } @Override - public void removingContact(ContactId c) { + public void removingContact(Contact c) { try { - db.removeGroup(getContactGroup(db.getContact(c))); + db.removeGroup(getContactGroup(c)); } catch (DbException e) { if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); } diff --git a/briar-core/src/org/briarproject/properties/TransportPropertyManagerImpl.java b/briar-core/src/org/briarproject/properties/TransportPropertyManagerImpl.java index bbc6cd57d..e972093d7 100644 --- a/briar-core/src/org/briarproject/properties/TransportPropertyManagerImpl.java +++ b/briar-core/src/org/briarproject/properties/TransportPropertyManagerImpl.java @@ -95,14 +95,14 @@ class TransportPropertyManagerImpl implements TransportPropertyManager, } @Override - public void addingContact(ContactId c) { + public void addingContact(Contact c) { lock.writeLock().lock(); try { // Create a group to share with the contact - Group g = getContactGroup(db.getContact(c)); + Group g = getContactGroup(c); // Store the group and share it with the contact db.addGroup(g); - db.setVisibility(g.getId(), Collections.singletonList(c)); + db.setVisibility(g.getId(), Collections.singletonList(c.getId())); // Copy the latest local properties into the group DeviceId dev = db.getDeviceId(); Map local = getLocalProperties(); @@ -120,10 +120,10 @@ class TransportPropertyManagerImpl implements TransportPropertyManager, } @Override - public void removingContact(ContactId c) { + public void removingContact(Contact c) { lock.writeLock().lock(); try { - db.removeGroup(getContactGroup(db.getContact(c))); + db.removeGroup(getContactGroup(c)); } catch (DbException e) { if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); } finally { @@ -136,7 +136,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager, Map props) throws DbException { lock.writeLock().lock(); try { - Group g = getContactGroup(db.getContact(c)); + Group g = getContactGroup(contactManager.getContact(c)); for (Entry e : props.entrySet()) { storeMessage(g.getId(), dev, e.getKey(), e.getValue(), 0, false, false); diff --git a/briar-tests/src/org/briarproject/contact/ContactManagerImplTest.java b/briar-tests/src/org/briarproject/contact/ContactManagerImplTest.java new file mode 100644 index 000000000..6cde435e3 --- /dev/null +++ b/briar-tests/src/org/briarproject/contact/ContactManagerImplTest.java @@ -0,0 +1,14 @@ +package org.briarproject.contact; + +import org.briarproject.BriarTestCase; +import org.junit.Test; + +import static org.junit.Assert.fail; + +public class ContactManagerImplTest extends BriarTestCase { + + @Test + public void testUnitTestsExist() { + fail(); // FIXME: Write tests + } +} diff --git a/briar-tests/src/org/briarproject/identity/IdentityManagerImplTest.java b/briar-tests/src/org/briarproject/identity/IdentityManagerImplTest.java new file mode 100644 index 000000000..c04567a0a --- /dev/null +++ b/briar-tests/src/org/briarproject/identity/IdentityManagerImplTest.java @@ -0,0 +1,14 @@ +package org.briarproject.identity; + +import org.briarproject.BriarTestCase; +import org.junit.Test; + +import static org.junit.Assert.fail; + +public class IdentityManagerImplTest extends BriarTestCase { + + @Test + public void testUnitTestsExist() { + fail(); // FIXME: Write tests + } +} From 2460e1feb0c35404f62e2f0e048b17a664c64682 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Mon, 8 Feb 2016 14:27:27 +0000 Subject: [PATCH 03/11] Moved author classes to identity package. --- .../{sync => identity}/AuthorFactoryImpl.java | 3 +-- .../briarproject/{sync => identity}/AuthorReader.java | 3 +-- .../src/org/briarproject/identity/IdentityModule.java | 11 +++++++++++ briar-core/src/org/briarproject/sync/SyncModule.java | 9 --------- 4 files changed, 13 insertions(+), 13 deletions(-) rename briar-core/src/org/briarproject/{sync => identity}/AuthorFactoryImpl.java (95%) rename briar-core/src/org/briarproject/{sync => identity}/AuthorReader.java (92%) diff --git a/briar-core/src/org/briarproject/sync/AuthorFactoryImpl.java b/briar-core/src/org/briarproject/identity/AuthorFactoryImpl.java similarity index 95% rename from briar-core/src/org/briarproject/sync/AuthorFactoryImpl.java rename to briar-core/src/org/briarproject/identity/AuthorFactoryImpl.java index bfb768802..87c03a599 100644 --- a/briar-core/src/org/briarproject/sync/AuthorFactoryImpl.java +++ b/briar-core/src/org/briarproject/identity/AuthorFactoryImpl.java @@ -1,4 +1,4 @@ -package org.briarproject.sync; +package org.briarproject.identity; import org.briarproject.api.crypto.CryptoComponent; import org.briarproject.api.data.BdfWriter; @@ -16,7 +16,6 @@ import javax.inject.Inject; import static org.briarproject.api.db.StorageStatus.ADDING; -// TODO: Move this class to the identity package class AuthorFactoryImpl implements AuthorFactory { private final CryptoComponent crypto; diff --git a/briar-core/src/org/briarproject/sync/AuthorReader.java b/briar-core/src/org/briarproject/identity/AuthorReader.java similarity index 92% rename from briar-core/src/org/briarproject/sync/AuthorReader.java rename to briar-core/src/org/briarproject/identity/AuthorReader.java index cb697681e..16be01ae6 100644 --- a/briar-core/src/org/briarproject/sync/AuthorReader.java +++ b/briar-core/src/org/briarproject/identity/AuthorReader.java @@ -1,4 +1,4 @@ -package org.briarproject.sync; +package org.briarproject.identity; import org.briarproject.api.FormatException; import org.briarproject.api.data.BdfReader; @@ -11,7 +11,6 @@ import java.io.IOException; import static org.briarproject.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH; import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH; -// TODO: Move this class to the identity package class AuthorReader implements ObjectReader { private final AuthorFactory authorFactory; diff --git a/briar-core/src/org/briarproject/identity/IdentityModule.java b/briar-core/src/org/briarproject/identity/IdentityModule.java index 98134d199..a57b916cc 100644 --- a/briar-core/src/org/briarproject/identity/IdentityModule.java +++ b/briar-core/src/org/briarproject/identity/IdentityModule.java @@ -1,13 +1,24 @@ package org.briarproject.identity; import com.google.inject.AbstractModule; +import com.google.inject.Provides; +import org.briarproject.api.data.ObjectReader; +import org.briarproject.api.identity.Author; +import org.briarproject.api.identity.AuthorFactory; import org.briarproject.api.identity.IdentityManager; public class IdentityModule extends AbstractModule { @Override protected void configure() { + bind(AuthorFactory.class).to( + org.briarproject.identity.AuthorFactoryImpl.class); bind(IdentityManager.class).to(IdentityManagerImpl.class); } + + @Provides + ObjectReader getAuthorReader(AuthorFactory authorFactory) { + return new org.briarproject.identity.AuthorReader(authorFactory); + } } diff --git a/briar-core/src/org/briarproject/sync/SyncModule.java b/briar-core/src/org/briarproject/sync/SyncModule.java index 7a61c68a4..83c8f86ec 100644 --- a/briar-core/src/org/briarproject/sync/SyncModule.java +++ b/briar-core/src/org/briarproject/sync/SyncModule.java @@ -3,10 +3,7 @@ package org.briarproject.sync; import com.google.inject.AbstractModule; import com.google.inject.Provides; -import org.briarproject.api.data.ObjectReader; import org.briarproject.api.event.EventBus; -import org.briarproject.api.identity.Author; -import org.briarproject.api.identity.AuthorFactory; import org.briarproject.api.lifecycle.LifecycleManager; import org.briarproject.api.sync.GroupFactory; import org.briarproject.api.sync.MessageFactory; @@ -22,7 +19,6 @@ public class SyncModule extends AbstractModule { @Override protected void configure() { - bind(AuthorFactory.class).to(AuthorFactoryImpl.class); bind(GroupFactory.class).to(GroupFactoryImpl.class); bind(MessageFactory.class).to(MessageFactoryImpl.class); bind(PacketReaderFactory.class).to(PacketReaderFactoryImpl.class); @@ -32,11 +28,6 @@ public class SyncModule extends AbstractModule { SyncSessionFactoryImpl.class).in(Singleton.class); } - @Provides - ObjectReader getAuthorReader(AuthorFactory authorFactory) { - return new AuthorReader(authorFactory); - } - @Provides @Singleton ValidationManager getValidationManager(LifecycleManager lifecycleManager, EventBus eventBus, ValidationManagerImpl validationManager) { From 3fa1bbe33e947a5f2bf8c79349df3d3311d3b118 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Mon, 8 Feb 2016 15:18:05 +0000 Subject: [PATCH 04/11] Expose transactions through DatabaseComponent interface. --- .../api/db/DatabaseComponent.java | 15 +++++++++++++++ .../org/briarproject/api/db/Transaction.java | 15 +++++++++++++++ .../db/DatabaseComponentImpl.java | 19 +++++++++++++++++-- .../org/briarproject/db/DatabaseModule.java | 3 ++- .../db/DatabaseComponentImplTest.java | 5 +++-- 5 files changed, 52 insertions(+), 5 deletions(-) create mode 100644 briar-api/src/org/briarproject/api/db/Transaction.java diff --git a/briar-api/src/org/briarproject/api/db/DatabaseComponent.java b/briar-api/src/org/briarproject/api/db/DatabaseComponent.java index 5609844f4..06800e1c4 100644 --- a/briar-api/src/org/briarproject/api/db/DatabaseComponent.java +++ b/briar-api/src/org/briarproject/api/db/DatabaseComponent.java @@ -39,6 +39,21 @@ public interface DatabaseComponent { /** 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. */ + Transaction startTransaction() throws DbException; + + /** + * Aborts the given transaction - no changes made during the transaction + * will be applied to the database. + */ + 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; + /** * Stores a contact associated with the given local and remote pseudonyms, * and returns an ID for the contact. diff --git a/briar-api/src/org/briarproject/api/db/Transaction.java b/briar-api/src/org/briarproject/api/db/Transaction.java new file mode 100644 index 000000000..2ea803b05 --- /dev/null +++ b/briar-api/src/org/briarproject/api/db/Transaction.java @@ -0,0 +1,15 @@ +package org.briarproject.api.db; + +/** A wrapper around a database transaction. */ +public class Transaction { + + private final Object txn; + + public Transaction(Object txn) { + this.txn = txn; + } + + public Object unbox() { + return txn; + } +} diff --git a/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java b/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java index dcf88660a..b62f18090 100644 --- a/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java +++ b/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java @@ -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; @@ -77,6 +78,7 @@ class DatabaseComponentImpl implements DatabaseComponent { Logger.getLogger(DatabaseComponentImpl.class.getName()); private final Database db; + private final Class txnClass; private final EventBus eventBus; private final ShutdownManager shutdown; private final AtomicBoolean closed = new AtomicBoolean(false); @@ -84,9 +86,10 @@ class DatabaseComponentImpl implements DatabaseComponent { private volatile int shutdownHandle = -1; @Inject - DatabaseComponentImpl(Database db, EventBus eventBus, + DatabaseComponentImpl(Database db, Class txnClass, EventBus eventBus, ShutdownManager shutdown) { this.db = db; + this.txnClass = txnClass; this.eventBus = eventBus; this.shutdown = shutdown; } @@ -116,6 +119,18 @@ class DatabaseComponentImpl implements DatabaseComponent { db.close(); } + public Transaction startTransaction() throws DbException { + return new Transaction(db.startTransaction()); + } + + public void abortTransaction(Transaction txn) { + db.abortTransaction(txnClass.cast(txn.unbox())); + } + + public void commitTransaction(Transaction txn) throws DbException { + db.commitTransaction(txnClass.cast(txn.unbox())); + } + public ContactId addContact(Author remote, AuthorId local) throws DbException { T txn = db.startTransaction(); @@ -975,7 +990,7 @@ class DatabaseComponentImpl implements DatabaseComponent { public void setVisibleToContact(ContactId c, GroupId g, boolean visible) throws DbException { - boolean wasVisible = false; + boolean wasVisible; T txn = db.startTransaction(); try { if (!db.containsContact(txn, c)) diff --git a/briar-core/src/org/briarproject/db/DatabaseModule.java b/briar-core/src/org/briarproject/db/DatabaseModule.java index 9ac531c07..6a050ed44 100644 --- a/briar-core/src/org/briarproject/db/DatabaseModule.java +++ b/briar-core/src/org/briarproject/db/DatabaseModule.java @@ -51,7 +51,8 @@ public class DatabaseModule extends AbstractModule { @Provides @Singleton DatabaseComponent getDatabaseComponent(Database db, EventBus eventBus, ShutdownManager shutdown) { - return new DatabaseComponentImpl(db, eventBus, shutdown); + return new DatabaseComponentImpl(db, Connection.class, + eventBus, shutdown); } @Provides @Singleton @DatabaseExecutor diff --git a/briar-tests/src/org/briarproject/db/DatabaseComponentImplTest.java b/briar-tests/src/org/briarproject/db/DatabaseComponentImplTest.java index 70a9113f2..178dee5b6 100644 --- a/briar-tests/src/org/briarproject/db/DatabaseComponentImplTest.java +++ b/briar-tests/src/org/briarproject/db/DatabaseComponentImplTest.java @@ -106,9 +106,10 @@ public class DatabaseComponentImplTest extends BriarTestCase { StorageStatus.ACTIVE); } - private DatabaseComponent createDatabaseComponent(Database database, + private DatabaseComponent createDatabaseComponent(Database database, EventBus eventBus, ShutdownManager shutdown) { - return new DatabaseComponentImpl(database, eventBus, shutdown); + return new DatabaseComponentImpl(database, Object.class, + eventBus, shutdown); } @Test From ef2b2b9710b089e0c29b9b0f4628dac23c86f75a Mon Sep 17 00:00:00 2001 From: akwizgran Date: Mon, 8 Feb 2016 15:32:13 +0000 Subject: [PATCH 05/11] Removed unneeded visibility methods. --- .../api/db/DatabaseComponent.java | 10 ---- .../db/DatabaseComponentImpl.java | 46 ------------------- .../forum/ForumSharingManagerImpl.java | 2 +- .../messaging/MessagingManagerImpl.java | 3 +- .../TransportPropertyManagerImpl.java | 2 +- .../db/DatabaseComponentImplTest.java | 46 ++++++------------- 6 files changed, 17 insertions(+), 92 deletions(-) diff --git a/briar-api/src/org/briarproject/api/db/DatabaseComponent.java b/briar-api/src/org/briarproject/api/db/DatabaseComponent.java index 06800e1c4..435d1025b 100644 --- a/briar-api/src/org/briarproject/api/db/DatabaseComponent.java +++ b/briar-api/src/org/briarproject/api/db/DatabaseComponent.java @@ -193,9 +193,6 @@ public interface DatabaseComponent { /** Returns the maximum latencies in milliseconds of all transports. */ Map getTransportLatencies() throws DbException; - /** Returns the IDs of all contacts to which the given group is visible. */ - Collection getVisibility(GroupId g) throws DbException; - /** * Increments the outgoing stream counter for the given contact and * transport in the given rotation period . @@ -271,13 +268,6 @@ public interface DatabaseComponent { void setReorderingWindow(ContactId c, TransportId t, long rotationPeriod, long base, byte[] bitmap) throws DbException; - /** - * Makes a group visible to the given set of contacts and invisible to any - * other contacts. - */ - void setVisibility(GroupId g, Collection visible) - throws DbException; - /** Makes a group visible or invisible to a contact. */ void setVisibleToContact(ContactId c, GroupId g, boolean visible) throws DbException; diff --git a/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java b/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java index b62f18090..9e46dabb9 100644 --- a/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java +++ b/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java @@ -638,20 +638,6 @@ class DatabaseComponentImpl implements DatabaseComponent { } } - public Collection getVisibility(GroupId g) throws DbException { - T txn = db.startTransaction(); - try { - if (!db.containsGroup(txn, g)) - throw new NoSuchGroupException(); - Collection visible = db.getVisibility(txn, g); - db.commitTransaction(txn); - return visible; - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } - } - public void incrementStreamCounter(ContactId c, TransportId t, long rotationPeriod) throws DbException { T txn = db.startTransaction(); @@ -956,38 +942,6 @@ class DatabaseComponentImpl implements DatabaseComponent { } } - public void setVisibility(GroupId g, Collection visible) - throws DbException { - Collection affected = new ArrayList(); - T txn = db.startTransaction(); - try { - if (!db.containsGroup(txn, g)) - throw new NoSuchGroupException(); - // Use HashSets for O(1) lookups, O(n) overall running time - Collection now = new HashSet(visible); - Collection before = db.getVisibility(txn, g); - before = new HashSet(before); - // Set the group's visibility for each current contact - for (ContactId c : db.getContactIds(txn)) { - boolean wasBefore = before.contains(c); - boolean isNow = now.contains(c); - if (!wasBefore && isNow) { - db.addVisibility(txn, c, g); - affected.add(c); - } else if (wasBefore && !isNow) { - db.removeVisibility(txn, c, g); - affected.add(c); - } - } - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } - if (!affected.isEmpty()) - eventBus.broadcast(new GroupVisibilityUpdatedEvent(affected)); - } - public void setVisibleToContact(ContactId c, GroupId g, boolean visible) throws DbException { boolean wasVisible; diff --git a/briar-core/src/org/briarproject/forum/ForumSharingManagerImpl.java b/briar-core/src/org/briarproject/forum/ForumSharingManagerImpl.java index abfb7f6c9..72efd00ac 100644 --- a/briar-core/src/org/briarproject/forum/ForumSharingManagerImpl.java +++ b/briar-core/src/org/briarproject/forum/ForumSharingManagerImpl.java @@ -113,7 +113,7 @@ class ForumSharingManagerImpl implements ForumSharingManager, AddContactHook, Group g = getContactGroup(c); // Store the group and share it with the contact db.addGroup(g); - db.setVisibility(g.getId(), Collections.singletonList(c.getId())); + db.setVisibleToContact(c.getId(), g.getId(), true); // Attach the contact ID to the group BdfDictionary d = new BdfDictionary(); d.put("contactId", c.getId().getInt()); diff --git a/briar-core/src/org/briarproject/messaging/MessagingManagerImpl.java b/briar-core/src/org/briarproject/messaging/MessagingManagerImpl.java index a5514fc04..5185cac6e 100644 --- a/briar-core/src/org/briarproject/messaging/MessagingManagerImpl.java +++ b/briar-core/src/org/briarproject/messaging/MessagingManagerImpl.java @@ -32,7 +32,6 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.Map; import java.util.logging.Logger; @@ -77,7 +76,7 @@ class MessagingManagerImpl implements MessagingManager, AddContactHook, Group g = getContactGroup(c); // Store the group and share it with the contact db.addGroup(g); - db.setVisibility(g.getId(), Collections.singletonList(c.getId())); + db.setVisibleToContact(c.getId(), g.getId(), true); // Attach the contact ID to the group BdfDictionary d = new BdfDictionary(); d.put("contactId", c.getId().getInt()); diff --git a/briar-core/src/org/briarproject/properties/TransportPropertyManagerImpl.java b/briar-core/src/org/briarproject/properties/TransportPropertyManagerImpl.java index e972093d7..a7babd5cd 100644 --- a/briar-core/src/org/briarproject/properties/TransportPropertyManagerImpl.java +++ b/briar-core/src/org/briarproject/properties/TransportPropertyManagerImpl.java @@ -102,7 +102,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager, Group g = getContactGroup(c); // Store the group and share it with the contact db.addGroup(g); - db.setVisibility(g.getId(), Collections.singletonList(c.getId())); + db.setVisibleToContact(c.getId(), g.getId(), true); // Copy the latest local properties into the group DeviceId dev = db.getDeviceId(); Map local = getLocalProperties(); diff --git a/briar-tests/src/org/briarproject/db/DatabaseComponentImplTest.java b/briar-tests/src/org/briarproject/db/DatabaseComponentImplTest.java index 178dee5b6..0743df949 100644 --- a/briar-tests/src/org/briarproject/db/DatabaseComponentImplTest.java +++ b/briar-tests/src/org/briarproject/db/DatabaseComponentImplTest.java @@ -455,11 +455,11 @@ public class DatabaseComponentImplTest extends BriarTestCase { final EventBus eventBus = context.mock(EventBus.class); context.checking(new Expectations() {{ // Check whether the group is in the DB (which it's not) - exactly(9).of(database).startTransaction(); + exactly(7).of(database).startTransaction(); will(returnValue(txn)); - exactly(9).of(database).containsGroup(txn, groupId); + exactly(7).of(database).containsGroup(txn, groupId); will(returnValue(false)); - exactly(9).of(database).abortTransaction(txn); + exactly(7).of(database).abortTransaction(txn); // This is needed for getMessageStatus(), isVisibleToContact(), and // setVisibleToContact() to proceed exactly(3).of(database).containsContact(txn, contactId); @@ -489,13 +489,6 @@ public class DatabaseComponentImplTest extends BriarTestCase { // Expected } - try { - db.getVisibility(groupId); - fail(); - } catch (NoSuchGroupException expected) { - // Expected - } - try { db.isVisibleToContact(contactId, groupId); fail(); @@ -517,13 +510,6 @@ public class DatabaseComponentImplTest extends BriarTestCase { // Expected } - try { - db.setVisibility(groupId, Collections.emptyList()); - fail(); - } catch (NoSuchGroupException expected) { - // Expected - } - try { db.setVisibleToContact(contactId, groupId, true); fail(); @@ -1048,8 +1034,6 @@ public class DatabaseComponentImplTest extends BriarTestCase { @Test public void testChangingVisibilityCallsListeners() throws Exception { - final ContactId contactId1 = new ContactId(123); - final Collection both = Arrays.asList(contactId, contactId1); Mockery context = new Mockery(); @SuppressWarnings("unchecked") final Database database = context.mock(Database.class); @@ -1058,13 +1042,13 @@ public class DatabaseComponentImplTest extends BriarTestCase { context.checking(new Expectations() {{ oneOf(database).startTransaction(); will(returnValue(txn)); + oneOf(database).containsContact(txn, contactId); + will(returnValue(true)); oneOf(database).containsGroup(txn, groupId); will(returnValue(true)); - oneOf(database).getVisibility(txn, groupId); - will(returnValue(both)); - oneOf(database).getContactIds(txn); - will(returnValue(both)); - oneOf(database).removeVisibility(txn, contactId1, groupId); + oneOf(database).containsVisibleGroup(txn, contactId, groupId); + will(returnValue(false)); // Not yet visible + oneOf(database).addVisibility(txn, contactId, groupId); oneOf(database).commitTransaction(txn); oneOf(eventBus).broadcast(with(any( GroupVisibilityUpdatedEvent.class))); @@ -1072,7 +1056,7 @@ public class DatabaseComponentImplTest extends BriarTestCase { DatabaseComponent db = createDatabaseComponent(database, eventBus, shutdown); - db.setVisibility(groupId, Collections.singletonList(contactId)); + db.setVisibleToContact(contactId, groupId, true); context.assertIsSatisfied(); } @@ -1080,8 +1064,6 @@ public class DatabaseComponentImplTest extends BriarTestCase { @Test public void testNotChangingVisibilityDoesNotCallListeners() throws Exception { - final ContactId contactId1 = new ContactId(123); - final Collection both = Arrays.asList(contactId, contactId1); Mockery context = new Mockery(); @SuppressWarnings("unchecked") final Database database = context.mock(Database.class); @@ -1090,18 +1072,18 @@ public class DatabaseComponentImplTest extends BriarTestCase { context.checking(new Expectations() {{ oneOf(database).startTransaction(); will(returnValue(txn)); + oneOf(database).containsContact(txn, contactId); + will(returnValue(true)); oneOf(database).containsGroup(txn, groupId); will(returnValue(true)); - oneOf(database).getVisibility(txn, groupId); - will(returnValue(both)); - oneOf(database).getContactIds(txn); - will(returnValue(both)); + oneOf(database).containsVisibleGroup(txn, contactId, groupId); + will(returnValue(true)); // Already visible oneOf(database).commitTransaction(txn); }}); DatabaseComponent db = createDatabaseComponent(database, eventBus, shutdown); - db.setVisibility(groupId, both); + db.setVisibleToContact(contactId, groupId, true); context.assertIsSatisfied(); } From de8cc50fb43d2de313078ad8e7eb1ecff9b956b9 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Thu, 11 Feb 2016 13:35:46 +0000 Subject: [PATCH 06/11] Moved transactions out of database component. --- .../api/contact/ContactManager.java | 5 +- .../api/db/DatabaseComponent.java | 304 +++-- .../org/briarproject/api/db/Transaction.java | 25 +- .../api/identity/IdentityManager.java | 6 +- .../api/sync/ValidationManager.java | 5 +- .../contact/ContactManagerImpl.java | 100 +- .../db/DatabaseComponentImpl.java | 1067 ++++++----------- .../briarproject/forum/ForumManagerImpl.java | 155 +-- .../forum/ForumSharingManagerImpl.java | 330 +++-- .../identity/IdentityManagerImpl.java | 82 +- .../messaging/MessagingManagerImpl.java | 108 +- .../plugins/PluginManagerImpl.java | 25 +- .../TransportPropertyManagerImpl.java | 295 ++--- .../settings/SettingsManagerImpl.java | 18 +- .../sync/DuplexOutgoingSession.java | 45 +- .../briarproject/sync/IncomingSession.java | 37 +- .../sync/SimplexOutgoingSession.java | 25 +- .../sync/ValidationManagerImpl.java | 49 +- .../transport/KeyManagerImpl.java | 10 +- .../transport/TransportKeyManager.java | 55 +- .../db/DatabaseComponentImplTest.java | 441 +++++-- .../plugins/PluginManagerImplTest.java | 26 +- .../sync/SimplexMessagingIntegrationTest.java | 17 +- .../sync/SimplexOutgoingSessionTest.java | 49 +- 24 files changed, 1828 insertions(+), 1451 deletions(-) diff --git a/briar-api/src/org/briarproject/api/contact/ContactManager.java b/briar-api/src/org/briarproject/api/contact/ContactManager.java index 3b311d66d..ffc493477 100644 --- a/briar-api/src/org/briarproject/api/contact/ContactManager.java +++ b/briar-api/src/org/briarproject/api/contact/ContactManager.java @@ -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; } } diff --git a/briar-api/src/org/briarproject/api/db/DatabaseComponent.java b/briar-api/src/org/briarproject/api/db/DatabaseComponent.java index 435d1025b..d38dc08d6 100644 --- a/briar-api/src/org/briarproject/api/db/DatabaseComponent.java +++ b/briar-api/src/org/briarproject/api/db/DatabaseComponent.java @@ -26,72 +26,85 @@ import java.util.Map; /** * Encapsulates the database implementation and exposes high-level operations * to other components. - *

+ *

* 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 generateBatch(ContactId c, int maxLength, - int maxLatency) throws DbException; + Collection 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 generateRequestedBatch(ContactId c, int maxLength, - int maxLatency) throws DbException; + Collection 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 getContacts() throws DbException; + /** + * Returns all contacts. + */ + Collection getContacts(Transaction txn) throws DbException; - /** Returns all contacts associated with the given local pseudonym. */ - Collection getContacts(AuthorId a) throws DbException; + /** + * Returns all contacts associated with the given local pseudonym. + */ + Collection 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 getGroups(ClientId c) throws DbException; + /** + * Returns all groups belonging to the given client. + */ + Collection 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 getLocalAuthors() throws DbException; + /** + * Returns all local pseudonyms. + */ + Collection getLocalAuthors(Transaction txn) throws DbException; /** * Returns the IDs of any messages that need to be validated by the given * client. */ - Collection 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 getMessageMetadata(GroupId g) + Collection 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 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 getMessageStatus(ContactId c, GroupId g) - throws DbException; + Collection 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 getTransportKeys(TransportId t) + /** + * Returns all transport keys for the given transport. + */ + Map getTransportKeys(Transaction txn, + TransportId t) throws DbException; + + /** + * Returns the maximum latencies in milliseconds of all transports. + */ + Map getTransportLatencies(Transaction txn) throws DbException; - /** Returns the maximum latencies in milliseconds of all transports. */ - Map 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 keys) - throws DbException; + void updateTransportKeys(Transaction txn, + Map keys) throws DbException; } diff --git a/briar-api/src/org/briarproject/api/db/Transaction.java b/briar-api/src/org/briarproject/api/db/Transaction.java index 2ea803b05..46497a039 100644 --- a/briar-api/src/org/briarproject/api/db/Transaction.java +++ b/briar-api/src/org/briarproject/api/db/Transaction.java @@ -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; + } } diff --git a/briar-api/src/org/briarproject/api/identity/IdentityManager.java b/briar-api/src/org/briarproject/api/identity/IdentityManager.java index e872d845e..f2a4db2d9 100644 --- a/briar-api/src/org/briarproject/api/identity/IdentityManager.java +++ b/briar-api/src/org/briarproject/api/identity/IdentityManager.java @@ -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; } } diff --git a/briar-api/src/org/briarproject/api/sync/ValidationManager.java b/briar-api/src/org/briarproject/api/sync/ValidationManager.java index ff1e854c3..690bc54e2 100644 --- a/briar-api/src/org/briarproject/api/sync/ValidationManager.java +++ b/briar-api/src/org/briarproject/api/sync/ValidationManager.java @@ -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; } } diff --git a/briar-core/src/org/briarproject/contact/ContactManagerImpl.java b/briar-core/src/org/briarproject/contact/ContactManagerImpl.java index b8f9553c8..7bdb3bc45 100644 --- a/briar-core/src/org/briarproject/contact/ContactManagerImpl.java +++ b/briar-core/src/org/briarproject/contact/ContactManagerImpl.java @@ -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 added = new ArrayList(); + List removed = new ArrayList(); + 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 getContacts() throws DbException { - Collection contacts = db.getContacts(); + Collection 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 active = new ArrayList(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); } } diff --git a/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java b/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java index 9e46dabb9..8d4c71166 100644 --- a/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java +++ b/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java @@ -66,6 +66,8 @@ import static org.briarproject.api.sync.ValidationManager.Validity.UNKNOWN; import static org.briarproject.api.sync.ValidationManager.Validity.VALID; import static org.briarproject.db.DatabaseConstants.MAX_OFFERED_MESSAGES; +// TODO: Callers should broadcast events after committing transactions + /** * An implementation of DatabaseComponent using reentrant read-write locks. * Depending on the JVM's lock implementation, this implementation may allow @@ -123,76 +125,55 @@ class DatabaseComponentImpl implements DatabaseComponent { return new Transaction(db.startTransaction()); } - public void abortTransaction(Transaction txn) { - db.abortTransaction(txnClass.cast(txn.unbox())); + public void endTransaction(Transaction transaction) throws DbException { + T txn = txnClass.cast(transaction.unbox()); + if (transaction.isComplete()) db.commitTransaction(txn); + else db.abortTransaction(txn); } - public void commitTransaction(Transaction txn) throws DbException { - db.commitTransaction(txnClass.cast(txn.unbox())); + private T unbox(Transaction transaction) { + if (transaction.isComplete()) throw new IllegalStateException(); + return txnClass.cast(transaction.unbox()); } - public ContactId addContact(Author remote, AuthorId local) - throws DbException { - T txn = db.startTransaction(); - try { - if (!db.containsLocalAuthor(txn, local)) - throw new NoSuchLocalAuthorException(); - if (db.containsContact(txn, remote.getId(), local)) - throw new ContactExistsException(); - ContactId c = db.addContact(txn, remote, local); - db.commitTransaction(txn); - return c; - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } + public ContactId addContact(Transaction transaction, Author remote, + AuthorId local) throws DbException { + T txn = unbox(transaction); + if (!db.containsLocalAuthor(txn, local)) + throw new NoSuchLocalAuthorException(); + if (db.containsContact(txn, remote.getId(), local)) + throw new ContactExistsException(); + return db.addContact(txn, remote, local); } - public void addGroup(Group g) throws DbException { + public void addGroup(Transaction transaction, Group g) throws DbException { boolean added = false; - T txn = db.startTransaction(); - try { - if (!db.containsGroup(txn, g.getId())) { - db.addGroup(txn, g); - added = true; - } - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; + T txn = unbox(transaction); + if (!db.containsGroup(txn, g.getId())) { + db.addGroup(txn, g); + added = true; } if (added) eventBus.broadcast(new GroupAddedEvent(g)); } - public void addLocalAuthor(LocalAuthor a) throws DbException { - T txn = db.startTransaction(); - try { - if (!db.containsLocalAuthor(txn, a.getId())) - db.addLocalAuthor(txn, a); - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } + public void addLocalAuthor(Transaction transaction, LocalAuthor a) + throws DbException { + T txn = unbox(transaction); + if (!db.containsLocalAuthor(txn, a.getId())) + db.addLocalAuthor(txn, a); } - public void addLocalMessage(Message m, ClientId c, Metadata meta, - boolean shared) throws DbException { + public void addLocalMessage(Transaction transaction, Message m, ClientId c, + Metadata meta, boolean shared) throws DbException { boolean added = false; - T txn = db.startTransaction(); - try { - if (!db.containsGroup(txn, m.getGroupId())) - throw new NoSuchGroupException(); - if (!db.containsMessage(txn, m.getId())) { - addMessage(txn, m, VALID, shared, null); - added = true; - } - db.mergeMessageMetadata(txn, m.getId(), meta); - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; + T txn = unbox(transaction); + if (!db.containsGroup(txn, m.getGroupId())) + throw new NoSuchGroupException(); + if (!db.containsMessage(txn, m.getId())) { + addMessage(txn, m, VALID, shared, null); + added = true; } + db.mergeMessageMetadata(txn, m.getId(), meta); if (added) { eventBus.broadcast(new MessageAddedEvent(m, null)); eventBus.broadcast(new MessageValidatedEvent(m, c, true, true)); @@ -223,538 +204,327 @@ class DatabaseComponentImpl implements DatabaseComponent { } } - public void addTransport(TransportId t, int maxLatency) throws DbException { + public void addTransport(Transaction transaction, TransportId t, + int maxLatency) throws DbException { boolean added = false; - T txn = db.startTransaction(); - try { - if (!db.containsTransport(txn, t)) { - db.addTransport(txn, t, maxLatency); - added = true; - } - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; + T txn = unbox(transaction); + if (!db.containsTransport(txn, t)) { + db.addTransport(txn, t, maxLatency); + added = true; } if (added) eventBus.broadcast(new TransportAddedEvent(t, maxLatency)); } - public void addTransportKeys(ContactId c, TransportKeys k) + public void addTransportKeys(Transaction transaction, ContactId c, + TransportKeys k) throws DbException { + T txn = unbox(transaction); + if (!db.containsContact(txn, c)) + throw new NoSuchContactException(); + if (!db.containsTransport(txn, k.getTransportId())) + throw new NoSuchTransportException(); + db.addTransportKeys(txn, c, k); + } + + public void deleteMessage(Transaction transaction, MessageId m) throws DbException { - T txn = db.startTransaction(); - try { - if (!db.containsContact(txn, c)) - throw new NoSuchContactException(); - if (!db.containsTransport(txn, k.getTransportId())) - throw new NoSuchTransportException(); - db.addTransportKeys(txn, c, k); - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } + T txn = unbox(transaction); + if (!db.containsMessage(txn, m)) + throw new NoSuchMessageException(); + db.deleteMessage(txn, m); } - public void deleteMessage(MessageId m) throws DbException { - lock.writeLock().lock(); - try { - T txn = db.startTransaction(); - try { - if (!db.containsMessage(txn, m)) - throw new NoSuchMessageException(); - db.deleteMessage(txn, m); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } - } finally { - lock.writeLock().unlock(); - } + public void deleteMessageMetadata(Transaction transaction, MessageId m) + throws DbException { + T txn = unbox(transaction); + if (!db.containsMessage(txn, m)) + throw new NoSuchMessageException(); + db.deleteMessageMetadata(txn, m); } - public void deleteMessageMetadata(MessageId m) throws DbException { - lock.writeLock().lock(); - try { - T txn = db.startTransaction(); - try { - if (!db.containsMessage(txn, m)) - throw new NoSuchMessageException(); - db.deleteMessageMetadata(txn, m); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } - } finally { - lock.writeLock().unlock(); - } - } - - public Ack generateAck(ContactId c, int maxMessages) throws DbException { + public Ack generateAck(Transaction transaction, ContactId c, + int maxMessages) throws DbException { Collection ids; - T txn = db.startTransaction(); - try { - if (!db.containsContact(txn, c)) - throw new NoSuchContactException(); - ids = db.getMessagesToAck(txn, c, maxMessages); - if (!ids.isEmpty()) db.lowerAckFlag(txn, c, ids); - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } + T txn = unbox(transaction); + if (!db.containsContact(txn, c)) + throw new NoSuchContactException(); + ids = db.getMessagesToAck(txn, c, maxMessages); + if (!ids.isEmpty()) db.lowerAckFlag(txn, c, ids); if (ids.isEmpty()) return null; return new Ack(ids); } - public Collection generateBatch(ContactId c, int maxLength, - int maxLatency) throws DbException { + public Collection generateBatch(Transaction transaction, + ContactId c, int maxLength, int maxLatency) throws DbException { Collection ids; - List messages = new ArrayList(); - T txn = db.startTransaction(); - try { - if (!db.containsContact(txn, c)) - throw new NoSuchContactException(); - ids = db.getMessagesToSend(txn, c, maxLength); - for (MessageId m : ids) { - messages.add(db.getRawMessage(txn, m)); - db.updateExpiryTime(txn, c, m, maxLatency); - } - if (!ids.isEmpty()) db.lowerRequestedFlag(txn, c, ids); - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; + List messages; + T txn = unbox(transaction); + if (!db.containsContact(txn, c)) + throw new NoSuchContactException(); + ids = db.getMessagesToSend(txn, c, maxLength); + messages = new ArrayList(ids.size()); + for (MessageId m : ids) { + messages.add(db.getRawMessage(txn, m)); + db.updateExpiryTime(txn, c, m, maxLatency); } + if (!ids.isEmpty()) db.lowerRequestedFlag(txn, c, ids); if (messages.isEmpty()) return null; if (!ids.isEmpty()) eventBus.broadcast(new MessagesSentEvent(c, ids)); return Collections.unmodifiableList(messages); } - public Offer generateOffer(ContactId c, int maxMessages, int maxLatency) - throws DbException { + public Offer generateOffer(Transaction transaction, ContactId c, + int maxMessages, int maxLatency) throws DbException { Collection ids; - T txn = db.startTransaction(); - try { - if (!db.containsContact(txn, c)) - throw new NoSuchContactException(); - ids = db.getMessagesToOffer(txn, c, maxMessages); - for (MessageId m : ids) db.updateExpiryTime(txn, c, m, maxLatency); - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } + T txn = unbox(transaction); + if (!db.containsContact(txn, c)) + throw new NoSuchContactException(); + ids = db.getMessagesToOffer(txn, c, maxMessages); + for (MessageId m : ids) db.updateExpiryTime(txn, c, m, maxLatency); if (ids.isEmpty()) return null; return new Offer(ids); } - public Request generateRequest(ContactId c, int maxMessages) - throws DbException { + public Request generateRequest(Transaction transaction, ContactId c, + int maxMessages) throws DbException { Collection ids; - T txn = db.startTransaction(); - try { - if (!db.containsContact(txn, c)) - throw new NoSuchContactException(); - ids = db.getMessagesToRequest(txn, c, maxMessages); - if (!ids.isEmpty()) db.removeOfferedMessages(txn, c, ids); - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } + T txn = unbox(transaction); + if (!db.containsContact(txn, c)) + throw new NoSuchContactException(); + ids = db.getMessagesToRequest(txn, c, maxMessages); + if (!ids.isEmpty()) db.removeOfferedMessages(txn, c, ids); if (ids.isEmpty()) return null; return new Request(ids); } - public Collection generateRequestedBatch(ContactId c, int maxLength, - int maxLatency) throws DbException { + public Collection generateRequestedBatch(Transaction transaction, + ContactId c, int maxLength, int maxLatency) throws DbException { Collection ids; - List messages = new ArrayList(); - T txn = db.startTransaction(); - try { - if (!db.containsContact(txn, c)) - throw new NoSuchContactException(); - ids = db.getRequestedMessagesToSend(txn, c, maxLength); - for (MessageId m : ids) { - messages.add(db.getRawMessage(txn, m)); - db.updateExpiryTime(txn, c, m, maxLatency); - } - if (!ids.isEmpty()) db.lowerRequestedFlag(txn, c, ids); - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; + List messages; + T txn = unbox(transaction); + if (!db.containsContact(txn, c)) + throw new NoSuchContactException(); + ids = db.getRequestedMessagesToSend(txn, c, maxLength); + messages = new ArrayList(ids.size()); + for (MessageId m : ids) { + messages.add(db.getRawMessage(txn, m)); + db.updateExpiryTime(txn, c, m, maxLatency); } + if (!ids.isEmpty()) db.lowerRequestedFlag(txn, c, ids); if (messages.isEmpty()) return null; if (!ids.isEmpty()) eventBus.broadcast(new MessagesSentEvent(c, ids)); return Collections.unmodifiableList(messages); } - public Contact getContact(ContactId c) throws DbException { - T txn = db.startTransaction(); - try { - if (!db.containsContact(txn, c)) - throw new NoSuchContactException(); - Contact contact = db.getContact(txn, c); - db.commitTransaction(txn); - return contact; - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } - } - - public Collection getContacts() throws DbException { - T txn = db.startTransaction(); - try { - Collection contacts = db.getContacts(txn); - db.commitTransaction(txn); - return contacts; - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } - } - - public Collection getContacts(AuthorId a) throws DbException { - T txn = db.startTransaction(); - try { - if (!db.containsLocalAuthor(txn, a)) - throw new NoSuchLocalAuthorException(); - Collection contacts = db.getContacts(txn, a); - db.commitTransaction(txn); - return contacts; - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } - } - - public DeviceId getDeviceId() throws DbException { - T txn = db.startTransaction(); - try { - DeviceId id = db.getDeviceId(txn); - db.commitTransaction(txn); - return id; - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } - } - - public Group getGroup(GroupId g) throws DbException { - T txn = db.startTransaction(); - try { - if (!db.containsGroup(txn, g)) - throw new NoSuchGroupException(); - Group group = db.getGroup(txn, g); - db.commitTransaction(txn); - return group; - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } - } - - public Metadata getGroupMetadata(GroupId g) throws DbException { - T txn = db.startTransaction(); - try { - if (!db.containsGroup(txn, g)) - throw new NoSuchGroupException(); - Metadata metadata = db.getGroupMetadata(txn, g); - db.commitTransaction(txn); - return metadata; - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } - } - - public Collection getGroups(ClientId c) throws DbException { - T txn = db.startTransaction(); - try { - Collection groups = db.getGroups(txn, c); - db.commitTransaction(txn); - return groups; - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } - } - - public LocalAuthor getLocalAuthor(AuthorId a) throws DbException { - T txn = db.startTransaction(); - try { - if (!db.containsLocalAuthor(txn, a)) - throw new NoSuchLocalAuthorException(); - LocalAuthor localAuthor = db.getLocalAuthor(txn, a); - db.commitTransaction(txn); - return localAuthor; - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } - } - - public Collection getLocalAuthors() throws DbException { - T txn = db.startTransaction(); - try { - Collection authors = db.getLocalAuthors(txn); - db.commitTransaction(txn); - return authors; - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } - } - - public Collection getMessagesToValidate(ClientId c) + public Contact getContact(Transaction transaction, ContactId c) throws DbException { - T txn = db.startTransaction(); - try { - Collection ids = db.getMessagesToValidate(txn, c); - db.commitTransaction(txn); - return ids; - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } + T txn = unbox(transaction); + if (!db.containsContact(txn, c)) + throw new NoSuchContactException(); + return db.getContact(txn, c); } - public byte[] getRawMessage(MessageId m) throws DbException { - T txn = db.startTransaction(); - try { - if (!db.containsMessage(txn, m)) - throw new NoSuchMessageException(); - byte[] raw = db.getRawMessage(txn, m); - db.commitTransaction(txn); - return raw; - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } - } - - public Map getMessageMetadata(GroupId g) + public Collection getContacts(Transaction transaction) throws DbException { - T txn = db.startTransaction(); - try { - if (!db.containsGroup(txn, g)) - throw new NoSuchGroupException(); - Map metadata = db.getMessageMetadata(txn, g); - db.commitTransaction(txn); - return metadata; - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } + T txn = unbox(transaction); + return db.getContacts(txn); } - public Metadata getMessageMetadata(MessageId m) throws DbException { - T txn = db.startTransaction(); - try { - if (!db.containsMessage(txn, m)) - throw new NoSuchMessageException(); - Metadata metadata = db.getMessageMetadata(txn, m); - db.commitTransaction(txn); - return metadata; - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } + public Collection getContacts(Transaction transaction, + AuthorId a) throws DbException { + T txn = unbox(transaction); + if (!db.containsLocalAuthor(txn, a)) + throw new NoSuchLocalAuthorException(); + return db.getContacts(txn, a); } - public Collection getMessageStatus(ContactId c, GroupId g) + public DeviceId getDeviceId(Transaction transaction) throws DbException { + T txn = unbox(transaction); + return db.getDeviceId(txn); + } + + public Group getGroup(Transaction transaction, GroupId g) throws DbException { - T txn = db.startTransaction(); - try { - if (!db.containsContact(txn, c)) - throw new NoSuchContactException(); - if (!db.containsGroup(txn, g)) - throw new NoSuchGroupException(); - Collection statuses = db.getMessageStatus(txn, c, g); - db.commitTransaction(txn); - return statuses; - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } + T txn = unbox(transaction); + if (!db.containsGroup(txn, g)) + throw new NoSuchGroupException(); + return db.getGroup(txn, g); } - public MessageStatus getMessageStatus(ContactId c, MessageId m) + public Metadata getGroupMetadata(Transaction transaction, GroupId g) throws DbException { - T txn = db.startTransaction(); - try { - if (!db.containsContact(txn, c)) - throw new NoSuchContactException(); - if (!db.containsMessage(txn, m)) - throw new NoSuchMessageException(); - MessageStatus status = db.getMessageStatus(txn, c, m); - db.commitTransaction(txn); - return status; - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } + T txn = unbox(transaction); + if (!db.containsGroup(txn, g)) + throw new NoSuchGroupException(); + return db.getGroupMetadata(txn, g); } - public Settings getSettings(String namespace) throws DbException { - T txn = db.startTransaction(); - try { - Settings s = db.getSettings(txn, namespace); - db.commitTransaction(txn); - return s; - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } - } - - public Map getTransportKeys(TransportId t) + public Collection getGroups(Transaction transaction, ClientId c) throws DbException { - T txn = db.startTransaction(); - try { - if (!db.containsTransport(txn, t)) - throw new NoSuchTransportException(); - Map keys = db.getTransportKeys(txn, t); - db.commitTransaction(txn); - return keys; - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } + T txn = unbox(transaction); + return db.getGroups(txn, c); } - public Map getTransportLatencies() + public LocalAuthor getLocalAuthor(Transaction transaction, AuthorId a) throws DbException { - T txn = db.startTransaction(); - try { - Map latencies = db.getTransportLatencies(txn); - db.commitTransaction(txn); - return latencies; - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } + T txn = unbox(transaction); + if (!db.containsLocalAuthor(txn, a)) + throw new NoSuchLocalAuthorException(); + return db.getLocalAuthor(txn, a); } - public void incrementStreamCounter(ContactId c, TransportId t, - long rotationPeriod) throws DbException { - T txn = db.startTransaction(); - try { - if (!db.containsContact(txn, c)) - throw new NoSuchContactException(); - if (!db.containsTransport(txn, t)) - throw new NoSuchTransportException(); - db.incrementStreamCounter(txn, c, t, rotationPeriod); - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } - } - - public boolean isVisibleToContact(ContactId c, GroupId g) + public Collection getLocalAuthors(Transaction transaction) throws DbException { - T txn = db.startTransaction(); - try { - if (!db.containsContact(txn, c)) - throw new NoSuchContactException(); - if (!db.containsGroup(txn, g)) - throw new NoSuchGroupException(); - boolean visible = db.containsVisibleGroup(txn, c, g); - db.commitTransaction(txn); - return visible; - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } + T txn = unbox(transaction); + return db.getLocalAuthors(txn); } - public void mergeGroupMetadata(GroupId g, Metadata meta) + public Collection getMessagesToValidate(Transaction transaction, + ClientId c) throws DbException { + T txn = unbox(transaction); + return db.getMessagesToValidate(txn, c); + } + + public byte[] getRawMessage(Transaction transaction, MessageId m) throws DbException { - T txn = db.startTransaction(); - try { - if (!db.containsGroup(txn, g)) - throw new NoSuchGroupException(); - db.mergeGroupMetadata(txn, g, meta); - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } + T txn = unbox(transaction); + if (!db.containsMessage(txn, m)) + throw new NoSuchMessageException(); + return db.getRawMessage(txn, m); } - public void mergeMessageMetadata(MessageId m, Metadata meta) + public Map getMessageMetadata(Transaction transaction, + GroupId g) throws DbException { + T txn = unbox(transaction); + if (!db.containsGroup(txn, g)) + throw new NoSuchGroupException(); + return db.getMessageMetadata(txn, g); + } + + public Metadata getMessageMetadata(Transaction transaction, MessageId m) throws DbException { - T txn = db.startTransaction(); - try { - if (!db.containsMessage(txn, m)) - throw new NoSuchMessageException(); - db.mergeMessageMetadata(txn, m, meta); - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } + T txn = unbox(transaction); + if (!db.containsMessage(txn, m)) + throw new NoSuchMessageException(); + return db.getMessageMetadata(txn, m); } - public void mergeSettings(Settings s, String namespace) throws DbException { + public Collection getMessageStatus(Transaction transaction, + ContactId c, GroupId g) throws DbException { + T txn = unbox(transaction); + if (!db.containsContact(txn, c)) + throw new NoSuchContactException(); + if (!db.containsGroup(txn, g)) + throw new NoSuchGroupException(); + return db.getMessageStatus(txn, c, g); + } + + public MessageStatus getMessageStatus(Transaction transaction, ContactId c, + MessageId m) throws DbException { + T txn = unbox(transaction); + if (!db.containsContact(txn, c)) + throw new NoSuchContactException(); + if (!db.containsMessage(txn, m)) + throw new NoSuchMessageException(); + return db.getMessageStatus(txn, c, m); + } + + public Settings getSettings(Transaction transaction, String namespace) + throws DbException { + T txn = unbox(transaction); + return db.getSettings(txn, namespace); + } + + public Map getTransportKeys( + Transaction transaction, TransportId t) throws DbException { + T txn = unbox(transaction); + if (!db.containsTransport(txn, t)) + throw new NoSuchTransportException(); + return db.getTransportKeys(txn, t); + } + + public Map getTransportLatencies( + Transaction transaction) throws DbException { + T txn = unbox(transaction); + return db.getTransportLatencies(txn); + } + + public void incrementStreamCounter(Transaction transaction, ContactId c, + TransportId t, long rotationPeriod) throws DbException { + T txn = unbox(transaction); + if (!db.containsContact(txn, c)) + throw new NoSuchContactException(); + if (!db.containsTransport(txn, t)) + throw new NoSuchTransportException(); + db.incrementStreamCounter(txn, c, t, rotationPeriod); + } + + public boolean isVisibleToContact(Transaction transaction, ContactId c, + GroupId g) throws DbException { + T txn = unbox(transaction); + if (!db.containsContact(txn, c)) + throw new NoSuchContactException(); + if (!db.containsGroup(txn, g)) + throw new NoSuchGroupException(); + return db.containsVisibleGroup(txn, c, g); + } + + public void mergeGroupMetadata(Transaction transaction, GroupId g, + Metadata meta) throws DbException { + T txn = unbox(transaction); + if (!db.containsGroup(txn, g)) + throw new NoSuchGroupException(); + db.mergeGroupMetadata(txn, g, meta); + } + + public void mergeMessageMetadata(Transaction transaction, MessageId m, + Metadata meta) throws DbException { + T txn = unbox(transaction); + if (!db.containsMessage(txn, m)) + throw new NoSuchMessageException(); + db.mergeMessageMetadata(txn, m, meta); + } + + public void mergeSettings(Transaction transaction, Settings s, + String namespace) throws DbException { boolean changed = false; - T txn = db.startTransaction(); - try { - Settings old = db.getSettings(txn, namespace); - Settings merged = new Settings(); - merged.putAll(old); - merged.putAll(s); - if (!merged.equals(old)) { - db.mergeSettings(txn, s, namespace); - changed = true; - } - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; + T txn = unbox(transaction); + Settings old = db.getSettings(txn, namespace); + Settings merged = new Settings(); + merged.putAll(old); + merged.putAll(s); + if (!merged.equals(old)) { + db.mergeSettings(txn, s, namespace); + changed = true; } if (changed) eventBus.broadcast(new SettingsUpdatedEvent(namespace)); } - public void receiveAck(ContactId c, Ack a) throws DbException { + public void receiveAck(Transaction transaction, ContactId c, Ack a) + throws DbException { Collection acked = new ArrayList(); - T txn = db.startTransaction(); - try { - if (!db.containsContact(txn, c)) - throw new NoSuchContactException(); - for (MessageId m : a.getMessageIds()) { - if (db.containsVisibleMessage(txn, c, m)) { - db.raiseSeenFlag(txn, c, m); - acked.add(m); - } + T txn = unbox(transaction); + if (!db.containsContact(txn, c)) + throw new NoSuchContactException(); + for (MessageId m : a.getMessageIds()) { + if (db.containsVisibleMessage(txn, c, m)) { + db.raiseSeenFlag(txn, c, m); + acked.add(m); } - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; } eventBus.broadcast(new MessagesAckedEvent(c, acked)); } - public void receiveMessage(ContactId c, Message m) throws DbException { + public void receiveMessage(Transaction transaction, ContactId c, Message m) + throws DbException { boolean duplicate, visible; - T txn = db.startTransaction(); - try { - if (!db.containsContact(txn, c)) - throw new NoSuchContactException(); - duplicate = db.containsMessage(txn, m.getId()); - visible = db.containsVisibleGroup(txn, c, m.getGroupId()); - if (visible) { - if (!duplicate) addMessage(txn, m, UNKNOWN, false, c); - db.raiseAckFlag(txn, c, m.getId()); - } - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; + T txn = unbox(transaction); + if (!db.containsContact(txn, c)) + throw new NoSuchContactException(); + duplicate = db.containsMessage(txn, m.getId()); + visible = db.containsVisibleGroup(txn, c, m.getGroupId()); + if (visible) { + if (!duplicate) addMessage(txn, m, UNKNOWN, false, c); + db.raiseAckFlag(txn, c, m.getId()); } if (visible) { if (!duplicate) eventBus.broadcast(new MessageAddedEvent(m, c)); @@ -762,228 +532,157 @@ class DatabaseComponentImpl implements DatabaseComponent { } } - public void receiveOffer(ContactId c, Offer o) throws DbException { + public void receiveOffer(Transaction transaction, ContactId c, Offer o) + throws DbException { boolean ack = false, request = false; - T txn = db.startTransaction(); - try { - if (!db.containsContact(txn, c)) - throw new NoSuchContactException(); - int count = db.countOfferedMessages(txn, c); - for (MessageId m : o.getMessageIds()) { - if (db.containsVisibleMessage(txn, c, m)) { - db.raiseSeenFlag(txn, c, m); - db.raiseAckFlag(txn, c, m); - ack = true; - } else if (count < MAX_OFFERED_MESSAGES) { - db.addOfferedMessage(txn, c, m); - request = true; - count++; - } + T txn = unbox(transaction); + if (!db.containsContact(txn, c)) + throw new NoSuchContactException(); + int count = db.countOfferedMessages(txn, c); + for (MessageId m : o.getMessageIds()) { + if (db.containsVisibleMessage(txn, c, m)) { + db.raiseSeenFlag(txn, c, m); + db.raiseAckFlag(txn, c, m); + ack = true; + } else if (count < MAX_OFFERED_MESSAGES) { + db.addOfferedMessage(txn, c, m); + request = true; + count++; } - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; } if (ack) eventBus.broadcast(new MessageToAckEvent(c)); if (request) eventBus.broadcast(new MessageToRequestEvent(c)); } - public void receiveRequest(ContactId c, Request r) throws DbException { + public void receiveRequest(Transaction transaction, ContactId c, Request r) + throws DbException { boolean requested = false; - T txn = db.startTransaction(); - try { - if (!db.containsContact(txn, c)) - throw new NoSuchContactException(); - for (MessageId m : r.getMessageIds()) { - if (db.containsVisibleMessage(txn, c, m)) { - db.raiseRequestedFlag(txn, c, m); - db.resetExpiryTime(txn, c, m); - requested = true; - } + T txn = unbox(transaction); + if (!db.containsContact(txn, c)) + throw new NoSuchContactException(); + for (MessageId m : r.getMessageIds()) { + if (db.containsVisibleMessage(txn, c, m)) { + db.raiseRequestedFlag(txn, c, m); + db.resetExpiryTime(txn, c, m); + requested = true; } - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; } if (requested) eventBus.broadcast(new MessageRequestedEvent(c)); } - public void removeContact(ContactId c) throws DbException { - T txn = db.startTransaction(); - try { - if (!db.containsContact(txn, c)) - throw new NoSuchContactException(); - db.removeContact(txn, c); - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } + public void removeContact(Transaction transaction, ContactId c) + throws DbException { + T txn = unbox(transaction); + if (!db.containsContact(txn, c)) + throw new NoSuchContactException(); + db.removeContact(txn, c); } - public void removeGroup(Group g) throws DbException { + public void removeGroup(Transaction transaction, Group g) + throws DbException { Collection affected; - T txn = db.startTransaction(); - try { - GroupId id = g.getId(); - if (!db.containsGroup(txn, id)) - throw new NoSuchGroupException(); - affected = db.getVisibility(txn, id); - db.removeGroup(txn, id); - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } + T txn = unbox(transaction); + GroupId id = g.getId(); + if (!db.containsGroup(txn, id)) + throw new NoSuchGroupException(); + affected = db.getVisibility(txn, id); + db.removeGroup(txn, id); eventBus.broadcast(new GroupRemovedEvent(g)); eventBus.broadcast(new GroupVisibilityUpdatedEvent(affected)); } - public void removeLocalAuthor(AuthorId a) throws DbException { - T txn = db.startTransaction(); - try { - if (!db.containsLocalAuthor(txn, a)) - throw new NoSuchLocalAuthorException(); - db.removeLocalAuthor(txn, a); - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } + public void removeLocalAuthor(Transaction transaction, AuthorId a) + throws DbException { + T txn = unbox(transaction); + if (!db.containsLocalAuthor(txn, a)) + throw new NoSuchLocalAuthorException(); + db.removeLocalAuthor(txn, a); } - public void removeTransport(TransportId t) throws DbException { - T txn = db.startTransaction(); - try { - if (!db.containsTransport(txn, t)) - throw new NoSuchTransportException(); - db.removeTransport(txn, t); - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } + public void removeTransport(Transaction transaction, TransportId t) + throws DbException { + T txn = unbox(transaction); + if (!db.containsTransport(txn, t)) + throw new NoSuchTransportException(); + db.removeTransport(txn, t); eventBus.broadcast(new TransportRemovedEvent(t)); } - public void setContactStatus(ContactId c, StorageStatus s) - throws DbException { - T txn = db.startTransaction(); - try { - if (!db.containsContact(txn, c)) - throw new NoSuchContactException(); - db.setContactStatus(txn, c, s); - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } + public void setContactStatus(Transaction transaction, ContactId c, + StorageStatus s) throws DbException { + T txn = unbox(transaction); + if (!db.containsContact(txn, c)) + throw new NoSuchContactException(); + db.setContactStatus(txn, c, s); } - public void setLocalAuthorStatus(AuthorId a, StorageStatus s) - throws DbException { - T txn = db.startTransaction(); - try { - if (!db.containsLocalAuthor(txn, a)) - throw new NoSuchLocalAuthorException(); - db.setLocalAuthorStatus(txn, a, s); - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } + public void setLocalAuthorStatus(Transaction transaction, AuthorId a, + StorageStatus s) throws DbException { + T txn = unbox(transaction); + if (!db.containsLocalAuthor(txn, a)) + throw new NoSuchLocalAuthorException(); + db.setLocalAuthorStatus(txn, a, s); } - public void setMessageShared(Message m, boolean shared) - throws DbException { - T txn = db.startTransaction(); - try { - if (!db.containsMessage(txn, m.getId())) - throw new NoSuchMessageException(); - db.setMessageShared(txn, m.getId(), shared); - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } + public void setMessageShared(Transaction transaction, Message m, + boolean shared) throws DbException { + T txn = unbox(transaction); + if (!db.containsMessage(txn, m.getId())) + throw new NoSuchMessageException(); + db.setMessageShared(txn, m.getId(), shared); if (shared) eventBus.broadcast(new MessageSharedEvent(m)); } - public void setMessageValid(Message m, ClientId c, boolean valid) - throws DbException { - T txn = db.startTransaction(); - try { - if (!db.containsMessage(txn, m.getId())) - throw new NoSuchMessageException(); - db.setMessageValid(txn, m.getId(), valid); - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } + public void setMessageValid(Transaction transaction, Message m, ClientId c, + boolean valid) throws DbException { + T txn = unbox(transaction); + if (!db.containsMessage(txn, m.getId())) + throw new NoSuchMessageException(); + db.setMessageValid(txn, m.getId(), valid); eventBus.broadcast(new MessageValidatedEvent(m, c, false, valid)); } - public void setReorderingWindow(ContactId c, TransportId t, - long rotationPeriod, long base, byte[] bitmap) throws DbException { - T txn = db.startTransaction(); - try { - if (!db.containsContact(txn, c)) - throw new NoSuchContactException(); - if (!db.containsTransport(txn, t)) - throw new NoSuchTransportException(); - db.setReorderingWindow(txn, c, t, rotationPeriod, base, bitmap); - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } + public void setReorderingWindow(Transaction transaction, ContactId c, + TransportId t, long rotationPeriod, long base, byte[] bitmap) + throws DbException { + T txn = unbox(transaction); + if (!db.containsContact(txn, c)) + throw new NoSuchContactException(); + if (!db.containsTransport(txn, t)) + throw new NoSuchTransportException(); + db.setReorderingWindow(txn, c, t, rotationPeriod, base, bitmap); } - public void setVisibleToContact(ContactId c, GroupId g, boolean visible) - throws DbException { + public void setVisibleToContact(Transaction transaction, ContactId c, + GroupId g, boolean visible) throws DbException { boolean wasVisible; - T txn = db.startTransaction(); - try { - if (!db.containsContact(txn, c)) - throw new NoSuchContactException(); - if (!db.containsGroup(txn, g)) - throw new NoSuchGroupException(); - wasVisible = db.containsVisibleGroup(txn, c, g); - if (visible && !wasVisible) db.addVisibility(txn, c, g); - else if (!visible && wasVisible) db.removeVisibility(txn, c, g); - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; - } + T txn = unbox(transaction); + if (!db.containsContact(txn, c)) + throw new NoSuchContactException(); + if (!db.containsGroup(txn, g)) + throw new NoSuchGroupException(); + wasVisible = db.containsVisibleGroup(txn, c, g); + if (visible && !wasVisible) db.addVisibility(txn, c, g); + else if (!visible && wasVisible) db.removeVisibility(txn, c, g); if (visible != wasVisible) { eventBus.broadcast(new GroupVisibilityUpdatedEvent( Collections.singletonList(c))); } } - public void updateTransportKeys(Map keys) - throws DbException { - T txn = db.startTransaction(); - try { - Map filtered = - new HashMap(); - for (Entry e : keys.entrySet()) { - ContactId c = e.getKey(); - TransportKeys k = e.getValue(); - if (db.containsContact(txn, c) - && db.containsTransport(txn, k.getTransportId())) { - filtered.put(c, k); - } + public void updateTransportKeys(Transaction transaction, + Map keys) throws DbException { + Map filtered = + new HashMap(); + T txn = unbox(transaction); + for (Entry e : keys.entrySet()) { + ContactId c = e.getKey(); + TransportKeys k = e.getValue(); + if (db.containsContact(txn, c) + && db.containsTransport(txn, k.getTransportId())) { + filtered.put(c, k); } - db.updateTransportKeys(txn, filtered); - db.commitTransaction(txn); - } catch (DbException e) { - db.abortTransaction(txn); - throw e; } + db.updateTransportKeys(txn, filtered); } } diff --git a/briar-core/src/org/briarproject/forum/ForumManagerImpl.java b/briar-core/src/org/briarproject/forum/ForumManagerImpl.java index 6820afb47..9da89c671 100644 --- a/briar-core/src/org/briarproject/forum/ForumManagerImpl.java +++ b/briar-core/src/org/briarproject/forum/ForumManagerImpl.java @@ -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 getForums() throws DbException { - lock.readLock().lock(); try { + Collection groups; + Transaction txn = db.startTransaction(); + try { + groups = db.getGroups(txn, CLIENT_ID); + txn.setComplete(); + } finally { + db.endTransaction(txn); + } List forums = new ArrayList(); - 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 getPostHeaders(GroupId g) throws DbException { - lock.readLock().lock(); + Set localAuthorIds = new HashSet(); + Set contactAuthorIds = new HashSet(); + Map metadata; + Transaction txn = db.startTransaction(); try { // Load the IDs of the user's identities - Set localAuthorIds = new HashSet(); - for (LocalAuthor a : identityManager.getLocalAuthors()) + for (LocalAuthor a : db.getLocalAuthors(txn)) localAuthorIds.add(a.getId()); // Load the IDs of contacts' identities - Set contactAuthorIds = new HashSet(); - for (Contact c : contactManager.getContacts()) + for (Contact c : db.getContacts(txn)) contactAuthorIds.add(c.getAuthor().getId()); - // Load and parse the metadata - Map metadata = db.getMessageMetadata(g); - Collection headers = - new ArrayList(); - for (Entry 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 headers = new ArrayList(); + for (Entry 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(); } } diff --git a/briar-core/src/org/briarproject/forum/ForumSharingManagerImpl.java b/briar-core/src/org/briarproject/forum/ForumSharingManagerImpl.java index 72efd00ac..6f8554ce8 100644 --- a/briar-core/src/org/briarproject/forum/ForumSharingManagerImpl.java +++ b/briar-core/src/org/briarproject/forum/ForumSharingManagerImpl.java @@ -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 shared = getForumsSharedWithAllContacts(); - storeMessage(g.getId(), shared, 0); - } catch (DbException e) { - if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); + List 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 getAvailableForums() throws DbException { - lock.readLock().lock(); try { - // Get any forums we subscribe to - Set subscribed = new HashSet(db.getGroups( - forumManager.getClientId())); - // Get all forums shared by contacts Set available = new HashSet(); - 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 subscribed = new HashSet(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 getSharedBy(GroupId g) throws DbException { - lock.readLock().lock(); try { List subscribers = new ArrayList(); - 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 getSharedWith(GroupId g) throws DbException { - lock.readLock().lock(); try { List shared = new ArrayList(); - 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 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(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(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 getForumsSharedWithAllContacts() throws DbException, - FormatException { + private List 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 metadata = db.getMessageMetadata(g); + Map metadata = db.getMessageMetadata(txn, g); for (Entry 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 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 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 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 getVisibleForums(Message remoteUpdate) - throws DbException, FormatException { + private Set 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 shared = new HashSet(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 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 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 list = parseForumList(db.getRawMessage(latest.messageId)); + byte[] raw = db.getRawMessage(txn, latest.messageId); + List 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 list = parseForumList(db.getRawMessage(latest.messageId)); + byte[] raw = db.getRawMessage(txn, latest.messageId); + List 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 list = parseForumList(db.getRawMessage(latest.messageId)); - if (list.remove(f)) storeMessage(g, list, latest.version + 1); + byte[] raw = db.getRawMessage(txn, latest.messageId); + List list = parseForumList(raw); + if (list.remove(f)) storeMessage(txn, g, list, latest.version + 1); } private static class LatestUpdate { diff --git a/briar-core/src/org/briarproject/identity/IdentityManagerImpl.java b/briar-core/src/org/briarproject/identity/IdentityManagerImpl.java index 71b34eac9..475ead3e4 100644 --- a/briar-core/src/org/briarproject/identity/IdentityManagerImpl.java +++ b/briar-core/src/org/briarproject/identity/IdentityManagerImpl.java @@ -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 added = new ArrayList(); + List removed = new ArrayList(); + 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 getLocalAuthors() throws DbException { - Collection authors = db.getLocalAuthors(); + Collection 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 active = new ArrayList(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)); } } diff --git a/briar-core/src/org/briarproject/messaging/MessagingManagerImpl.java b/briar-core/src/org/briarproject/messaging/MessagingManagerImpl.java index 5185cac6e..0441d27ce 100644 --- a/briar-core/src/org/briarproject/messaging/MessagingManagerImpl.java +++ b/briar-core/src/org/briarproject/messaging/MessagingManagerImpl.java @@ -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 getMessageHeaders(ContactId c) throws DbException { - GroupId groupId = getConversationId(c); - Map metadata = db.getMessageMetadata(groupId); - Collection statuses = db.getMessageStatus(c, groupId); + GroupId g = getConversationId(c); + Map metadata; + Collection statuses; + Transaction txn = db.startTransaction(); + try { + metadata = db.getMessageMetadata(txn, g); + statuses = db.getMessageStatus(txn, c, g); + txn.setComplete(); + } finally { + db.endTransaction(txn); + } Collection headers = new ArrayList(); 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); } diff --git a/briar-core/src/org/briarproject/plugins/PluginManagerImpl.java b/briar-core/src/org/briarproject/plugins/PluginManagerImpl.java index 66987c4fd..bd110d0cf 100644 --- a/briar-core/src/org/briarproject/plugins/PluginManagerImpl.java +++ b/briar-core/src/org/briarproject/plugins/PluginManagerImpl.java @@ -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 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(); @@ -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); } diff --git a/briar-core/src/org/briarproject/properties/TransportPropertyManagerImpl.java b/briar-core/src/org/briarproject/properties/TransportPropertyManagerImpl.java index a7babd5cd..1032717f5 100644 --- a/briar-core/src/org/briarproject/properties/TransportPropertyManagerImpl.java +++ b/briar-core/src/org/briarproject/properties/TransportPropertyManagerImpl.java @@ -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 local = getLocalProperties(); - for (Entry 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 local = getLocalProperties(); + for (Entry 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 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 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 getLocalProperties() throws DbException { - lock.readLock().lock(); try { - // Find the latest local update for each transport - Map latest = - findLatest(localGroup.getId(), true); - // Retrieve and parse the latest local properties Map local = new HashMap(); - for (Entry 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 latest = findLatest(txn, + localGroup.getId(), true); + // Retrieve and parse the latest local properties + for (Entry 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 getRemoteProperties( TransportId t) throws DbException { - lock.readLock().lock(); try { Map remote = new HashMap(); - 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 findLatest(GroupId g, boolean local) - throws DbException, FormatException { + private Map findLatest(Transaction txn, + GroupId g, boolean local) throws DbException, FormatException { Map latestUpdates = new HashMap(); - Map metadata = db.getMessageMetadata(g); + Map metadata = db.getMessageMetadata(txn, g); for (Entry 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 metadata = db.getMessageMetadata(g); + Map metadata = db.getMessageMetadata(txn, g); for (Entry 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 { diff --git a/briar-core/src/org/briarproject/settings/SettingsManagerImpl.java b/briar-core/src/org/briarproject/settings/SettingsManagerImpl.java index ee7a3ed68..639d822bb 100644 --- a/briar-core/src/org/briarproject/settings/SettingsManagerImpl.java +++ b/briar-core/src/org/briarproject/settings/SettingsManagerImpl.java @@ -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); + } } } diff --git a/briar-core/src/org/briarproject/sync/DuplexOutgoingSession.java b/briar-core/src/org/briarproject/sync/DuplexOutgoingSession.java index d0af71201..8976eb7d1 100644 --- a/briar-core/src/org/briarproject/sync/DuplexOutgoingSession.java +++ b/briar-core/src/org/briarproject/sync/DuplexOutgoingSession.java @@ -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 CLOSE = new ThrowingRunnable() { - 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 b = db.generateRequestedBatch(contactId, - MAX_PACKET_PAYLOAD_LENGTH, maxLatency); + Collection 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)); diff --git a/briar-core/src/org/briarproject/sync/IncomingSession.java b/briar-core/src/org/briarproject/sync/IncomingSession.java index 6fbfdd242..5e5f4c6df 100644 --- a/briar-core/src/org/briarproject/sync/IncomingSession.java +++ b/briar-core/src/org/briarproject/sync/IncomingSession.java @@ -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(); diff --git a/briar-core/src/org/briarproject/sync/SimplexOutgoingSession.java b/briar-core/src/org/briarproject/sync/SimplexOutgoingSession.java index 620e23a4b..31ca62684 100644 --- a/briar-core/src/org/briarproject/sync/SimplexOutgoingSession.java +++ b/briar-core/src/org/briarproject/sync/SimplexOutgoingSession.java @@ -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 CLOSE = new ThrowingRunnable() { - 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 b = db.generateBatch(contactId, - MAX_PACKET_PAYLOAD_LENGTH, maxLatency); + Collection 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(); diff --git a/briar-core/src/org/briarproject/sync/ValidationManagerImpl.java b/briar-core/src/org/briarproject/sync/ValidationManagerImpl.java index 5fca14584..e075f341f 100644 --- a/briar-core/src/org/briarproject/sync/ValidationManagerImpl.java +++ b/briar-core/src/org/briarproject/sync/ValidationManagerImpl.java @@ -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) { diff --git a/briar-core/src/org/briarproject/transport/KeyManagerImpl.java b/briar-core/src/org/briarproject/transport/KeyManagerImpl.java index b1fa018fc..7e31a877c 100644 --- a/briar-core/src/org/briarproject/transport/KeyManagerImpl.java +++ b/briar-core/src/org/briarproject/transport/KeyManagerImpl.java @@ -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 latencies = db.getTransportLatencies(); + Map latencies; + Transaction txn = db.startTransaction(); + try { + latencies = db.getTransportLatencies(txn); + txn.setComplete(); + } finally { + db.endTransaction(txn); + } for (Entry e : latencies.entrySet()) addTransport(e.getKey(), e.getValue()); } catch (DbException e) { diff --git a/briar-core/src/org/briarproject/transport/TransportKeyManager.java b/briar-core/src/org/briarproject/transport/TransportKeyManager.java index 2df96ccc7..ce15995ac 100644 --- a/briar-core/src/org/briarproject/transport/TransportKeyManager.java +++ b/briar-core/src/org/briarproject/transport/TransportKeyManager.java @@ -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 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 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 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 { diff --git a/briar-tests/src/org/briarproject/db/DatabaseComponentImplTest.java b/briar-tests/src/org/briarproject/db/DatabaseComponentImplTest.java index 0743df949..48e6ea037 100644 --- a/briar-tests/src/org/briarproject/db/DatabaseComponentImplTest.java +++ b/briar-tests/src/org/briarproject/db/DatabaseComponentImplTest.java @@ -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 keys = Collections.singletonMap( + contactId, transportKeys); Mockery context = new Mockery(); @SuppressWarnings("unchecked") final Database 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(); } diff --git a/briar-tests/src/org/briarproject/plugins/PluginManagerImplTest.java b/briar-tests/src/org/briarproject/plugins/PluginManagerImplTest.java index 23fbddea8..25cb91d03 100644 --- a/briar-tests/src/org/briarproject/plugins/PluginManagerImplTest.java +++ b/briar-tests/src/org/briarproject/plugins/PluginManagerImplTest.java @@ -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()); diff --git a/briar-tests/src/org/briarproject/sync/SimplexMessagingIntegrationTest.java b/briar-tests/src/org/briarproject/sync/SimplexMessagingIntegrationTest.java index a4bd5d20e..bfb815c2d 100644 --- a/briar-tests/src/org/briarproject/sync/SimplexMessagingIntegrationTest.java +++ b/briar-tests/src/org/briarproject/sync/SimplexMessagingIntegrationTest.java @@ -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, diff --git a/briar-tests/src/org/briarproject/sync/SimplexOutgoingSessionTest.java b/briar-tests/src/org/briarproject/sync/SimplexOutgoingSessionTest.java index 7b49108ef..635b2b068 100644 --- a/briar-tests/src/org/briarproject/sync/SimplexOutgoingSessionTest.java +++ b/briar-tests/src/org/briarproject/sync/SimplexOutgoingSessionTest.java @@ -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 From 146dac056d906d96b401015dc8dd61bd5d331b5a Mon Sep 17 00:00:00 2001 From: akwizgran Date: Thu, 11 Feb 2016 14:49:41 +0000 Subject: [PATCH 07/11] Broadcast events after committing transactions. --- .../api/db/DatabaseComponent.java | 6 +- .../org/briarproject/api/db/Transaction.java | 25 +++++ .../contact/ContactManagerImpl.java | 17 +--- .../db/DatabaseComponentImpl.java | 96 +++++++++---------- .../identity/IdentityManagerImpl.java | 17 +--- .../db/DatabaseComponentImplTest.java | 12 ++- 6 files changed, 88 insertions(+), 85 deletions(-) diff --git a/briar-api/src/org/briarproject/api/db/DatabaseComponent.java b/briar-api/src/org/briarproject/api/db/DatabaseComponent.java index d38dc08d6..5592042b1 100644 --- a/briar-api/src/org/briarproject/api/db/DatabaseComponent.java +++ b/briar-api/src/org/briarproject/api/db/DatabaseComponent.java @@ -49,9 +49,9 @@ public interface DatabaseComponent { Transaction startTransaction() throws DbException; /** - * Ends a transaction. If the transaction's - * {@link Transaction#setComplete() commit} flag is set, the - * transaction is committed, otherwise it is aborted. + * Ends a transaction. If the transaction is marked as complete, the + * transaction is committed and any events attached to the transaction are + * broadcast; otherwise the transaction is aborted. */ void endTransaction(Transaction txn) throws DbException; diff --git a/briar-api/src/org/briarproject/api/db/Transaction.java b/briar-api/src/org/briarproject/api/db/Transaction.java index 46497a039..cb89f142c 100644 --- a/briar-api/src/org/briarproject/api/db/Transaction.java +++ b/briar-api/src/org/briarproject/api/db/Transaction.java @@ -1,11 +1,19 @@ package org.briarproject.api.db; +import org.briarproject.api.event.Event; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + /** * A wrapper around a database transaction. Transactions are not thread-safe. */ public class Transaction { private final Object txn; + + private List events = null; private boolean complete = false; public Transaction(Object txn) { @@ -20,6 +28,23 @@ public class Transaction { return txn; } + /** + * Attaches an event to be broadcast when the transaction has been + * committed. + */ + public void attach(Event e) { + if (events == null) events = new ArrayList(); + events.add(e); + } + + /** + * Returns any events attached to the transaction. + */ + public List getEvents() { + if (events == null) return Collections.emptyList(); + return events; + } + /** * Returns true if the transaction is ready to be committed. */ diff --git a/briar-core/src/org/briarproject/contact/ContactManagerImpl.java b/briar-core/src/org/briarproject/contact/ContactManagerImpl.java index 7bdb3bc45..4edc06545 100644 --- a/briar-core/src/org/briarproject/contact/ContactManagerImpl.java +++ b/briar-core/src/org/briarproject/contact/ContactManagerImpl.java @@ -9,9 +9,6 @@ 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; import org.briarproject.api.identity.Author; import org.briarproject.api.identity.AuthorId; import org.briarproject.api.identity.IdentityManager.RemoveIdentityHook; @@ -37,14 +34,12 @@ class ContactManagerImpl implements ContactManager, Service, Logger.getLogger(ContactManagerImpl.class.getName()); private final DatabaseComponent db; - private final EventBus eventBus; private final List addHooks; private final List removeHooks; @Inject - ContactManagerImpl(DatabaseComponent db, EventBus eventBus) { + ContactManagerImpl(DatabaseComponent db) { this.db = db; - this.eventBus = eventBus; addHooks = new CopyOnWriteArrayList(); removeHooks = new CopyOnWriteArrayList(); } @@ -53,8 +48,6 @@ class ContactManagerImpl implements ContactManager, Service, public boolean start() { // Finish adding/removing any partly added/removed contacts try { - List added = new ArrayList(); - List removed = new ArrayList(); Transaction txn = db.startTransaction(); try { for (Contact c : db.getContacts(txn)) { @@ -62,22 +55,16 @@ class ContactManagerImpl implements ContactManager, Service, 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); @@ -115,7 +102,6 @@ class ContactManagerImpl implements ContactManager, Service, } finally { db.endTransaction(txn); } - eventBus.broadcast(new ContactAddedEvent(c)); return c; } @@ -159,7 +145,6 @@ class ContactManagerImpl implements ContactManager, Service, } finally { db.endTransaction(txn); } - eventBus.broadcast(new ContactRemovedEvent(c)); } private void removeContact(Transaction txn, ContactId c) diff --git a/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java b/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java index 8d4c71166..00e064883 100644 --- a/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java +++ b/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java @@ -15,10 +15,15 @@ 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.ContactAddedEvent; +import org.briarproject.api.event.ContactRemovedEvent; +import org.briarproject.api.event.Event; import org.briarproject.api.event.EventBus; import org.briarproject.api.event.GroupAddedEvent; import org.briarproject.api.event.GroupRemovedEvent; import org.briarproject.api.event.GroupVisibilityUpdatedEvent; +import org.briarproject.api.event.LocalAuthorAddedEvent; +import org.briarproject.api.event.LocalAuthorRemovedEvent; import org.briarproject.api.event.MessageAddedEvent; import org.briarproject.api.event.MessageRequestedEvent; import org.briarproject.api.event.MessageSharedEvent; @@ -66,8 +71,6 @@ import static org.briarproject.api.sync.ValidationManager.Validity.UNKNOWN; import static org.briarproject.api.sync.ValidationManager.Validity.VALID; import static org.briarproject.db.DatabaseConstants.MAX_OFFERED_MESSAGES; -// TODO: Callers should broadcast events after committing transactions - /** * An implementation of DatabaseComponent using reentrant read-write locks. * Depending on the JVM's lock implementation, this implementation may allow @@ -127,8 +130,12 @@ class DatabaseComponentImpl implements DatabaseComponent { public void endTransaction(Transaction transaction) throws DbException { T txn = txnClass.cast(transaction.unbox()); - if (transaction.isComplete()) db.commitTransaction(txn); - else db.abortTransaction(txn); + if (transaction.isComplete()) { + db.commitTransaction(txn); + for (Event e : transaction.getEvents()) eventBus.broadcast(e); + } else { + db.abortTransaction(txn); + } } private T unbox(Transaction transaction) { @@ -143,42 +150,40 @@ class DatabaseComponentImpl implements DatabaseComponent { throw new NoSuchLocalAuthorException(); if (db.containsContact(txn, remote.getId(), local)) throw new ContactExistsException(); - return db.addContact(txn, remote, local); + ContactId c = db.addContact(txn, remote, local); + transaction.attach(new ContactAddedEvent(c)); + return c; } public void addGroup(Transaction transaction, Group g) throws DbException { - boolean added = false; T txn = unbox(transaction); if (!db.containsGroup(txn, g.getId())) { db.addGroup(txn, g); - added = true; + transaction.attach(new GroupAddedEvent(g)); } - if (added) eventBus.broadcast(new GroupAddedEvent(g)); } public void addLocalAuthor(Transaction transaction, LocalAuthor a) throws DbException { T txn = unbox(transaction); - if (!db.containsLocalAuthor(txn, a.getId())) + if (!db.containsLocalAuthor(txn, a.getId())) { db.addLocalAuthor(txn, a); + transaction.attach(new LocalAuthorAddedEvent(a.getId())); + } } public void addLocalMessage(Transaction transaction, Message m, ClientId c, Metadata meta, boolean shared) throws DbException { - boolean added = false; T txn = unbox(transaction); if (!db.containsGroup(txn, m.getGroupId())) throw new NoSuchGroupException(); if (!db.containsMessage(txn, m.getId())) { addMessage(txn, m, VALID, shared, null); - added = true; + transaction.attach(new MessageAddedEvent(m, null)); + transaction.attach(new MessageValidatedEvent(m, c, true, true)); + if (shared) transaction.attach(new MessageSharedEvent(m)); } db.mergeMessageMetadata(txn, m.getId(), meta); - if (added) { - eventBus.broadcast(new MessageAddedEvent(m, null)); - eventBus.broadcast(new MessageValidatedEvent(m, c, true, true)); - if (shared) eventBus.broadcast(new MessageSharedEvent(m)); - } } /** @@ -206,13 +211,11 @@ class DatabaseComponentImpl implements DatabaseComponent { public void addTransport(Transaction transaction, TransportId t, int maxLatency) throws DbException { - boolean added = false; T txn = unbox(transaction); if (!db.containsTransport(txn, t)) { db.addTransport(txn, t, maxLatency); - added = true; + transaction.attach(new TransportAddedEvent(t, maxLatency)); } - if (added) eventBus.broadcast(new TransportAddedEvent(t, maxLatency)); } public void addTransportKeys(Transaction transaction, ContactId c, @@ -266,9 +269,9 @@ class DatabaseComponentImpl implements DatabaseComponent { messages.add(db.getRawMessage(txn, m)); db.updateExpiryTime(txn, c, m, maxLatency); } - if (!ids.isEmpty()) db.lowerRequestedFlag(txn, c, ids); - if (messages.isEmpty()) return null; - if (!ids.isEmpty()) eventBus.broadcast(new MessagesSentEvent(c, ids)); + if (ids.isEmpty()) return null; + db.lowerRequestedFlag(txn, c, ids); + transaction.attach(new MessagesSentEvent(c, ids)); return Collections.unmodifiableList(messages); } @@ -309,9 +312,9 @@ class DatabaseComponentImpl implements DatabaseComponent { messages.add(db.getRawMessage(txn, m)); db.updateExpiryTime(txn, c, m, maxLatency); } - if (!ids.isEmpty()) db.lowerRequestedFlag(txn, c, ids); - if (messages.isEmpty()) return null; - if (!ids.isEmpty()) eventBus.broadcast(new MessagesSentEvent(c, ids)); + if (ids.isEmpty()) return null; + db.lowerRequestedFlag(txn, c, ids); + transaction.attach(new MessagesSentEvent(c, ids)); return Collections.unmodifiableList(messages); } @@ -486,7 +489,6 @@ class DatabaseComponentImpl implements DatabaseComponent { public void mergeSettings(Transaction transaction, Settings s, String namespace) throws DbException { - boolean changed = false; T txn = unbox(transaction); Settings old = db.getSettings(txn, namespace); Settings merged = new Settings(); @@ -494,9 +496,8 @@ class DatabaseComponentImpl implements DatabaseComponent { merged.putAll(s); if (!merged.equals(old)) { db.mergeSettings(txn, s, namespace); - changed = true; + transaction.attach(new SettingsUpdatedEvent(namespace)); } - if (changed) eventBus.broadcast(new SettingsUpdatedEvent(namespace)); } public void receiveAck(Transaction transaction, ContactId c, Ack a) @@ -511,24 +512,21 @@ class DatabaseComponentImpl implements DatabaseComponent { acked.add(m); } } - eventBus.broadcast(new MessagesAckedEvent(c, acked)); + transaction.attach(new MessagesAckedEvent(c, acked)); } public void receiveMessage(Transaction transaction, ContactId c, Message m) throws DbException { - boolean duplicate, visible; T txn = unbox(transaction); if (!db.containsContact(txn, c)) throw new NoSuchContactException(); - duplicate = db.containsMessage(txn, m.getId()); - visible = db.containsVisibleGroup(txn, c, m.getGroupId()); - if (visible) { - if (!duplicate) addMessage(txn, m, UNKNOWN, false, c); + if (db.containsVisibleGroup(txn, c, m.getGroupId())) { + if (!db.containsMessage(txn, m.getId())) { + addMessage(txn, m, UNKNOWN, false, c); + transaction.attach(new MessageAddedEvent(m, c)); + } db.raiseAckFlag(txn, c, m.getId()); - } - if (visible) { - if (!duplicate) eventBus.broadcast(new MessageAddedEvent(m, c)); - eventBus.broadcast(new MessageToAckEvent(c)); + transaction.attach(new MessageToAckEvent(c)); } } @@ -550,8 +548,8 @@ class DatabaseComponentImpl implements DatabaseComponent { count++; } } - if (ack) eventBus.broadcast(new MessageToAckEvent(c)); - if (request) eventBus.broadcast(new MessageToRequestEvent(c)); + if (ack) transaction.attach(new MessageToAckEvent(c)); + if (request) transaction.attach(new MessageToRequestEvent(c)); } public void receiveRequest(Transaction transaction, ContactId c, Request r) @@ -567,7 +565,7 @@ class DatabaseComponentImpl implements DatabaseComponent { requested = true; } } - if (requested) eventBus.broadcast(new MessageRequestedEvent(c)); + if (requested) transaction.attach(new MessageRequestedEvent(c)); } public void removeContact(Transaction transaction, ContactId c) @@ -576,6 +574,7 @@ class DatabaseComponentImpl implements DatabaseComponent { if (!db.containsContact(txn, c)) throw new NoSuchContactException(); db.removeContact(txn, c); + transaction.attach(new ContactRemovedEvent(c)); } public void removeGroup(Transaction transaction, Group g) @@ -587,8 +586,8 @@ class DatabaseComponentImpl implements DatabaseComponent { throw new NoSuchGroupException(); affected = db.getVisibility(txn, id); db.removeGroup(txn, id); - eventBus.broadcast(new GroupRemovedEvent(g)); - eventBus.broadcast(new GroupVisibilityUpdatedEvent(affected)); + transaction.attach(new GroupRemovedEvent(g)); + transaction.attach(new GroupVisibilityUpdatedEvent(affected)); } public void removeLocalAuthor(Transaction transaction, AuthorId a) @@ -597,6 +596,7 @@ class DatabaseComponentImpl implements DatabaseComponent { if (!db.containsLocalAuthor(txn, a)) throw new NoSuchLocalAuthorException(); db.removeLocalAuthor(txn, a); + transaction.attach(new LocalAuthorRemovedEvent(a)); } public void removeTransport(Transaction transaction, TransportId t) @@ -605,7 +605,7 @@ class DatabaseComponentImpl implements DatabaseComponent { if (!db.containsTransport(txn, t)) throw new NoSuchTransportException(); db.removeTransport(txn, t); - eventBus.broadcast(new TransportRemovedEvent(t)); + transaction.attach(new TransportRemovedEvent(t)); } public void setContactStatus(Transaction transaction, ContactId c, @@ -630,7 +630,7 @@ class DatabaseComponentImpl implements DatabaseComponent { if (!db.containsMessage(txn, m.getId())) throw new NoSuchMessageException(); db.setMessageShared(txn, m.getId(), shared); - if (shared) eventBus.broadcast(new MessageSharedEvent(m)); + if (shared) transaction.attach(new MessageSharedEvent(m)); } public void setMessageValid(Transaction transaction, Message m, ClientId c, @@ -639,7 +639,7 @@ class DatabaseComponentImpl implements DatabaseComponent { if (!db.containsMessage(txn, m.getId())) throw new NoSuchMessageException(); db.setMessageValid(txn, m.getId(), valid); - eventBus.broadcast(new MessageValidatedEvent(m, c, false, valid)); + transaction.attach(new MessageValidatedEvent(m, c, false, valid)); } public void setReorderingWindow(Transaction transaction, ContactId c, @@ -665,8 +665,8 @@ class DatabaseComponentImpl implements DatabaseComponent { if (visible && !wasVisible) db.addVisibility(txn, c, g); else if (!visible && wasVisible) db.removeVisibility(txn, c, g); if (visible != wasVisible) { - eventBus.broadcast(new GroupVisibilityUpdatedEvent( - Collections.singletonList(c))); + List affected = Collections.singletonList(c); + transaction.attach(new GroupVisibilityUpdatedEvent(affected)); } } diff --git a/briar-core/src/org/briarproject/identity/IdentityManagerImpl.java b/briar-core/src/org/briarproject/identity/IdentityManagerImpl.java index 475ead3e4..93997dbb3 100644 --- a/briar-core/src/org/briarproject/identity/IdentityManagerImpl.java +++ b/briar-core/src/org/briarproject/identity/IdentityManagerImpl.java @@ -6,9 +6,6 @@ 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; import org.briarproject.api.identity.AuthorId; import org.briarproject.api.identity.IdentityManager; import org.briarproject.api.identity.LocalAuthor; @@ -32,14 +29,12 @@ class IdentityManagerImpl implements IdentityManager, Service { Logger.getLogger(IdentityManagerImpl.class.getName()); private final DatabaseComponent db; - private final EventBus eventBus; private final List addHooks; private final List removeHooks; @Inject - IdentityManagerImpl(DatabaseComponent db, EventBus eventBus) { + IdentityManagerImpl(DatabaseComponent db) { this.db = db; - this.eventBus = eventBus; addHooks = new CopyOnWriteArrayList(); removeHooks = new CopyOnWriteArrayList(); } @@ -48,8 +43,6 @@ class IdentityManagerImpl implements IdentityManager, Service { public boolean start() { // Finish adding/removing any partly added/removed pseudonyms try { - List added = new ArrayList(); - List removed = new ArrayList(); Transaction txn = db.startTransaction(); try { for (LocalAuthor a : db.getLocalAuthors(txn)) { @@ -57,22 +50,16 @@ class IdentityManagerImpl implements IdentityManager, Service { 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); @@ -107,7 +94,6 @@ class IdentityManagerImpl implements IdentityManager, Service { } finally { db.endTransaction(txn); } - eventBus.broadcast(new LocalAuthorAddedEvent(localAuthor.getId())); } @Override @@ -154,6 +140,5 @@ class IdentityManagerImpl implements IdentityManager, Service { } finally { db.endTransaction(txn); } - eventBus.broadcast(new LocalAuthorRemovedEvent(a)); } } diff --git a/briar-tests/src/org/briarproject/db/DatabaseComponentImplTest.java b/briar-tests/src/org/briarproject/db/DatabaseComponentImplTest.java index 48e6ea037..a56542d38 100644 --- a/briar-tests/src/org/briarproject/db/DatabaseComponentImplTest.java +++ b/briar-tests/src/org/briarproject/db/DatabaseComponentImplTest.java @@ -15,10 +15,14 @@ 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.ContactAddedEvent; +import org.briarproject.api.event.ContactRemovedEvent; import org.briarproject.api.event.EventBus; import org.briarproject.api.event.GroupAddedEvent; import org.briarproject.api.event.GroupRemovedEvent; import org.briarproject.api.event.GroupVisibilityUpdatedEvent; +import org.briarproject.api.event.LocalAuthorAddedEvent; +import org.briarproject.api.event.LocalAuthorRemovedEvent; import org.briarproject.api.event.MessageAddedEvent; import org.briarproject.api.event.MessageRequestedEvent; import org.briarproject.api.event.MessageSharedEvent; @@ -136,6 +140,7 @@ public class DatabaseComponentImplTest extends BriarTestCase { oneOf(database).containsLocalAuthor(txn, localAuthorId); will(returnValue(false)); oneOf(database).addLocalAuthor(txn, localAuthor); + oneOf(eventBus).broadcast(with(any(LocalAuthorAddedEvent.class))); // addContact() oneOf(database).containsLocalAuthor(txn, localAuthorId); will(returnValue(true)); @@ -143,6 +148,7 @@ public class DatabaseComponentImplTest extends BriarTestCase { will(returnValue(false)); oneOf(database).addContact(txn, author, localAuthorId); will(returnValue(contactId)); + oneOf(eventBus).broadcast(with(any(ContactAddedEvent.class))); // getContacts() oneOf(database).getContacts(txn); will(returnValue(Collections.singletonList(contact))); @@ -170,10 +176,12 @@ public class DatabaseComponentImplTest extends BriarTestCase { oneOf(database).containsContact(txn, contactId); will(returnValue(true)); oneOf(database).removeContact(txn, contactId); + oneOf(eventBus).broadcast(with(any(ContactRemovedEvent.class))); // removeLocalAuthor() oneOf(database).containsLocalAuthor(txn, localAuthorId); will(returnValue(true)); oneOf(database).removeLocalAuthor(txn, localAuthorId); + oneOf(eventBus).broadcast(with(any(LocalAuthorRemovedEvent.class))); // endTransaction() oneOf(database).commitTransaction(txn); // close() @@ -743,6 +751,7 @@ public class DatabaseComponentImplTest extends BriarTestCase { oneOf(database).containsLocalAuthor(txn, localAuthorId); will(returnValue(false)); oneOf(database).addLocalAuthor(txn, localAuthor); + oneOf(eventBus).broadcast(with(any(LocalAuthorAddedEvent.class))); // addContact() oneOf(database).containsLocalAuthor(txn, localAuthorId); will(returnValue(true)); @@ -750,6 +759,7 @@ public class DatabaseComponentImplTest extends BriarTestCase { will(returnValue(false)); oneOf(database).addContact(txn, author, localAuthorId); will(returnValue(contactId)); + oneOf(eventBus).broadcast(with(any(ContactAddedEvent.class))); // endTransaction() oneOf(database).commitTransaction(txn); // Check whether the transport is in the DB (which it's not) @@ -1137,8 +1147,6 @@ public class DatabaseComponentImplTest extends BriarTestCase { will(returnValue(txn)); oneOf(database).containsContact(txn, contactId); will(returnValue(true)); - oneOf(database).containsMessage(txn, messageId); - will(returnValue(false)); oneOf(database).containsVisibleGroup(txn, contactId, groupId); will(returnValue(false)); oneOf(database).commitTransaction(txn); From 6505db58e679eb312091363e385899400ad4c120 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Thu, 11 Feb 2016 14:52:38 +0000 Subject: [PATCH 08/11] Contact and identity managers don't need to be services. --- .../contact/ContactManagerImpl.java | 43 +------------------ .../briarproject/contact/ContactModule.java | 5 +-- .../identity/IdentityManagerImpl.java | 42 +----------------- .../briarproject/identity/IdentityModule.java | 5 +-- 4 files changed, 5 insertions(+), 90 deletions(-) diff --git a/briar-core/src/org/briarproject/contact/ContactManagerImpl.java b/briar-core/src/org/briarproject/contact/ContactManagerImpl.java index 4edc06545..b86e63f29 100644 --- a/briar-core/src/org/briarproject/contact/ContactManagerImpl.java +++ b/briar-core/src/org/briarproject/contact/ContactManagerImpl.java @@ -13,25 +13,17 @@ import org.briarproject.api.identity.Author; import org.briarproject.api.identity.AuthorId; import org.briarproject.api.identity.IdentityManager.RemoveIdentityHook; import org.briarproject.api.identity.LocalAuthor; -import org.briarproject.api.lifecycle.Service; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; -import java.util.logging.Logger; -import static java.util.logging.Level.WARNING; import static org.briarproject.api.db.StorageStatus.ACTIVE; -import static org.briarproject.api.db.StorageStatus.ADDING; import static org.briarproject.api.db.StorageStatus.REMOVING; -class ContactManagerImpl implements ContactManager, Service, - RemoveIdentityHook { - - private static final Logger LOG = - Logger.getLogger(ContactManagerImpl.class.getName()); +class ContactManagerImpl implements ContactManager, RemoveIdentityHook { private final DatabaseComponent db; private final List addHooks; @@ -44,39 +36,6 @@ class ContactManagerImpl implements ContactManager, Service, removeHooks = new CopyOnWriteArrayList(); } - @Override - public boolean start() { - // Finish adding/removing any partly added/removed contacts - try { - 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); - } else if (c.getStatus().equals(REMOVING)) { - for (RemoveContactHook hook : removeHooks) - hook.removingContact(txn, c); - db.removeContact(txn, c.getId()); - } - } - txn.setComplete(); - } finally { - db.endTransaction(txn); - } - return true; - } catch (DbException e) { - if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - return false; - } - } - - @Override - public boolean stop() { - return true; - } - @Override public void registerAddContactHook(AddContactHook hook) { addHooks.add(hook); diff --git a/briar-core/src/org/briarproject/contact/ContactModule.java b/briar-core/src/org/briarproject/contact/ContactModule.java index 5bfb8e404..8e9f3d632 100644 --- a/briar-core/src/org/briarproject/contact/ContactModule.java +++ b/briar-core/src/org/briarproject/contact/ContactModule.java @@ -5,7 +5,6 @@ import com.google.inject.Provides; import org.briarproject.api.contact.ContactManager; import org.briarproject.api.identity.IdentityManager; -import org.briarproject.api.lifecycle.LifecycleManager; import javax.inject.Singleton; @@ -15,10 +14,8 @@ public class ContactModule extends AbstractModule { protected void configure() {} @Provides @Singleton - ContactManager getContactManager(LifecycleManager lifecycleManager, - IdentityManager identityManager, + ContactManager getContactManager(IdentityManager identityManager, ContactManagerImpl contactManager) { - lifecycleManager.register(contactManager); identityManager.registerRemoveIdentityHook(contactManager); return contactManager; } diff --git a/briar-core/src/org/briarproject/identity/IdentityManagerImpl.java b/briar-core/src/org/briarproject/identity/IdentityManagerImpl.java index 93997dbb3..49929799b 100644 --- a/briar-core/src/org/briarproject/identity/IdentityManagerImpl.java +++ b/briar-core/src/org/briarproject/identity/IdentityManagerImpl.java @@ -9,24 +9,17 @@ import org.briarproject.api.db.Transaction; import org.briarproject.api.identity.AuthorId; import org.briarproject.api.identity.IdentityManager; import org.briarproject.api.identity.LocalAuthor; -import org.briarproject.api.lifecycle.Service; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; -import java.util.logging.Logger; -import static java.util.logging.Level.WARNING; import static org.briarproject.api.db.StorageStatus.ACTIVE; -import static org.briarproject.api.db.StorageStatus.ADDING; import static org.briarproject.api.db.StorageStatus.REMOVING; -class IdentityManagerImpl implements IdentityManager, Service { - - private static final Logger LOG = - Logger.getLogger(IdentityManagerImpl.class.getName()); +class IdentityManagerImpl implements IdentityManager { private final DatabaseComponent db; private final List addHooks; @@ -39,39 +32,6 @@ class IdentityManagerImpl implements IdentityManager, Service { removeHooks = new CopyOnWriteArrayList(); } - @Override - public boolean start() { - // Finish adding/removing any partly added/removed pseudonyms - try { - 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); - } else if (a.getStatus().equals(REMOVING)) { - for (RemoveIdentityHook hook : removeHooks) - hook.removingIdentity(txn, a); - db.removeLocalAuthor(txn, a.getId()); - } - } - txn.setComplete(); - } finally { - db.endTransaction(txn); - } - return true; - } catch (DbException e) { - if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - return false; - } - } - - @Override - public boolean stop() { - return false; - } - @Override public void registerAddIdentityHook(AddIdentityHook hook) { addHooks.add(hook); diff --git a/briar-core/src/org/briarproject/identity/IdentityModule.java b/briar-core/src/org/briarproject/identity/IdentityModule.java index a57b916cc..c987a8a55 100644 --- a/briar-core/src/org/briarproject/identity/IdentityModule.java +++ b/briar-core/src/org/briarproject/identity/IdentityModule.java @@ -12,13 +12,12 @@ public class IdentityModule extends AbstractModule { @Override protected void configure() { - bind(AuthorFactory.class).to( - org.briarproject.identity.AuthorFactoryImpl.class); + bind(AuthorFactory.class).to(AuthorFactoryImpl.class); bind(IdentityManager.class).to(IdentityManagerImpl.class); } @Provides ObjectReader getAuthorReader(AuthorFactory authorFactory) { - return new org.briarproject.identity.AuthorReader(authorFactory); + return new AuthorReader(authorFactory); } } From 074892b6778185cad001e587bd85e209b533c2ca Mon Sep 17 00:00:00 2001 From: akwizgran Date: Thu, 11 Feb 2016 15:02:41 +0000 Subject: [PATCH 09/11] StorageStatus is no longer needed. --- .../org/briarproject/api/contact/Contact.java | 10 +-- .../api/db/DatabaseComponent.java | 12 --- .../briarproject/api/db/StorageStatus.java | 21 ----- .../api/identity/LocalAuthor.java | 11 +-- .../contact/ContactManagerImpl.java | 17 +--- .../src/org/briarproject/db/Database.java | 13 ---- .../db/DatabaseComponentImpl.java | 17 ---- .../src/org/briarproject/db/JdbcDatabase.java | 78 ++++--------------- .../identity/AuthorFactoryImpl.java | 4 +- .../identity/IdentityManagerImpl.java | 17 +--- .../db/DatabaseComponentImplTest.java | 7 +- .../org/briarproject/db/H2DatabaseTest.java | 7 +- .../sync/SimplexMessagingIntegrationTest.java | 7 +- 13 files changed, 28 insertions(+), 193 deletions(-) delete mode 100644 briar-api/src/org/briarproject/api/db/StorageStatus.java diff --git a/briar-api/src/org/briarproject/api/contact/Contact.java b/briar-api/src/org/briarproject/api/contact/Contact.java index 18a2cb446..5c5b9b197 100644 --- a/briar-api/src/org/briarproject/api/contact/Contact.java +++ b/briar-api/src/org/briarproject/api/contact/Contact.java @@ -1,6 +1,5 @@ package org.briarproject.api.contact; -import org.briarproject.api.db.StorageStatus; import org.briarproject.api.identity.Author; import org.briarproject.api.identity.AuthorId; @@ -9,14 +8,11 @@ public class Contact { private final ContactId id; private final Author author; private final AuthorId localAuthorId; - private final StorageStatus status; - public Contact(ContactId id, Author author, AuthorId localAuthorId, - StorageStatus status) { + public Contact(ContactId id, Author author, AuthorId localAuthorId) { this.id = id; this.author = author; this.localAuthorId = localAuthorId; - this.status = status; } public ContactId getId() { @@ -31,10 +27,6 @@ public class Contact { return localAuthorId; } - public StorageStatus getStatus() { - return status; - } - @Override public int hashCode() { return id.hashCode(); diff --git a/briar-api/src/org/briarproject/api/db/DatabaseComponent.java b/briar-api/src/org/briarproject/api/db/DatabaseComponent.java index 5592042b1..ca2497694 100644 --- a/briar-api/src/org/briarproject/api/db/DatabaseComponent.java +++ b/briar-api/src/org/briarproject/api/db/DatabaseComponent.java @@ -317,18 +317,6 @@ public interface DatabaseComponent { */ void removeTransport(Transaction txn, TransportId t) 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(Transaction txn, AuthorId a, StorageStatus s) - throws DbException; - /** * Marks the given message as shared or unshared. */ diff --git a/briar-api/src/org/briarproject/api/db/StorageStatus.java b/briar-api/src/org/briarproject/api/db/StorageStatus.java deleted file mode 100644 index dc2d554ff..000000000 --- a/briar-api/src/org/briarproject/api/db/StorageStatus.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.briarproject.api.db; - -public enum StorageStatus { - - ADDING(0), ACTIVE(1), REMOVING(2); - - private final int value; - - StorageStatus(int value) { - this.value = value; - } - - public int getValue() { - return value; - } - - public static StorageStatus fromValue(int value) { - for (StorageStatus s : values()) if (s.value == value) return s; - throw new IllegalArgumentException(); - } -} diff --git a/briar-api/src/org/briarproject/api/identity/LocalAuthor.java b/briar-api/src/org/briarproject/api/identity/LocalAuthor.java index 08ee121e5..cdcfdc788 100644 --- a/briar-api/src/org/briarproject/api/identity/LocalAuthor.java +++ b/briar-api/src/org/briarproject/api/identity/LocalAuthor.java @@ -1,20 +1,16 @@ package org.briarproject.api.identity; -import org.briarproject.api.db.StorageStatus; - /** A pseudonym for the local user. */ public class LocalAuthor extends Author { private final byte[] privateKey; private final long created; - private final StorageStatus status; public LocalAuthor(AuthorId id, String name, byte[] publicKey, - byte[] privateKey, long created, StorageStatus status) { + byte[] privateKey, long created) { super(id, name, publicKey); this.privateKey = privateKey; this.created = created; - this.status = status; } /** Returns the private key used to generate the pseudonym's signatures. */ @@ -29,9 +25,4 @@ public class LocalAuthor extends Author { public long getTimeCreated() { return created; } - - /** Returns the status of the pseudonym. */ - public StorageStatus getStatus() { - return status; - } } diff --git a/briar-core/src/org/briarproject/contact/ContactManagerImpl.java b/briar-core/src/org/briarproject/contact/ContactManagerImpl.java index b86e63f29..2fafe8a16 100644 --- a/briar-core/src/org/briarproject/contact/ContactManagerImpl.java +++ b/briar-core/src/org/briarproject/contact/ContactManagerImpl.java @@ -7,22 +7,16 @@ import org.briarproject.api.contact.ContactId; 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.identity.Author; import org.briarproject.api.identity.AuthorId; import org.briarproject.api.identity.IdentityManager.RemoveIdentityHook; import org.briarproject.api.identity.LocalAuthor; -import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; -import static org.briarproject.api.db.StorageStatus.ACTIVE; -import static org.briarproject.api.db.StorageStatus.REMOVING; - class ContactManagerImpl implements ContactManager, RemoveIdentityHook { private final DatabaseComponent db; @@ -56,7 +50,6 @@ class ContactManagerImpl implements ContactManager, RemoveIdentityHook { 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); @@ -74,8 +67,7 @@ class ContactManagerImpl implements ContactManager, RemoveIdentityHook { } finally { db.endTransaction(txn); } - if (contact.getStatus().equals(ACTIVE)) return contact; - throw new NoSuchContactException(); + return contact; } @Override @@ -88,11 +80,7 @@ class ContactManagerImpl implements ContactManager, RemoveIdentityHook { } finally { db.endTransaction(txn); } - // Filter out any contacts that are being added or removed - List active = new ArrayList(contacts.size()); - for (Contact c : contacts) - if (c.getStatus().equals(ACTIVE)) active.add(c); - return Collections.unmodifiableList(active); + return contacts; } @Override @@ -109,7 +97,6 @@ class ContactManagerImpl implements ContactManager, RemoveIdentityHook { 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); diff --git a/briar-core/src/org/briarproject/db/Database.java b/briar-core/src/org/briarproject/db/Database.java index 30c824038..91d17b49d 100644 --- a/briar-core/src/org/briarproject/db/Database.java +++ b/briar-core/src/org/briarproject/db/Database.java @@ -6,7 +6,6 @@ import org.briarproject.api.contact.Contact; import org.briarproject.api.contact.ContactId; import org.briarproject.api.db.DbException; import org.briarproject.api.db.Metadata; -import org.briarproject.api.db.StorageStatus; import org.briarproject.api.identity.Author; import org.briarproject.api.identity.AuthorId; import org.briarproject.api.identity.LocalAuthor; @@ -441,18 +440,6 @@ interface Database { */ void resetExpiryTime(T txn, ContactId c, MessageId m) throws DbException; - /** - * Sets the status of the given contact. - */ - void setContactStatus(T txn, ContactId c, StorageStatus s) - throws DbException; - - /** - * Sets the status of the given local pseudonym. - */ - void setLocalAuthorStatus(T txn, AuthorId a, StorageStatus s) - throws DbException; - /** * Marks the given message as shared or unshared. */ diff --git a/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java b/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java index 00e064883..9a271f7ab 100644 --- a/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java +++ b/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java @@ -13,7 +13,6 @@ import org.briarproject.api.db.NoSuchGroupException; 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.ContactAddedEvent; import org.briarproject.api.event.ContactRemovedEvent; @@ -608,22 +607,6 @@ class DatabaseComponentImpl implements DatabaseComponent { transaction.attach(new TransportRemovedEvent(t)); } - public void setContactStatus(Transaction transaction, ContactId c, - StorageStatus s) throws DbException { - T txn = unbox(transaction); - if (!db.containsContact(txn, c)) - throw new NoSuchContactException(); - db.setContactStatus(txn, c, s); - } - - public void setLocalAuthorStatus(Transaction transaction, AuthorId a, - StorageStatus s) throws DbException { - T txn = unbox(transaction); - if (!db.containsLocalAuthor(txn, a)) - throw new NoSuchLocalAuthorException(); - db.setLocalAuthorStatus(txn, a, s); - } - public void setMessageShared(Transaction transaction, Message m, boolean shared) throws DbException { T txn = unbox(transaction); diff --git a/briar-core/src/org/briarproject/db/JdbcDatabase.java b/briar-core/src/org/briarproject/db/JdbcDatabase.java index b1b536d7f..4af26f320 100644 --- a/briar-core/src/org/briarproject/db/JdbcDatabase.java +++ b/briar-core/src/org/briarproject/db/JdbcDatabase.java @@ -9,7 +9,6 @@ import org.briarproject.api.crypto.SecretKey; import org.briarproject.api.db.DbClosedException; import org.briarproject.api.db.DbException; import org.briarproject.api.db.Metadata; -import org.briarproject.api.db.StorageStatus; import org.briarproject.api.identity.Author; import org.briarproject.api.identity.AuthorId; import org.briarproject.api.identity.LocalAuthor; @@ -48,7 +47,6 @@ import java.util.logging.Logger; import static java.util.logging.Level.WARNING; import static org.briarproject.api.db.Metadata.REMOVE; -import static org.briarproject.api.db.StorageStatus.ADDING; import static org.briarproject.api.sync.ValidationManager.Validity.INVALID; import static org.briarproject.api.sync.ValidationManager.Validity.UNKNOWN; import static org.briarproject.api.sync.ValidationManager.Validity.VALID; @@ -65,8 +63,8 @@ import static org.briarproject.db.ExponentialBackoff.calculateExpiry; */ abstract class JdbcDatabase implements Database { - private static final int SCHEMA_VERSION = 20; - private static final int MIN_SCHEMA_VERSION = 20; + private static final int SCHEMA_VERSION = 21; + private static final int MIN_SCHEMA_VERSION = 21; private static final String CREATE_SETTINGS = "CREATE TABLE settings" @@ -82,7 +80,6 @@ abstract class JdbcDatabase implements Database { + " publicKey BINARY NOT NULL," + " privateKey BINARY NOT NULL," + " created BIGINT NOT NULL," - + " status INT NOT NULL," + " PRIMARY KEY (authorId))"; private static final String CREATE_CONTACTS = @@ -92,7 +89,6 @@ abstract class JdbcDatabase implements Database { + " name VARCHAR NOT NULL," + " publicKey BINARY NOT NULL," + " localAuthorId HASH NOT NULL," - + " status INT NOT NULL," + " PRIMARY KEY (contactId)," + " FOREIGN KEY (localAuthorId)" + " REFERENCES localAuthors (authorId)" @@ -452,14 +448,13 @@ abstract class JdbcDatabase implements Database { try { // Create a contact row String sql = "INSERT INTO contacts" - + " (authorId, name, publicKey, localAuthorId, status)" - + " VALUES (?, ?, ?, ?, ?)"; + + " (authorId, name, publicKey, localAuthorId)" + + " VALUES (?, ?, ?, ?)"; ps = txn.prepareStatement(sql); ps.setBytes(1, remote.getId().getBytes()); ps.setString(2, remote.getName()); ps.setBytes(3, remote.getPublicKey()); ps.setBytes(4, local.getBytes()); - ps.setInt(5, ADDING.getValue()); int affected = ps.executeUpdate(); if (affected != 1) throw new DbStateException(); ps.close(); @@ -528,16 +523,15 @@ abstract class JdbcDatabase implements Database { throws DbException { PreparedStatement ps = null; try { - String sql = "INSERT INTO localAuthors (authorId, name, publicKey," - + " privateKey, created, status)" - + " VALUES (?, ?, ?, ?, ?, ?)"; + String sql = "INSERT INTO localAuthors" + + " (authorId, name, publicKey, privateKey, created)" + + " VALUES (?, ?, ?, ?, ?)"; ps = txn.prepareStatement(sql); ps.setBytes(1, a.getId().getBytes()); ps.setString(2, a.getName()); ps.setBytes(3, a.getPublicKey()); ps.setBytes(4, a.getPrivateKey()); ps.setLong(5, a.getTimeCreated()); - ps.setInt(6, a.getStatus().getValue()); int affected = ps.executeUpdate(); if (affected != 1) throw new DbStateException(); ps.close(); @@ -957,8 +951,7 @@ abstract class JdbcDatabase implements Database { PreparedStatement ps = null; ResultSet rs = null; try { - String sql = "SELECT authorId, name, publicKey, localAuthorId," - + " status" + String sql = "SELECT authorId, name, publicKey, localAuthorId" + " FROM contacts" + " WHERE contactId = ?"; ps = txn.prepareStatement(sql); @@ -969,11 +962,10 @@ abstract class JdbcDatabase implements Database { String name = rs.getString(2); byte[] publicKey = rs.getBytes(3); AuthorId localAuthorId = new AuthorId(rs.getBytes(4)); - StorageStatus status = StorageStatus.fromValue(rs.getInt(5)); rs.close(); ps.close(); Author author = new Author(authorId, name, publicKey); - return new Contact(c, author, localAuthorId, status); + return new Contact(c, author, localAuthorId); } catch (SQLException e) { tryToClose(rs); tryToClose(ps); @@ -1007,7 +999,7 @@ abstract class JdbcDatabase implements Database { ResultSet rs = null; try { String sql = "SELECT contactId, authorId, name, publicKey," - + " localAuthorId, status" + + " localAuthorId" + " FROM contacts"; ps = txn.prepareStatement(sql); rs = ps.executeQuery(); @@ -1019,9 +1011,7 @@ abstract class JdbcDatabase implements Database { byte[] publicKey = rs.getBytes(4); Author author = new Author(authorId, name, publicKey); AuthorId localAuthorId = new AuthorId(rs.getBytes(5)); - StorageStatus status = StorageStatus.fromValue(rs.getInt(6)); - contacts.add(new Contact(contactId, author, localAuthorId, - status)); + contacts.add(new Contact(contactId, author, localAuthorId)); } rs.close(); ps.close(); @@ -1108,7 +1098,7 @@ abstract class JdbcDatabase implements Database { PreparedStatement ps = null; ResultSet rs = null; try { - String sql = "SELECT name, publicKey, privateKey, created, status" + String sql = "SELECT name, publicKey, privateKey, created" + " FROM localAuthors" + " WHERE authorId = ?"; ps = txn.prepareStatement(sql); @@ -1119,9 +1109,8 @@ abstract class JdbcDatabase implements Database { byte[] publicKey = rs.getBytes(2); byte[] privateKey = rs.getBytes(3); long created = rs.getLong(4); - StorageStatus status = StorageStatus.fromValue(rs.getInt(5)); LocalAuthor localAuthor = new LocalAuthor(a, name, publicKey, - privateKey, created, status); + privateKey, created); if (rs.next()) throw new DbStateException(); rs.close(); ps.close(); @@ -1138,8 +1127,7 @@ abstract class JdbcDatabase implements Database { PreparedStatement ps = null; ResultSet rs = null; try { - String sql = "SELECT authorId, name, publicKey, privateKey," - + " created, status" + String sql = "SELECT authorId, name, publicKey, privateKey, created" + " FROM localAuthors"; ps = txn.prepareStatement(sql); rs = ps.executeQuery(); @@ -1150,9 +1138,8 @@ abstract class JdbcDatabase implements Database { byte[] publicKey = rs.getBytes(3); byte[] privateKey = rs.getBytes(4); long created = rs.getLong(5); - StorageStatus status = StorageStatus.fromValue(rs.getInt(6)); authors.add(new LocalAuthor(authorId, name, publicKey, - privateKey, created, status)); + privateKey, created)); } rs.close(); ps.close(); @@ -2041,41 +2028,6 @@ abstract class JdbcDatabase implements Database { } } - public void setContactStatus(Connection txn, ContactId c, StorageStatus s) - throws DbException { - PreparedStatement ps = null; - try { - String sql = "UPDATE contacts SET status = ? WHERE contactId = ?"; - ps = txn.prepareStatement(sql); - ps.setInt(1, s.getValue()); - ps.setInt(2, c.getInt()); - int affected = ps.executeUpdate(); - if (affected < 0 || affected > 1) throw new DbStateException(); - ps.close(); - } catch (SQLException e) { - tryToClose(ps); - throw new DbException(e); - } - } - - public void setLocalAuthorStatus(Connection txn, AuthorId a, - StorageStatus s) throws DbException { - PreparedStatement ps = null; - try { - String sql = "UPDATE localAuthors SET status = ?" - + " WHERE authorId = ?"; - ps = txn.prepareStatement(sql); - ps.setInt(1, s.getValue()); - ps.setBytes(2, a.getBytes()); - int affected = ps.executeUpdate(); - if (affected < 0 || affected > 1) throw new DbStateException(); - ps.close(); - } catch (SQLException e) { - tryToClose(ps); - throw new DbException(e); - } - } - public void setMessageShared(Connection txn, MessageId m, boolean shared) throws DbException { PreparedStatement ps = null; diff --git a/briar-core/src/org/briarproject/identity/AuthorFactoryImpl.java b/briar-core/src/org/briarproject/identity/AuthorFactoryImpl.java index 87c03a599..b745f5e00 100644 --- a/briar-core/src/org/briarproject/identity/AuthorFactoryImpl.java +++ b/briar-core/src/org/briarproject/identity/AuthorFactoryImpl.java @@ -14,8 +14,6 @@ import java.io.IOException; import javax.inject.Inject; -import static org.briarproject.api.db.StorageStatus.ADDING; - class AuthorFactoryImpl implements AuthorFactory { private final CryptoComponent crypto; @@ -37,7 +35,7 @@ class AuthorFactoryImpl implements AuthorFactory { public LocalAuthor createLocalAuthor(String name, byte[] publicKey, byte[] privateKey) { return new LocalAuthor(getId(name, publicKey), name, publicKey, - privateKey, clock.currentTimeMillis(), ADDING); + privateKey, clock.currentTimeMillis()); } private AuthorId getId(String name, byte[] publicKey) { diff --git a/briar-core/src/org/briarproject/identity/IdentityManagerImpl.java b/briar-core/src/org/briarproject/identity/IdentityManagerImpl.java index 49929799b..0ae2e5aa5 100644 --- a/briar-core/src/org/briarproject/identity/IdentityManagerImpl.java +++ b/briar-core/src/org/briarproject/identity/IdentityManagerImpl.java @@ -4,21 +4,15 @@ 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.identity.AuthorId; import org.briarproject.api.identity.IdentityManager; import org.briarproject.api.identity.LocalAuthor; -import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; -import static org.briarproject.api.db.StorageStatus.ACTIVE; -import static org.briarproject.api.db.StorageStatus.REMOVING; - class IdentityManagerImpl implements IdentityManager { private final DatabaseComponent db; @@ -49,7 +43,6 @@ class IdentityManagerImpl implements IdentityManager { db.addLocalAuthor(txn, localAuthor); for (AddIdentityHook hook : addHooks) hook.addingIdentity(txn, localAuthor); - db.setLocalAuthorStatus(txn, localAuthor.getId(), ACTIVE); txn.setComplete(); } finally { db.endTransaction(txn); @@ -66,8 +59,7 @@ class IdentityManagerImpl implements IdentityManager { } finally { db.endTransaction(txn); } - if (author.getStatus().equals(ACTIVE)) return author; - throw new NoSuchLocalAuthorException(); + return author; } @Override @@ -80,11 +72,7 @@ class IdentityManagerImpl implements IdentityManager { } finally { db.endTransaction(txn); } - // Filter out any pseudonyms that are being added or removed - List active = new ArrayList(authors.size()); - for (LocalAuthor a : authors) - if (a.getStatus().equals(ACTIVE)) active.add(a); - return Collections.unmodifiableList(active); + return authors; } @Override @@ -92,7 +80,6 @@ class IdentityManagerImpl implements IdentityManager { 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); diff --git a/briar-tests/src/org/briarproject/db/DatabaseComponentImplTest.java b/briar-tests/src/org/briarproject/db/DatabaseComponentImplTest.java index a56542d38..b98155075 100644 --- a/briar-tests/src/org/briarproject/db/DatabaseComponentImplTest.java +++ b/briar-tests/src/org/briarproject/db/DatabaseComponentImplTest.java @@ -13,7 +13,6 @@ import org.briarproject.api.db.NoSuchGroupException; 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.ContactAddedEvent; import org.briarproject.api.event.ContactRemovedEvent; @@ -97,8 +96,7 @@ public class DatabaseComponentImplTest extends BriarTestCase { localAuthorId = new AuthorId(TestUtils.getRandomId()); long timestamp = System.currentTimeMillis(); localAuthor = new LocalAuthor(localAuthorId, "Bob", - new byte[MAX_PUBLIC_KEY_LENGTH], new byte[123], timestamp, - StorageStatus.ACTIVE); + new byte[MAX_PUBLIC_KEY_LENGTH], new byte[123], timestamp); messageId = new MessageId(TestUtils.getRandomId()); messageId1 = new MessageId(TestUtils.getRandomId()); size = 1234; @@ -109,8 +107,7 @@ public class DatabaseComponentImplTest extends BriarTestCase { transportId = new TransportId("id"); maxLatency = Integer.MAX_VALUE; contactId = new ContactId(234); - contact = new Contact(contactId, author, localAuthorId, - StorageStatus.ACTIVE); + contact = new Contact(contactId, author, localAuthorId); } private DatabaseComponent createDatabaseComponent(Database database, diff --git a/briar-tests/src/org/briarproject/db/H2DatabaseTest.java b/briar-tests/src/org/briarproject/db/H2DatabaseTest.java index 1e880356b..34a91d63c 100644 --- a/briar-tests/src/org/briarproject/db/H2DatabaseTest.java +++ b/briar-tests/src/org/briarproject/db/H2DatabaseTest.java @@ -8,7 +8,6 @@ import org.briarproject.api.contact.ContactId; import org.briarproject.api.crypto.SecretKey; import org.briarproject.api.db.DbException; import org.briarproject.api.db.Metadata; -import org.briarproject.api.db.StorageStatus; import org.briarproject.api.identity.Author; import org.briarproject.api.identity.AuthorId; import org.briarproject.api.identity.LocalAuthor; @@ -87,8 +86,7 @@ public class H2DatabaseTest extends BriarTestCase { localAuthorId = new AuthorId(TestUtils.getRandomId()); timestamp = System.currentTimeMillis(); localAuthor = new LocalAuthor(localAuthorId, "Bob", - new byte[MAX_PUBLIC_KEY_LENGTH], new byte[123], timestamp, - StorageStatus.ACTIVE); + new byte[MAX_PUBLIC_KEY_LENGTH], new byte[123], timestamp); messageId = new MessageId(TestUtils.getRandomId()); size = 1234; raw = new byte[size]; @@ -1060,8 +1058,7 @@ public class H2DatabaseTest extends BriarTestCase { throws Exception { AuthorId localAuthorId1 = new AuthorId(TestUtils.getRandomId()); LocalAuthor localAuthor1 = new LocalAuthor(localAuthorId1, "Carol", - new byte[MAX_PUBLIC_KEY_LENGTH], new byte[123], timestamp, - StorageStatus.ACTIVE); + new byte[MAX_PUBLIC_KEY_LENGTH], new byte[123], timestamp); Database db = open(false); Connection txn = db.startTransaction(); diff --git a/briar-tests/src/org/briarproject/sync/SimplexMessagingIntegrationTest.java b/briar-tests/src/org/briarproject/sync/SimplexMessagingIntegrationTest.java index bfb815c2d..2c3cc4348 100644 --- a/briar-tests/src/org/briarproject/sync/SimplexMessagingIntegrationTest.java +++ b/briar-tests/src/org/briarproject/sync/SimplexMessagingIntegrationTest.java @@ -12,7 +12,6 @@ import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactManager; import org.briarproject.api.crypto.SecretKey; import org.briarproject.api.db.DatabaseComponent; -import org.briarproject.api.db.StorageStatus; import org.briarproject.api.db.Transaction; import org.briarproject.api.event.Event; import org.briarproject.api.event.EventBus; @@ -130,8 +129,7 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase { } // Add an identity for Alice LocalAuthor aliceAuthor = new LocalAuthor(aliceId, "Alice", - new byte[MAX_PUBLIC_KEY_LENGTH], new byte[123], timestamp, - StorageStatus.ADDING); + new byte[MAX_PUBLIC_KEY_LENGTH], new byte[123], timestamp); identityManager.addLocalAuthor(aliceAuthor); // Add Bob as a contact Author bobAuthor = new Author(bobId, "Bob", @@ -201,8 +199,7 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase { } // Add an identity for Bob LocalAuthor bobAuthor = new LocalAuthor(bobId, "Bob", - new byte[MAX_PUBLIC_KEY_LENGTH], new byte[123], timestamp, - StorageStatus.ADDING); + new byte[MAX_PUBLIC_KEY_LENGTH], new byte[123], timestamp); identityManager.addLocalAuthor(bobAuthor); // Add Alice as a contact Author aliceAuthor = new Author(aliceId, "Alice", From 9f8baab60fd54b8a0dab792779f8ae576419844e Mon Sep 17 00:00:00 2001 From: akwizgran Date: Thu, 11 Feb 2016 15:30:15 +0000 Subject: [PATCH 10/11] Code cleanup. --- .../db/DatabaseComponentImpl.java | 43 +++++++-------- .../forum/ForumSharingManagerImpl.java | 5 +- .../messaging/MessagingManagerImpl.java | 2 +- .../TransportPropertyManagerImpl.java | 53 +++++++++++-------- .../settings/SettingsManagerImpl.java | 5 +- .../db/DatabaseComponentImplTest.java | 31 ++++++----- 6 files changed, 71 insertions(+), 68 deletions(-) diff --git a/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java b/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java index 9a271f7ab..8ed1ece78 100644 --- a/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java +++ b/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java @@ -245,25 +245,22 @@ class DatabaseComponentImpl implements DatabaseComponent { public Ack generateAck(Transaction transaction, ContactId c, int maxMessages) throws DbException { - Collection ids; T txn = unbox(transaction); if (!db.containsContact(txn, c)) throw new NoSuchContactException(); - ids = db.getMessagesToAck(txn, c, maxMessages); - if (!ids.isEmpty()) db.lowerAckFlag(txn, c, ids); + Collection ids = db.getMessagesToAck(txn, c, maxMessages); if (ids.isEmpty()) return null; + db.lowerAckFlag(txn, c, ids); return new Ack(ids); } public Collection generateBatch(Transaction transaction, ContactId c, int maxLength, int maxLatency) throws DbException { - Collection ids; - List messages; T txn = unbox(transaction); if (!db.containsContact(txn, c)) throw new NoSuchContactException(); - ids = db.getMessagesToSend(txn, c, maxLength); - messages = new ArrayList(ids.size()); + Collection ids = db.getMessagesToSend(txn, c, maxLength); + List messages = new ArrayList(ids.size()); for (MessageId m : ids) { messages.add(db.getRawMessage(txn, m)); db.updateExpiryTime(txn, c, m, maxLatency); @@ -276,37 +273,35 @@ class DatabaseComponentImpl implements DatabaseComponent { public Offer generateOffer(Transaction transaction, ContactId c, int maxMessages, int maxLatency) throws DbException { - Collection ids; T txn = unbox(transaction); if (!db.containsContact(txn, c)) throw new NoSuchContactException(); - ids = db.getMessagesToOffer(txn, c, maxMessages); - for (MessageId m : ids) db.updateExpiryTime(txn, c, m, maxLatency); + Collection ids = db.getMessagesToOffer(txn, c, maxMessages); if (ids.isEmpty()) return null; + for (MessageId m : ids) db.updateExpiryTime(txn, c, m, maxLatency); return new Offer(ids); } public Request generateRequest(Transaction transaction, ContactId c, int maxMessages) throws DbException { - Collection ids; T txn = unbox(transaction); if (!db.containsContact(txn, c)) throw new NoSuchContactException(); - ids = db.getMessagesToRequest(txn, c, maxMessages); - if (!ids.isEmpty()) db.removeOfferedMessages(txn, c, ids); + Collection ids = db.getMessagesToRequest(txn, c, + maxMessages); if (ids.isEmpty()) return null; + db.removeOfferedMessages(txn, c, ids); return new Request(ids); } public Collection generateRequestedBatch(Transaction transaction, ContactId c, int maxLength, int maxLatency) throws DbException { - Collection ids; - List messages; T txn = unbox(transaction); if (!db.containsContact(txn, c)) throw new NoSuchContactException(); - ids = db.getRequestedMessagesToSend(txn, c, maxLength); - messages = new ArrayList(ids.size()); + Collection ids = db.getRequestedMessagesToSend(txn, c, + maxLength); + List messages = new ArrayList(ids.size()); for (MessageId m : ids) { messages.add(db.getRawMessage(txn, m)); db.updateExpiryTime(txn, c, m, maxLatency); @@ -501,10 +496,10 @@ class DatabaseComponentImpl implements DatabaseComponent { public void receiveAck(Transaction transaction, ContactId c, Ack a) throws DbException { - Collection acked = new ArrayList(); T txn = unbox(transaction); if (!db.containsContact(txn, c)) throw new NoSuchContactException(); + Collection acked = new ArrayList(); for (MessageId m : a.getMessageIds()) { if (db.containsVisibleMessage(txn, c, m)) { db.raiseSeenFlag(txn, c, m); @@ -531,11 +526,11 @@ class DatabaseComponentImpl implements DatabaseComponent { public void receiveOffer(Transaction transaction, ContactId c, Offer o) throws DbException { - boolean ack = false, request = false; T txn = unbox(transaction); if (!db.containsContact(txn, c)) throw new NoSuchContactException(); int count = db.countOfferedMessages(txn, c); + boolean ack = false, request = false; for (MessageId m : o.getMessageIds()) { if (db.containsVisibleMessage(txn, c, m)) { db.raiseSeenFlag(txn, c, m); @@ -553,10 +548,10 @@ class DatabaseComponentImpl implements DatabaseComponent { public void receiveRequest(Transaction transaction, ContactId c, Request r) throws DbException { - boolean requested = false; T txn = unbox(transaction); if (!db.containsContact(txn, c)) throw new NoSuchContactException(); + boolean requested = false; for (MessageId m : r.getMessageIds()) { if (db.containsVisibleMessage(txn, c, m)) { db.raiseRequestedFlag(txn, c, m); @@ -578,12 +573,11 @@ class DatabaseComponentImpl implements DatabaseComponent { public void removeGroup(Transaction transaction, Group g) throws DbException { - Collection affected; T txn = unbox(transaction); GroupId id = g.getId(); if (!db.containsGroup(txn, id)) throw new NoSuchGroupException(); - affected = db.getVisibility(txn, id); + Collection affected = db.getVisibility(txn, id); db.removeGroup(txn, id); transaction.attach(new GroupRemovedEvent(g)); transaction.attach(new GroupVisibilityUpdatedEvent(affected)); @@ -638,13 +632,12 @@ class DatabaseComponentImpl implements DatabaseComponent { public void setVisibleToContact(Transaction transaction, ContactId c, GroupId g, boolean visible) throws DbException { - boolean wasVisible; T txn = unbox(transaction); if (!db.containsContact(txn, c)) throw new NoSuchContactException(); if (!db.containsGroup(txn, g)) throw new NoSuchGroupException(); - wasVisible = db.containsVisibleGroup(txn, c, g); + boolean wasVisible = db.containsVisibleGroup(txn, c, g); if (visible && !wasVisible) db.addVisibility(txn, c, g); else if (!visible && wasVisible) db.removeVisibility(txn, c, g); if (visible != wasVisible) { @@ -655,9 +648,9 @@ class DatabaseComponentImpl implements DatabaseComponent { public void updateTransportKeys(Transaction transaction, Map keys) throws DbException { + T txn = unbox(transaction); Map filtered = new HashMap(); - T txn = unbox(transaction); for (Entry e : keys.entrySet()) { ContactId c = e.getKey(); TransportKeys k = e.getValue(); diff --git a/briar-core/src/org/briarproject/forum/ForumSharingManagerImpl.java b/briar-core/src/org/briarproject/forum/ForumSharingManagerImpl.java index 6f8554ce8..c3ab558de 100644 --- a/briar-core/src/org/briarproject/forum/ForumSharingManagerImpl.java +++ b/briar-core/src/org/briarproject/forum/ForumSharingManagerImpl.java @@ -45,7 +45,6 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import java.util.logging.Logger; import static org.briarproject.api.forum.ForumConstants.FORUM_SALT_LENGTH; import static org.briarproject.api.forum.ForumConstants.MAX_FORUM_NAME_LENGTH; @@ -376,8 +375,8 @@ class ForumSharingManagerImpl implements ForumSharingManager, AddContactHook, BdfDictionary d = new BdfDictionary(); d.put("version", version); d.put("local", true); - db.addLocalMessage(txn, m, CLIENT_ID, metadataEncoder.encode(d), - true); + Metadata meta = metadataEncoder.encode(d); + db.addLocalMessage(txn, m, CLIENT_ID, meta, true); } catch (FormatException e) { throw new RuntimeException(e); } diff --git a/briar-core/src/org/briarproject/messaging/MessagingManagerImpl.java b/briar-core/src/org/briarproject/messaging/MessagingManagerImpl.java index 0441d27ce..ef566f2b1 100644 --- a/briar-core/src/org/briarproject/messaging/MessagingManagerImpl.java +++ b/briar-core/src/org/briarproject/messaging/MessagingManagerImpl.java @@ -154,11 +154,11 @@ class MessagingManagerImpl implements MessagingManager, AddContactHook, @Override public Collection getMessageHeaders(ContactId c) throws DbException { - GroupId g = getConversationId(c); Map metadata; Collection statuses; Transaction txn = db.startTransaction(); try { + GroupId g = getContactGroup(db.getContact(txn, c)).getId(); metadata = db.getMessageMetadata(txn, g); statuses = db.getMessageStatus(txn, c, g); txn.setComplete(); diff --git a/briar-core/src/org/briarproject/properties/TransportPropertyManagerImpl.java b/briar-core/src/org/briarproject/properties/TransportPropertyManagerImpl.java index 1032717f5..b3db2611c 100644 --- a/briar-core/src/org/briarproject/properties/TransportPropertyManagerImpl.java +++ b/briar-core/src/org/briarproject/properties/TransportPropertyManagerImpl.java @@ -91,7 +91,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager, db.setVisibleToContact(txn, c.getId(), g.getId(), true); // Copy the latest local properties into the group DeviceId dev = db.getDeviceId(txn); - Map local = getLocalProperties(); + Map local = getLocalProperties(txn); for (Entry e : local.entrySet()) { storeMessage(txn, g.getId(), dev, e.getKey(), e.getValue(), 1, true, true); @@ -122,30 +122,15 @@ class TransportPropertyManagerImpl implements TransportPropertyManager, @Override public Map getLocalProperties() throws DbException { + Map local; + Transaction txn = db.startTransaction(); try { - Map local = - new HashMap(); - Transaction txn = db.startTransaction(); - try { - // Find the latest local update for each transport - Map latest = findLatest(txn, - localGroup.getId(), true); - // Retrieve and parse the latest local properties - for (Entry 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 (FormatException e) { - throw new DbException(e); + local = getLocalProperties(txn); + txn.setComplete(); + } finally { + db.endTransaction(txn); } + return Collections.unmodifiableMap(local); } @Override @@ -255,6 +240,28 @@ class TransportPropertyManagerImpl implements TransportPropertyManager, return privateGroupFactory.createPrivateGroup(CLIENT_ID, c); } + private Map getLocalProperties( + Transaction txn) throws DbException { + try { + Map local = + new HashMap(); + // Find the latest local update for each transport + Map latest = findLatest(txn, + localGroup.getId(), true); + // Retrieve and parse the latest local properties + for (Entry e : latest.entrySet()) { + byte[] raw = db.getRawMessage(txn, e.getValue().messageId); + local.put(e.getKey(), parseProperties(raw)); + } + return local; + } catch (NoSuchGroupException e) { + // Local group doesn't exist - there are no local properties + return Collections.emptyMap(); + } catch (FormatException e) { + throw new DbException(e); + } + } + private void storeMessage(Transaction txn, GroupId g, DeviceId dev, TransportId t, TransportProperties p, long version, boolean local, boolean shared) throws DbException { diff --git a/briar-core/src/org/briarproject/settings/SettingsManagerImpl.java b/briar-core/src/org/briarproject/settings/SettingsManagerImpl.java index 639d822bb..eb929aa09 100644 --- a/briar-core/src/org/briarproject/settings/SettingsManagerImpl.java +++ b/briar-core/src/org/briarproject/settings/SettingsManagerImpl.java @@ -19,14 +19,15 @@ class SettingsManagerImpl implements SettingsManager { @Override public Settings getSettings(String namespace) throws DbException { + Settings s; Transaction txn = db.startTransaction(); try { - Settings s = db.getSettings(txn, namespace); + s = db.getSettings(txn, namespace); txn.setComplete(); - return s; } finally { db.endTransaction(txn); } + return s; } @Override diff --git a/briar-tests/src/org/briarproject/db/DatabaseComponentImplTest.java b/briar-tests/src/org/briarproject/db/DatabaseComponentImplTest.java index b98155075..3488bb2d1 100644 --- a/briar-tests/src/org/briarproject/db/DatabaseComponentImplTest.java +++ b/briar-tests/src/org/briarproject/db/DatabaseComponentImplTest.java @@ -190,20 +190,23 @@ public class DatabaseComponentImplTest extends BriarTestCase { assertFalse(db.open()); 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); + try { + 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(); + } finally { + db.endTransaction(transaction); + } db.close(); context.assertIsSatisfied(); From 00e59613c07ee59fbb8f35d67ddc0e72ffba330d Mon Sep 17 00:00:00 2001 From: akwizgran Date: Wed, 17 Feb 2016 16:51:44 +0000 Subject: [PATCH 11/11] Call validation hooks after storing message & metadata. --- .../src/org/briarproject/sync/ValidationManagerImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/briar-core/src/org/briarproject/sync/ValidationManagerImpl.java b/briar-core/src/org/briarproject/sync/ValidationManagerImpl.java index e075f341f..85f383bae 100644 --- a/briar-core/src/org/briarproject/sync/ValidationManagerImpl.java +++ b/briar-core/src/org/briarproject/sync/ValidationManagerImpl.java @@ -135,11 +135,11 @@ class ValidationManagerImpl implements ValidationManager, Service, 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); + for (ValidationHook hook : hooks) + hook.validatingMessage(txn, m, c, meta); } txn.setComplete(); } finally {