From 6d91603bf70b885065b479b1310304126822b56f Mon Sep 17 00:00:00 2001 From: akwizgran <akwizgran@users.sourceforge.net> Date: Wed, 26 Oct 2011 17:07:09 +0100 Subject: [PATCH] Moved MessageHeader into DB component and added read/starred flags. --- .../sf/briar/api/db/DatabaseComponent.java | 1 - .../api/{protocol => db}/MessageHeader.java | 12 ++++- api/net/sf/briar/api/protocol/Message.java | 23 ++++++++- .../api/protocol/MessageHeaderFactory.java | 7 --- components/net/sf/briar/db/Database.java | 4 +- .../sf/briar/db/DatabaseComponentImpl.java | 23 +++++---- .../net/sf/briar/db/DatabaseModule.java | 6 +-- components/net/sf/briar/db/H2Database.java | 6 +-- components/net/sf/briar/db/JdbcDatabase.java | 22 ++++----- .../{protocol => db}/MessageHeaderImpl.java | 18 +++++-- .../protocol/MessageHeaderFactoryImpl.java | 16 ------ .../net/sf/briar/protocol/ProtocolModule.java | 2 - .../sf/briar/db/DatabaseComponentTest.java | 2 +- test/net/sf/briar/db/H2DatabaseTest.java | 49 +++++++++++-------- 14 files changed, 108 insertions(+), 83 deletions(-) rename api/net/sf/briar/api/{protocol => db}/MessageHeader.java (63%) delete mode 100644 api/net/sf/briar/api/protocol/MessageHeaderFactory.java rename components/net/sf/briar/{protocol => db}/MessageHeaderImpl.java (71%) delete mode 100644 components/net/sf/briar/protocol/MessageHeaderFactoryImpl.java diff --git a/api/net/sf/briar/api/db/DatabaseComponent.java b/api/net/sf/briar/api/db/DatabaseComponent.java index 01d2253b95..2f5c3321f0 100644 --- a/api/net/sf/briar/api/db/DatabaseComponent.java +++ b/api/net/sf/briar/api/db/DatabaseComponent.java @@ -16,7 +16,6 @@ import net.sf.briar.api.protocol.Batch; import net.sf.briar.api.protocol.Group; import net.sf.briar.api.protocol.GroupId; import net.sf.briar.api.protocol.Message; -import net.sf.briar.api.protocol.MessageHeader; import net.sf.briar.api.protocol.MessageId; import net.sf.briar.api.protocol.Offer; import net.sf.briar.api.protocol.SubscriptionUpdate; diff --git a/api/net/sf/briar/api/protocol/MessageHeader.java b/api/net/sf/briar/api/db/MessageHeader.java similarity index 63% rename from api/net/sf/briar/api/protocol/MessageHeader.java rename to api/net/sf/briar/api/db/MessageHeader.java index 31ffc973cd..bbbcd85cf0 100644 --- a/api/net/sf/briar/api/protocol/MessageHeader.java +++ b/api/net/sf/briar/api/db/MessageHeader.java @@ -1,4 +1,8 @@ -package net.sf.briar.api.protocol; +package net.sf.briar.api.db; + +import net.sf.briar.api.protocol.AuthorId; +import net.sf.briar.api.protocol.GroupId; +import net.sf.briar.api.protocol.MessageId; public interface MessageHeader { @@ -22,4 +26,10 @@ public interface MessageHeader { /** Returns the timestamp created by the message's author. */ long getTimestamp(); + + /** Returns true if the message has been read. */ + boolean getRead(); + + /** Returns true if the message has been starred. */ + boolean getStarred(); } diff --git a/api/net/sf/briar/api/protocol/Message.java b/api/net/sf/briar/api/protocol/Message.java index 7a3506dc71..d204fd667f 100644 --- a/api/net/sf/briar/api/protocol/Message.java +++ b/api/net/sf/briar/api/protocol/Message.java @@ -1,6 +1,6 @@ package net.sf.briar.api.protocol; -public interface Message extends MessageHeader { +public interface Message { /** * The maximum length of a message body in bytes. To allow for future @@ -19,6 +19,27 @@ public interface Message extends MessageHeader { /** The length of the random salt in bytes. */ static final int SALT_LENGTH = 8; + /** Returns the message's unique identifier. */ + MessageId getId(); + + /** + * Returns the message's parent, or MessageId.NONE if this is the first + * message in a thread. + */ + MessageId getParent(); + + /** Returns the group to which the message belongs. */ + GroupId getGroup(); + + /** Returns the message's author. */ + AuthorId getAuthor(); + + /** Returns the message's subject line. */ + String getSubject(); + + /** Returns the timestamp created by the message's author. */ + long getTimestamp(); + /** Returns the length of the serialised message in bytes. */ int getLength(); diff --git a/api/net/sf/briar/api/protocol/MessageHeaderFactory.java b/api/net/sf/briar/api/protocol/MessageHeaderFactory.java deleted file mode 100644 index a9f51a46c5..0000000000 --- a/api/net/sf/briar/api/protocol/MessageHeaderFactory.java +++ /dev/null @@ -1,7 +0,0 @@ -package net.sf.briar.api.protocol; - -public interface MessageHeaderFactory { - - MessageHeader createMessageHeader(MessageId id, MessageId parent, - GroupId group, AuthorId author, String subject, long timestamp); -} diff --git a/components/net/sf/briar/db/Database.java b/components/net/sf/briar/db/Database.java index b6a980b67b..b65f99dbee 100644 --- a/components/net/sf/briar/db/Database.java +++ b/components/net/sf/briar/db/Database.java @@ -10,13 +10,13 @@ import net.sf.briar.api.TransportConfig; import net.sf.briar.api.TransportId; import net.sf.briar.api.TransportProperties; import net.sf.briar.api.db.DbException; +import net.sf.briar.api.db.MessageHeader; import net.sf.briar.api.db.Status; import net.sf.briar.api.protocol.AuthorId; import net.sf.briar.api.protocol.BatchId; import net.sf.briar.api.protocol.Group; import net.sf.briar.api.protocol.GroupId; import net.sf.briar.api.protocol.Message; -import net.sf.briar.api.protocol.MessageHeader; import net.sf.briar.api.protocol.MessageId; import net.sf.briar.api.transport.ConnectionWindow; @@ -257,7 +257,7 @@ interface Database<T> { /** * Returns the headers of all messages in the given group. * <p> - * Locking: message read. + * Locking: message read, messageFlag read. */ Collection<MessageHeader> getMessageHeaders(T txn, GroupId g) throws DbException; diff --git a/components/net/sf/briar/db/DatabaseComponentImpl.java b/components/net/sf/briar/db/DatabaseComponentImpl.java index 36c88cce8d..881a2c4bcc 100644 --- a/components/net/sf/briar/db/DatabaseComponentImpl.java +++ b/components/net/sf/briar/db/DatabaseComponentImpl.java @@ -27,6 +27,7 @@ import net.sf.briar.api.TransportId; import net.sf.briar.api.TransportProperties; import net.sf.briar.api.db.DatabaseComponent; import net.sf.briar.api.db.DbException; +import net.sf.briar.api.db.MessageHeader; import net.sf.briar.api.db.NoSuchContactException; import net.sf.briar.api.db.Status; import net.sf.briar.api.db.event.BatchReceivedEvent; @@ -45,7 +46,6 @@ import net.sf.briar.api.protocol.BatchId; import net.sf.briar.api.protocol.Group; import net.sf.briar.api.protocol.GroupId; import net.sf.briar.api.protocol.Message; -import net.sf.briar.api.protocol.MessageHeader; import net.sf.briar.api.protocol.MessageId; import net.sf.briar.api.protocol.Offer; import net.sf.briar.api.protocol.SubscriptionUpdate; @@ -789,15 +789,20 @@ DatabaseCleaner.Callback { throws DbException { messageLock.readLock().lock(); try { - T txn = db.startTransaction(); + messageFlagLock.readLock().lock(); try { - Collection<MessageHeader> headers = - db.getMessageHeaders(txn, g); - db.commitTransaction(txn); - return headers; - } catch(DbException e) { - db.abortTransaction(txn); - throw e; + T txn = db.startTransaction(); + try { + Collection<MessageHeader> headers = + db.getMessageHeaders(txn, g); + db.commitTransaction(txn); + return headers; + } catch(DbException e) { + db.abortTransaction(txn); + throw e; + } + } finally { + messageFlagLock.readLock().unlock(); } } finally { messageLock.readLock().unlock(); diff --git a/components/net/sf/briar/db/DatabaseModule.java b/components/net/sf/briar/db/DatabaseModule.java index d6ec12810d..903fb43458 100644 --- a/components/net/sf/briar/db/DatabaseModule.java +++ b/components/net/sf/briar/db/DatabaseModule.java @@ -9,7 +9,6 @@ import net.sf.briar.api.db.DatabaseDirectory; import net.sf.briar.api.db.DatabaseMaxSize; import net.sf.briar.api.db.DatabasePassword; import net.sf.briar.api.protocol.GroupFactory; -import net.sf.briar.api.protocol.MessageHeaderFactory; import net.sf.briar.api.transport.ConnectionWindowFactory; import com.google.inject.AbstractModule; @@ -27,10 +26,9 @@ public class DatabaseModule extends AbstractModule { Database<Connection> getDatabase(@DatabaseDirectory File dir, @DatabasePassword Password password, @DatabaseMaxSize long maxSize, ConnectionWindowFactory connectionWindowFactory, - GroupFactory groupFactory, - MessageHeaderFactory messageHeaderFactory) { + GroupFactory groupFactory) { return new H2Database(dir, password, maxSize, connectionWindowFactory, - groupFactory, messageHeaderFactory); + groupFactory); } @Provides @Singleton diff --git a/components/net/sf/briar/db/H2Database.java b/components/net/sf/briar/db/H2Database.java index 4b116ee323..2922c8ff7d 100644 --- a/components/net/sf/briar/db/H2Database.java +++ b/components/net/sf/briar/db/H2Database.java @@ -16,7 +16,6 @@ import net.sf.briar.api.db.DatabaseMaxSize; import net.sf.briar.api.db.DatabasePassword; import net.sf.briar.api.db.DbException; import net.sf.briar.api.protocol.GroupFactory; -import net.sf.briar.api.protocol.MessageHeaderFactory; import net.sf.briar.api.transport.ConnectionWindowFactory; import org.apache.commons.io.FileSystemUtils; @@ -38,9 +37,8 @@ class H2Database extends JdbcDatabase { H2Database(@DatabaseDirectory File dir, @DatabasePassword Password password, @DatabaseMaxSize long maxSize, ConnectionWindowFactory connectionWindowFactory, - GroupFactory groupFactory, - MessageHeaderFactory messageHeaderFactory) { - super(connectionWindowFactory, groupFactory, messageHeaderFactory, + GroupFactory groupFactory) { + super(connectionWindowFactory, groupFactory, "BINARY(32)", "BINARY", "INT NOT NULL AUTO_INCREMENT"); home = new File(dir, "db"); this.password = password; diff --git a/components/net/sf/briar/db/JdbcDatabase.java b/components/net/sf/briar/db/JdbcDatabase.java index f167aa121c..fe8e757bfc 100644 --- a/components/net/sf/briar/db/JdbcDatabase.java +++ b/components/net/sf/briar/db/JdbcDatabase.java @@ -23,6 +23,7 @@ import net.sf.briar.api.TransportConfig; import net.sf.briar.api.TransportId; import net.sf.briar.api.TransportProperties; import net.sf.briar.api.db.DbException; +import net.sf.briar.api.db.MessageHeader; import net.sf.briar.api.db.Status; import net.sf.briar.api.protocol.AuthorId; import net.sf.briar.api.protocol.BatchId; @@ -30,8 +31,6 @@ import net.sf.briar.api.protocol.Group; import net.sf.briar.api.protocol.GroupFactory; import net.sf.briar.api.protocol.GroupId; import net.sf.briar.api.protocol.Message; -import net.sf.briar.api.protocol.MessageHeader; -import net.sf.briar.api.protocol.MessageHeaderFactory; import net.sf.briar.api.protocol.MessageId; import net.sf.briar.api.transport.ConnectionWindow; import net.sf.briar.api.transport.ConnectionWindowFactory; @@ -241,7 +240,6 @@ abstract class JdbcDatabase implements Database<Connection> { private final String hashType, binaryType, counterType; private final ConnectionWindowFactory connectionWindowFactory; private final GroupFactory groupFactory; - private final MessageHeaderFactory messageHeaderFactory; private final LinkedList<Connection> connections = new LinkedList<Connection>(); // Locking: self @@ -252,12 +250,10 @@ abstract class JdbcDatabase implements Database<Connection> { protected abstract Connection createConnection() throws SQLException; JdbcDatabase(ConnectionWindowFactory connectionWindowFactory, - GroupFactory groupFactory, - MessageHeaderFactory messageHeaderFactory, - String hashType, String binaryType, String counterType) { + GroupFactory groupFactory, String hashType, String binaryType, + String counterType) { this.connectionWindowFactory = connectionWindowFactory; this.groupFactory = groupFactory; - this.messageHeaderFactory = messageHeaderFactory; this.hashType = hashType; this.binaryType = binaryType; this.counterType = counterType; @@ -1115,8 +1111,10 @@ abstract class JdbcDatabase implements Database<Connection> { PreparedStatement ps = null; ResultSet rs = null; try { - String sql = "SELECT messageId, parentId, authorId, subject," - + " timestamp FROM messages" + String sql = "SELECT messages.messageId, parentId, authorId," + + " subject, timestamp, read, starred" + + " FROM messages LEFT OUTER JOIN flags" + + " ON messages.messageId = flags.messageId" + " WHERE groupId = ?"; ps = txn.prepareStatement(sql); ps.setBytes(1, g.getBytes()); @@ -1129,8 +1127,10 @@ abstract class JdbcDatabase implements Database<Connection> { AuthorId author = new AuthorId(rs.getBytes(3)); String subject = rs.getString(4); long timestamp = rs.getLong(5); - headers.add(messageHeaderFactory.createMessageHeader(id, parent, - g, author, subject, timestamp)); + boolean read = rs.getBoolean(6); // False if absent + boolean starred = rs.getBoolean(7); // False if absent + headers.add(new MessageHeaderImpl(id, parent, g, author, + subject, timestamp, read, starred)); } rs.close(); ps.close(); diff --git a/components/net/sf/briar/protocol/MessageHeaderImpl.java b/components/net/sf/briar/db/MessageHeaderImpl.java similarity index 71% rename from components/net/sf/briar/protocol/MessageHeaderImpl.java rename to components/net/sf/briar/db/MessageHeaderImpl.java index b173c4f4e4..0e4d6fa668 100644 --- a/components/net/sf/briar/protocol/MessageHeaderImpl.java +++ b/components/net/sf/briar/db/MessageHeaderImpl.java @@ -1,8 +1,8 @@ -package net.sf.briar.protocol; +package net.sf.briar.db; +import net.sf.briar.api.db.MessageHeader; import net.sf.briar.api.protocol.AuthorId; import net.sf.briar.api.protocol.GroupId; -import net.sf.briar.api.protocol.MessageHeader; import net.sf.briar.api.protocol.MessageId; class MessageHeaderImpl implements MessageHeader { @@ -12,15 +12,19 @@ class MessageHeaderImpl implements MessageHeader { private final AuthorId author; private final String subject; private final long timestamp; + private final boolean read, starred; MessageHeaderImpl(MessageId id, MessageId parent, GroupId group, - AuthorId author, String subject, long timestamp) { + AuthorId author, String subject, long timestamp, boolean read, + boolean starred) { this.id = id; this.parent = parent; this.group = group; this.author = author; this.subject = subject; this.timestamp = timestamp; + this.read = read; + this.starred = starred; } public MessageId getId() { @@ -46,4 +50,12 @@ class MessageHeaderImpl implements MessageHeader { public long getTimestamp() { return timestamp; } + + public boolean getRead() { + return read; + } + + public boolean getStarred() { + return starred; + } } diff --git a/components/net/sf/briar/protocol/MessageHeaderFactoryImpl.java b/components/net/sf/briar/protocol/MessageHeaderFactoryImpl.java deleted file mode 100644 index dbe2abadf1..0000000000 --- a/components/net/sf/briar/protocol/MessageHeaderFactoryImpl.java +++ /dev/null @@ -1,16 +0,0 @@ -package net.sf.briar.protocol; - -import net.sf.briar.api.protocol.AuthorId; -import net.sf.briar.api.protocol.GroupId; -import net.sf.briar.api.protocol.MessageHeader; -import net.sf.briar.api.protocol.MessageHeaderFactory; -import net.sf.briar.api.protocol.MessageId; - -class MessageHeaderFactoryImpl implements MessageHeaderFactory { - - public MessageHeader createMessageHeader(MessageId id, MessageId parent, - GroupId group, AuthorId author, String subject, long timestamp) { - return new MessageHeaderImpl(id, parent, group, author, subject, - timestamp); - } -} diff --git a/components/net/sf/briar/protocol/ProtocolModule.java b/components/net/sf/briar/protocol/ProtocolModule.java index 4ced04e083..6d36d8efb3 100644 --- a/components/net/sf/briar/protocol/ProtocolModule.java +++ b/components/net/sf/briar/protocol/ProtocolModule.java @@ -10,7 +10,6 @@ import net.sf.briar.api.protocol.Group; import net.sf.briar.api.protocol.GroupFactory; import net.sf.briar.api.protocol.Message; import net.sf.briar.api.protocol.MessageEncoder; -import net.sf.briar.api.protocol.MessageHeaderFactory; import net.sf.briar.api.protocol.MessageId; import net.sf.briar.api.protocol.Offer; import net.sf.briar.api.protocol.ProtocolReaderFactory; @@ -31,7 +30,6 @@ public class ProtocolModule extends AbstractModule { bind(BatchFactory.class).to(BatchFactoryImpl.class); bind(GroupFactory.class).to(GroupFactoryImpl.class); bind(MessageEncoder.class).to(MessageEncoderImpl.class); - bind(MessageHeaderFactory.class).to(MessageHeaderFactoryImpl.class); bind(OfferFactory.class).to(OfferFactoryImpl.class); bind(ProtocolReaderFactory.class).to(ProtocolReaderFactoryImpl.class); bind(RequestFactory.class).to(RequestFactoryImpl.class); diff --git a/test/net/sf/briar/db/DatabaseComponentTest.java b/test/net/sf/briar/db/DatabaseComponentTest.java index e2618ad7c3..8b9332217c 100644 --- a/test/net/sf/briar/db/DatabaseComponentTest.java +++ b/test/net/sf/briar/db/DatabaseComponentTest.java @@ -13,6 +13,7 @@ import net.sf.briar.api.Rating; import net.sf.briar.api.TransportId; import net.sf.briar.api.TransportProperties; import net.sf.briar.api.db.DatabaseComponent; +import net.sf.briar.api.db.MessageHeader; import net.sf.briar.api.db.NoSuchContactException; import net.sf.briar.api.db.Status; import net.sf.briar.api.db.event.ContactAddedEvent; @@ -28,7 +29,6 @@ import net.sf.briar.api.protocol.BatchId; import net.sf.briar.api.protocol.Group; import net.sf.briar.api.protocol.GroupId; import net.sf.briar.api.protocol.Message; -import net.sf.briar.api.protocol.MessageHeader; import net.sf.briar.api.protocol.MessageId; import net.sf.briar.api.protocol.Offer; import net.sf.briar.api.protocol.ProtocolConstants; diff --git a/test/net/sf/briar/db/H2DatabaseTest.java b/test/net/sf/briar/db/H2DatabaseTest.java index b2f5b16c87..270085085c 100644 --- a/test/net/sf/briar/db/H2DatabaseTest.java +++ b/test/net/sf/briar/db/H2DatabaseTest.java @@ -26,6 +26,7 @@ import net.sf.briar.api.TransportId; import net.sf.briar.api.TransportProperties; import net.sf.briar.api.crypto.Password; import net.sf.briar.api.db.DbException; +import net.sf.briar.api.db.MessageHeader; import net.sf.briar.api.db.Status; import net.sf.briar.api.protocol.AuthorId; import net.sf.briar.api.protocol.BatchId; @@ -33,8 +34,6 @@ import net.sf.briar.api.protocol.Group; import net.sf.briar.api.protocol.GroupFactory; import net.sf.briar.api.protocol.GroupId; import net.sf.briar.api.protocol.Message; -import net.sf.briar.api.protocol.MessageHeader; -import net.sf.briar.api.protocol.MessageHeaderFactory; import net.sf.briar.api.protocol.MessageId; import net.sf.briar.api.transport.ConnectionWindow; import net.sf.briar.api.transport.ConnectionWindowFactory; @@ -63,7 +62,6 @@ public class H2DatabaseTest extends TestCase { private final Random random = new Random(); private final ConnectionWindowFactory connectionWindowFactory; private final GroupFactory groupFactory; - private final MessageHeaderFactory messageHeaderFactory; private final AuthorId authorId; private final BatchId batchId; @@ -90,7 +88,6 @@ public class H2DatabaseTest extends TestCase { new TransportModule(), new TestDatabaseModule(testDir)); connectionWindowFactory = i.getInstance(ConnectionWindowFactory.class); groupFactory = i.getInstance(GroupFactory.class); - messageHeaderFactory = i.getInstance(MessageHeaderFactory.class); authorId = new AuthorId(TestUtils.getRandomId()); batchId = new BatchId(TestUtils.getRandomId()); contactId = new ContactId(1); @@ -1667,6 +1664,8 @@ public class H2DatabaseTest extends TestCase { Message message1 = new TestMessage(messageId1, parentId, groupId, authorId, subject, timestamp1, raw); db.addGroupMessage(txn, message1); + // Mark one of the messages read + assertFalse(db.setRead(txn, messageId, true)); // Retrieve the message headers Collection<MessageHeader> headers = db.getMessageHeaders(txn, groupId); @@ -1676,10 +1675,14 @@ public class H2DatabaseTest extends TestCase { assertTrue(it.hasNext()); MessageHeader header = it.next(); if(messageId.equals(header.getId())) { - assertHeadersAreEqual(message, header); + assertHeadersMatch(message, header); + assertTrue(header.getRead()); + assertFalse(header.getStarred()); messageFound = true; } else if(messageId1.equals(header.getId())) { - assertHeadersAreEqual(message1, header); + assertHeadersMatch(message1, header); + assertFalse(header.getRead()); + assertFalse(header.getStarred()); message1Found = true; } else { fail(); @@ -1688,10 +1691,14 @@ public class H2DatabaseTest extends TestCase { assertTrue(it.hasNext()); header = it.next(); if(messageId.equals(header.getId())) { - assertHeadersAreEqual(message, header); + assertHeadersMatch(message, header); + assertTrue(header.getRead()); + assertFalse(header.getStarred()); messageFound = true; } else if(messageId1.equals(header.getId())) { - assertHeadersAreEqual(message1, header); + assertHeadersMatch(message1, header); + assertFalse(header.getRead()); + assertFalse(header.getStarred()); message1Found = true; } else { fail(); @@ -1705,6 +1712,18 @@ public class H2DatabaseTest extends TestCase { db.close(); } + private void assertHeadersMatch(Message m, MessageHeader h) { + assertEquals(m.getId(), h.getId()); + if(m.getParent() == null) assertNull(h.getParent()); + else assertEquals(m.getParent(), h.getParent()); + if(m.getGroup() == null) assertNull(h.getGroup()); + else assertEquals(m.getGroup(), h.getGroup()); + if(m.getAuthor() == null) assertNull(h.getAuthor()); + else assertEquals(m.getAuthor(), h.getAuthor()); + assertEquals(m.getSubject(), h.getSubject()); + assertEquals(m.getTimestamp(), h.getTimestamp()); + } + @Test public void testReadFlag() throws Exception { Database<Connection> db = open(false); @@ -1757,18 +1776,6 @@ public class H2DatabaseTest extends TestCase { db.close(); } - private void assertHeadersAreEqual(MessageHeader h1, MessageHeader h2) { - assertEquals(h1.getId(), h2.getId()); - if(h1.getParent() == null) assertNull(h2.getParent()); - else assertEquals(h1.getParent(), h2.getParent()); - if(h1.getGroup() == null) assertNull(h2.getGroup()); - else assertEquals(h1.getGroup(), h2.getGroup()); - if(h1.getAuthor() == null) assertNull(h2.getAuthor()); - else assertEquals(h1.getAuthor(), h2.getAuthor()); - assertEquals(h1.getSubject(), h2.getSubject()); - assertEquals(h1.getTimestamp(), h2.getTimestamp()); - } - @Test public void testExceptionHandling() throws Exception { Database<Connection> db = open(false); @@ -1787,7 +1794,7 @@ public class H2DatabaseTest extends TestCase { private Database<Connection> open(boolean resume) throws Exception { Database<Connection> db = new H2Database(testDir, password, MAX_SIZE, - connectionWindowFactory, groupFactory, messageHeaderFactory); + connectionWindowFactory, groupFactory); db.open(resume); return db; } -- GitLab