diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/db/DatabaseComponent.java b/bramble-api/src/main/java/org/briarproject/bramble/api/db/DatabaseComponent.java index aec77c5aa..3fff5f9d6 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/db/DatabaseComponent.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/db/DatabaseComponent.java @@ -76,6 +76,19 @@ public interface DatabaseComponent { */ void endTransaction(Transaction txn); + /** + * Runs the given task within a transaction. + */ + void transaction(boolean readOnly, + DbRunnable task) throws DbException, E; + + /** + * Runs the given task within a transaction and returns the result of the + * task. + */ + R transactionWithResult(boolean readOnly, + DbCallable task) throws DbException, E; + /** * Stores a contact associated with the given local and remote pseudonyms, * and returns an ID for the contact. diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/db/DbCallable.java b/bramble-api/src/main/java/org/briarproject/bramble/api/db/DbCallable.java new file mode 100644 index 000000000..d90a2e9e4 --- /dev/null +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/db/DbCallable.java @@ -0,0 +1,6 @@ +package org.briarproject.bramble.api.db; + +public interface DbCallable { + + R call(Transaction txn) throws DbException, E; +} diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/db/DbRunnable.java b/bramble-api/src/main/java/org/briarproject/bramble/api/db/DbRunnable.java new file mode 100644 index 000000000..64d36143c --- /dev/null +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/db/DbRunnable.java @@ -0,0 +1,6 @@ +package org.briarproject.bramble.api.db; + +public interface DbRunnable { + + void run(Transaction txn) throws DbException, E; +} diff --git a/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseComponentImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseComponentImpl.java index 99f9e7c10..829433ba5 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseComponentImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseComponentImpl.java @@ -9,7 +9,9 @@ import org.briarproject.bramble.api.contact.event.ContactVerifiedEvent; import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.db.ContactExistsException; import org.briarproject.bramble.api.db.DatabaseComponent; +import org.briarproject.bramble.api.db.DbCallable; import org.briarproject.bramble.api.db.DbException; +import org.briarproject.bramble.api.db.DbRunnable; import org.briarproject.bramble.api.db.Metadata; import org.briarproject.bramble.api.db.MigrationListener; import org.briarproject.bramble.api.db.NoSuchContactException; @@ -166,6 +168,31 @@ class DatabaseComponentImpl implements DatabaseComponent { for (Event e : transaction.getEvents()) eventBus.broadcast(e); } + @Override + public void transaction(boolean readOnly, + DbRunnable task) throws DbException, E { + Transaction txn = startTransaction(readOnly); + try { + task.run(txn); + commitTransaction(txn); + } finally { + endTransaction(txn); + } + } + + @Override + public R transactionWithResult(boolean readOnly, + DbCallable task) throws DbException, E { + Transaction txn = startTransaction(readOnly); + try { + R result = task.call(txn); + commitTransaction(txn); + return result; + } finally { + endTransaction(txn); + } + } + private T unbox(Transaction transaction) { if (transaction.isCommitted()) throw new IllegalStateException(); return txnClass.cast(transaction.unbox()); diff --git a/briar-core/src/main/java/org/briarproject/briar/forum/ForumManagerImpl.java b/briar-core/src/main/java/org/briarproject/briar/forum/ForumManagerImpl.java index 58b35ea65..b60e7c94b 100644 --- a/briar-core/src/main/java/org/briarproject/briar/forum/ForumManagerImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/forum/ForumManagerImpl.java @@ -94,14 +94,7 @@ class ForumManagerImpl extends BdfIncomingMessageHook implements ForumManager { @Override public Forum addForum(String name) throws DbException { Forum f = forumFactory.createForum(name); - - Transaction txn = db.startTransaction(false); - try { - db.addGroup(txn, f.getGroup()); - db.commitTransaction(txn); - } finally { - db.endTransaction(txn); - } + db.transaction(false, txn -> db.addGroup(txn, f.getGroup())); return f; } @@ -112,15 +105,11 @@ class ForumManagerImpl extends BdfIncomingMessageHook implements ForumManager { @Override public void removeForum(Forum f) throws DbException { - Transaction txn = db.startTransaction(false); - try { + db.transaction(false, txn -> { for (RemoveForumHook hook : removeHooks) hook.removingForum(txn, f); db.removeGroup(txn, f.getGroup()); - db.commitTransaction(txn); - } finally { - db.endTransaction(txn); - } + }); } @Override @@ -131,45 +120,35 @@ class ForumManagerImpl extends BdfIncomingMessageHook implements ForumManager { p = forumPostFactory.createPost(groupId, timestamp, parentId, author, body); } catch (GeneralSecurityException | FormatException e) { - throw new RuntimeException(e); + throw new AssertionError(e); } return p; } @Override public ForumPostHeader addLocalPost(ForumPost p) throws DbException { - Transaction txn = db.startTransaction(false); - try { - BdfDictionary meta = new BdfDictionary(); - meta.put(KEY_TIMESTAMP, p.getMessage().getTimestamp()); - if (p.getParent() != null) meta.put(KEY_PARENT, p.getParent()); - Author a = p.getAuthor(); - meta.put(KEY_AUTHOR, clientHelper.toList(a)); - meta.put(KEY_LOCAL, true); - meta.put(MSG_KEY_READ, true); - clientHelper.addLocalMessage(txn, p.getMessage(), meta, true); - messageTracker.trackOutgoingMessage(txn, p.getMessage()); - db.commitTransaction(txn); - } catch (FormatException e) { - throw new RuntimeException(e); - } finally { - db.endTransaction(txn); - } + db.transaction(false, txn -> { + try { + BdfDictionary meta = new BdfDictionary(); + meta.put(KEY_TIMESTAMP, p.getMessage().getTimestamp()); + if (p.getParent() != null) meta.put(KEY_PARENT, p.getParent()); + Author a = p.getAuthor(); + meta.put(KEY_AUTHOR, clientHelper.toList(a)); + meta.put(KEY_LOCAL, true); + meta.put(MSG_KEY_READ, true); + clientHelper.addLocalMessage(txn, p.getMessage(), meta, true); + messageTracker.trackOutgoingMessage(txn, p.getMessage()); + } catch (FormatException e) { + throw new AssertionError(e); + } + }); return new ForumPostHeader(p.getMessage().getId(), p.getParent(), p.getMessage().getTimestamp(), p.getAuthor(), OURSELVES, true); } @Override public Forum getForum(GroupId g) throws DbException { - Forum forum; - Transaction txn = db.startTransaction(true); - try { - forum = getForum(txn, g); - db.commitTransaction(txn); - } finally { - db.endTransaction(txn); - } - return forum; + return db.transactionWithResult(true, txn -> getForum(txn, g)); } @Override @@ -184,15 +163,9 @@ class ForumManagerImpl extends BdfIncomingMessageHook implements ForumManager { @Override public Collection getForums() throws DbException { + Collection groups = db.transactionWithResult(true, txn -> + db.getGroups(txn, CLIENT_ID, MAJOR_VERSION)); try { - Collection groups; - Transaction txn = db.startTransaction(true); - try { - groups = db.getGroups(txn, CLIENT_ID, MAJOR_VERSION); - db.commitTransaction(txn); - } finally { - db.endTransaction(txn); - } List forums = new ArrayList<>(); for (Group g : groups) forums.add(parseForum(g)); return forums; @@ -218,36 +191,35 @@ class ForumManagerImpl extends BdfIncomingMessageHook implements ForumManager { @Override public Collection getPostHeaders(GroupId g) throws DbException { - - Collection headers = new ArrayList<>(); - Transaction txn = db.startTransaction(true); try { - Map metadata = - clientHelper.getMessageMetadataAsDictionary(txn, g); - // get all authors we need to get the status for - Set authors = new HashSet<>(); - for (Entry entry : metadata.entrySet()) { - BdfList authorList = entry.getValue().getList(KEY_AUTHOR); - Author a = clientHelper.parseAndValidateAuthor(authorList); - authors.add(a.getId()); - } - // get statuses for all authors - Map statuses = new HashMap<>(); - for (AuthorId id : authors) { - statuses.put(id, identityManager.getAuthorStatus(txn, id)); - } - // Parse the metadata - for (Entry entry : metadata.entrySet()) { - BdfDictionary meta = entry.getValue(); - headers.add(getForumPostHeader(txn, entry.getKey(), meta, - statuses)); - } - db.commitTransaction(txn); - return headers; + return db.transactionWithResult(true, txn -> { + Collection headers = new ArrayList<>(); + Map metadata = + clientHelper.getMessageMetadataAsDictionary(txn, g); + // get all authors we need to get the status for + Set authors = new HashSet<>(); + for (Entry entry : + metadata.entrySet()) { + BdfList authorList = entry.getValue().getList(KEY_AUTHOR); + Author a = clientHelper.parseAndValidateAuthor(authorList); + authors.add(a.getId()); + } + // get statuses for all authors + Map statuses = new HashMap<>(); + for (AuthorId id : authors) { + statuses.put(id, identityManager.getAuthorStatus(txn, id)); + } + // Parse the metadata + for (Entry entry : + metadata.entrySet()) { + BdfDictionary meta = entry.getValue(); + headers.add(getForumPostHeader(txn, entry.getKey(), meta, + statuses)); + } + return headers; + }); } catch (FormatException e) { throw new DbException(e); - } finally { - db.endTransaction(txn); } }