mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-20 14:49:53 +01:00
Metadata for groups. #221
This commit is contained in:
@@ -158,6 +158,9 @@ public interface DatabaseComponent {
|
|||||||
/** Returns the group with the given ID, if the user subscribes to it. */
|
/** Returns the group with the given ID, if the user subscribes to it. */
|
||||||
Group getGroup(GroupId g) throws DbException;
|
Group getGroup(GroupId g) throws DbException;
|
||||||
|
|
||||||
|
/** Returns the metadata for the given group. */
|
||||||
|
Metadata getGroupMetadata(GroupId g) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns all groups belonging to the given client to which the user
|
* Returns all groups belonging to the given client to which the user
|
||||||
* subscribes.
|
* subscribes.
|
||||||
@@ -234,6 +237,12 @@ public interface DatabaseComponent {
|
|||||||
void incrementStreamCounter(ContactId c, TransportId t, long rotationPeriod)
|
void incrementStreamCounter(ContactId c, TransportId t, long rotationPeriod)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merges the given metadata with the existing metadata for the given
|
||||||
|
* group.
|
||||||
|
*/
|
||||||
|
void mergeGroupMetadata(GroupId g, Metadata meta) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Merges the given properties with the existing local properties for the
|
* Merges the given properties with the existing local properties for the
|
||||||
* given transport.
|
* given transport.
|
||||||
|
|||||||
@@ -27,8 +27,6 @@ import java.io.IOException;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
// FIXME: Document the preconditions for calling each method
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A low-level interface to the database (DatabaseComponent provides a
|
* A low-level interface to the database (DatabaseComponent provides a
|
||||||
* high-level interface). Most operations take a transaction argument, which is
|
* high-level interface). Most operations take a transaction argument, which is
|
||||||
@@ -275,6 +273,13 @@ interface Database<T> {
|
|||||||
*/
|
*/
|
||||||
Group getGroup(T txn, GroupId g) throws DbException;
|
Group getGroup(T txn, GroupId g) throws DbException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the metadata for the given group.
|
||||||
|
* <p>
|
||||||
|
* Locking: read.
|
||||||
|
*/
|
||||||
|
Metadata getGroupMetadata(T txn, GroupId g) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns all groups belonging to the given client to which the user
|
* Returns all groups belonging to the given client to which the user
|
||||||
* subscribes.
|
* subscribes.
|
||||||
@@ -515,6 +520,15 @@ interface Database<T> {
|
|||||||
void lowerRequestedFlag(T txn, ContactId c, Collection<MessageId> requested)
|
void lowerRequestedFlag(T txn, ContactId c, Collection<MessageId> requested)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Merges the given metadata with the existing metadata for the given
|
||||||
|
* group.
|
||||||
|
* <p>
|
||||||
|
* Locking: write.
|
||||||
|
*/
|
||||||
|
void mergeGroupMetadata(T txn, GroupId g, Metadata meta)
|
||||||
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Merges the given properties with the existing local properties for the
|
* Merges the given properties with the existing local properties for the
|
||||||
* given transport.
|
* given transport.
|
||||||
|
|||||||
@@ -607,6 +607,25 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Metadata getGroupMetadata(GroupId g) throws DbException {
|
||||||
|
lock.readLock().lock();
|
||||||
|
try {
|
||||||
|
T txn = db.startTransaction();
|
||||||
|
try {
|
||||||
|
if (!db.containsGroup(txn, g))
|
||||||
|
throw new NoSuchSubscriptionException();
|
||||||
|
Metadata metadata = db.getGroupMetadata(txn, g);
|
||||||
|
db.commitTransaction(txn);
|
||||||
|
return metadata;
|
||||||
|
} catch (DbException e) {
|
||||||
|
db.abortTransaction(txn);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
lock.readLock().unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Collection<Group> getGroups(ClientId c) throws DbException {
|
public Collection<Group> getGroups(ClientId c) throws DbException {
|
||||||
lock.readLock().lock();
|
lock.readLock().lock();
|
||||||
try {
|
try {
|
||||||
@@ -954,6 +973,25 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void mergeGroupMetadata(GroupId g, Metadata meta)
|
||||||
|
throws DbException {
|
||||||
|
lock.writeLock().lock();
|
||||||
|
try {
|
||||||
|
T txn = db.startTransaction();
|
||||||
|
try {
|
||||||
|
if (!db.containsGroup(txn, g))
|
||||||
|
throw new NoSuchSubscriptionException();
|
||||||
|
db.mergeGroupMetadata(txn, g, meta);
|
||||||
|
db.commitTransaction(txn);
|
||||||
|
} catch (DbException e) {
|
||||||
|
db.abortTransaction(txn);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
lock.writeLock().unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void mergeLocalProperties(TransportId t, TransportProperties p)
|
public void mergeLocalProperties(TransportId t, TransportProperties p)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
boolean changed = false;
|
boolean changed = false;
|
||||||
|
|||||||
@@ -65,8 +65,8 @@ import static org.briarproject.db.ExponentialBackoff.calculateExpiry;
|
|||||||
*/
|
*/
|
||||||
abstract class JdbcDatabase implements Database<Connection> {
|
abstract class JdbcDatabase implements Database<Connection> {
|
||||||
|
|
||||||
private static final int SCHEMA_VERSION = 16;
|
private static final int SCHEMA_VERSION = 17;
|
||||||
private static final int MIN_SCHEMA_VERSION = 16;
|
private static final int MIN_SCHEMA_VERSION = 17;
|
||||||
|
|
||||||
private static final String CREATE_SETTINGS =
|
private static final String CREATE_SETTINGS =
|
||||||
"CREATE TABLE settings"
|
"CREATE TABLE settings"
|
||||||
@@ -107,6 +107,16 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
+ " visibleToAll BOOLEAN NOT NULL,"
|
+ " visibleToAll BOOLEAN NOT NULL,"
|
||||||
+ " PRIMARY KEY (groupId))";
|
+ " PRIMARY KEY (groupId))";
|
||||||
|
|
||||||
|
private static final String CREATE_GROUP_METADATA =
|
||||||
|
"CREATE TABLE groupMetadata"
|
||||||
|
+ " (groupId HASH NOT NULL,"
|
||||||
|
+ " key VARCHAR NOT NULL,"
|
||||||
|
+ " value BINARY NOT NULL,"
|
||||||
|
+ " PRIMARY KEY (groupId, key),"
|
||||||
|
+ " FOREIGN KEY (groupId)"
|
||||||
|
+ " REFERENCES groups (groupId)"
|
||||||
|
+ " ON DELETE CASCADE)";
|
||||||
|
|
||||||
private static final String CREATE_GROUP_VISIBILITIES =
|
private static final String CREATE_GROUP_VISIBILITIES =
|
||||||
"CREATE TABLE groupVisibilities"
|
"CREATE TABLE groupVisibilities"
|
||||||
+ " (contactId INT NOT NULL,"
|
+ " (contactId INT NOT NULL,"
|
||||||
@@ -386,6 +396,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
s.executeUpdate(insertTypeNames(CREATE_LOCAL_AUTHORS));
|
s.executeUpdate(insertTypeNames(CREATE_LOCAL_AUTHORS));
|
||||||
s.executeUpdate(insertTypeNames(CREATE_CONTACTS));
|
s.executeUpdate(insertTypeNames(CREATE_CONTACTS));
|
||||||
s.executeUpdate(insertTypeNames(CREATE_GROUPS));
|
s.executeUpdate(insertTypeNames(CREATE_GROUPS));
|
||||||
|
s.executeUpdate(insertTypeNames(CREATE_GROUP_METADATA));
|
||||||
s.executeUpdate(insertTypeNames(CREATE_GROUP_VISIBILITIES));
|
s.executeUpdate(insertTypeNames(CREATE_GROUP_VISIBILITIES));
|
||||||
s.executeUpdate(insertTypeNames(CREATE_CONTACT_GROUPS));
|
s.executeUpdate(insertTypeNames(CREATE_CONTACT_GROUPS));
|
||||||
s.executeUpdate(insertTypeNames(CREATE_GROUP_VERSIONS));
|
s.executeUpdate(insertTypeNames(CREATE_GROUP_VERSIONS));
|
||||||
@@ -1496,16 +1507,25 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Metadata getGroupMetadata(Connection txn, GroupId g)
|
||||||
|
throws DbException {
|
||||||
|
return getMetadata(txn, g.getBytes(), "groupMetadata", "groupId");
|
||||||
|
}
|
||||||
|
|
||||||
public Metadata getMessageMetadata(Connection txn, MessageId m)
|
public Metadata getMessageMetadata(Connection txn, MessageId m)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
|
return getMetadata(txn, m.getBytes(), "messageMetadata", "messageId");
|
||||||
|
}
|
||||||
|
|
||||||
|
private Metadata getMetadata(Connection txn, byte[] id, String tableName,
|
||||||
|
String columnName) throws DbException {
|
||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
ResultSet rs = null;
|
ResultSet rs = null;
|
||||||
try {
|
try {
|
||||||
String sql = "SELECT key, value"
|
String sql = "SELECT key, value FROM " + tableName
|
||||||
+ " FROM messageMetadata"
|
+ " WHERE " + columnName + " = ?";
|
||||||
+ " WHERE messageId = ?";
|
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setBytes(1, m.getBytes());
|
ps.setBytes(1, id);
|
||||||
rs = ps.executeQuery();
|
rs = ps.executeQuery();
|
||||||
Metadata metadata = new Metadata();
|
Metadata metadata = new Metadata();
|
||||||
while (rs.next()) metadata.put(rs.getString(1), rs.getBytes(2));
|
while (rs.next()) metadata.put(rs.getString(1), rs.getBytes(2));
|
||||||
@@ -2329,8 +2349,18 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void mergeGroupMetadata(Connection txn, GroupId g, Metadata meta)
|
||||||
|
throws DbException {
|
||||||
|
mergeMetadata(txn, g.getBytes(), meta, "groupMetadata", "groupId");
|
||||||
|
}
|
||||||
|
|
||||||
public void mergeMessageMetadata(Connection txn, MessageId m, Metadata meta)
|
public void mergeMessageMetadata(Connection txn, MessageId m, Metadata meta)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
|
mergeMetadata(txn, m.getBytes(), meta, "messageMetadata", "messageId");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void mergeMetadata(Connection txn, byte[] id, Metadata meta,
|
||||||
|
String tableName, String columnName) throws DbException {
|
||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
try {
|
try {
|
||||||
// Determine which keys are being removed
|
// Determine which keys are being removed
|
||||||
@@ -2342,10 +2372,10 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
}
|
}
|
||||||
// Delete any keys that are being removed
|
// Delete any keys that are being removed
|
||||||
if (!removed.isEmpty()) {
|
if (!removed.isEmpty()) {
|
||||||
String sql = "DELETE FROM messageMetadata"
|
String sql = "DELETE FROM " + tableName
|
||||||
+ " WHERE messageId = ? AND key = ?";
|
+ " WHERE " + columnName + " = ? AND key = ?";
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setBytes(1, m.getBytes());
|
ps.setBytes(1, id);
|
||||||
for (String key : removed) {
|
for (String key : removed) {
|
||||||
ps.setString(2, key);
|
ps.setString(2, key);
|
||||||
ps.addBatch();
|
ps.addBatch();
|
||||||
@@ -2361,10 +2391,10 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
}
|
}
|
||||||
if (retained.isEmpty()) return;
|
if (retained.isEmpty()) return;
|
||||||
// Update any keys that already exist
|
// Update any keys that already exist
|
||||||
String sql = "UPDATE messageMetadata SET value = ?"
|
String sql = "UPDATE " + tableName + " SET value = ?"
|
||||||
+ " WHERE messageId = ? AND key = ?";
|
+ " WHERE " + columnName + " = ? AND key = ?";
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setBytes(2, m.getBytes());
|
ps.setBytes(2, id);
|
||||||
for (Entry<String, byte[]> e : retained.entrySet()) {
|
for (Entry<String, byte[]> e : retained.entrySet()) {
|
||||||
ps.setBytes(1, e.getValue());
|
ps.setBytes(1, e.getValue());
|
||||||
ps.setString(3, e.getKey());
|
ps.setString(3, e.getKey());
|
||||||
@@ -2378,10 +2408,11 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
if (batchAffected[i] > 1) throw new DbStateException();
|
if (batchAffected[i] > 1) throw new DbStateException();
|
||||||
}
|
}
|
||||||
// Insert any keys that don't already exist
|
// Insert any keys that don't already exist
|
||||||
sql = "INSERT INTO messageMetadata (messageId, key, value)"
|
sql = "INSERT INTO " + tableName
|
||||||
|
+ " (" + columnName + ", key, value)"
|
||||||
+ " VALUES (?, ?, ?)";
|
+ " VALUES (?, ?, ?)";
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setBytes(1, m.getBytes());
|
ps.setBytes(1, id);
|
||||||
int updateIndex = 0, inserted = 0;
|
int updateIndex = 0, inserted = 0;
|
||||||
for (Entry<String, byte[]> e : retained.entrySet()) {
|
for (Entry<String, byte[]> e : retained.entrySet()) {
|
||||||
if (batchAffected[updateIndex] == 0) {
|
if (batchAffected[updateIndex] == 0) {
|
||||||
|
|||||||
@@ -83,8 +83,14 @@ class MessagingManagerImpl implements MessagingManager, AddContactHook,
|
|||||||
db.addGroup(g);
|
db.addGroup(g);
|
||||||
db.addContactGroup(c, g);
|
db.addContactGroup(c, g);
|
||||||
db.setVisibility(g.getId(), Collections.singletonList(c));
|
db.setVisibility(g.getId(), Collections.singletonList(c));
|
||||||
|
// Attach the contact ID to the group
|
||||||
|
BdfDictionary d = new BdfDictionary();
|
||||||
|
d.put("contactId", c.getInt());
|
||||||
|
db.mergeGroupMetadata(g.getId(), metadataEncoder.encode(d));
|
||||||
} catch (DbException e) {
|
} catch (DbException e) {
|
||||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||||
|
} catch (FormatException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,18 +147,20 @@ class MessagingManagerImpl implements MessagingManager, AddContactHook,
|
|||||||
Metadata meta = metadataEncoder.encode(d);
|
Metadata meta = metadataEncoder.encode(d);
|
||||||
db.addLocalMessage(m.getMessage(), CLIENT_ID, meta);
|
db.addLocalMessage(m.getMessage(), CLIENT_ID, meta);
|
||||||
} catch (FormatException e) {
|
} catch (FormatException e) {
|
||||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ContactId getContactId(GroupId g) throws DbException {
|
public ContactId getContactId(GroupId g) throws DbException {
|
||||||
// TODO: Use metadata to attach the contact ID to the group
|
try {
|
||||||
for (Contact c : db.getContacts()) {
|
BdfDictionary d = metadataParser.parse(db.getGroupMetadata(g));
|
||||||
Group conversation = getConversationGroup(c);
|
long id = d.getInteger("contactId");
|
||||||
if (conversation.getId().equals(g)) return c.getId();
|
return new ContactId((int) id);
|
||||||
|
} catch (FormatException e) {
|
||||||
|
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||||
|
throw new NoSuchContactException();
|
||||||
}
|
}
|
||||||
throw new NoSuchContactException();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -533,11 +533,11 @@ public class DatabaseComponentImplTest extends BriarTestCase {
|
|||||||
final EventBus eventBus = context.mock(EventBus.class);
|
final EventBus eventBus = context.mock(EventBus.class);
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
// Check whether the subscription is in the DB (which it's not)
|
// Check whether the subscription is in the DB (which it's not)
|
||||||
exactly(5).of(database).startTransaction();
|
exactly(7).of(database).startTransaction();
|
||||||
will(returnValue(txn));
|
will(returnValue(txn));
|
||||||
exactly(5).of(database).containsGroup(txn, groupId);
|
exactly(7).of(database).containsGroup(txn, groupId);
|
||||||
will(returnValue(false));
|
will(returnValue(false));
|
||||||
exactly(5).of(database).abortTransaction(txn);
|
exactly(7).of(database).abortTransaction(txn);
|
||||||
// This is needed for getMessageStatus() to proceed
|
// This is needed for getMessageStatus() to proceed
|
||||||
exactly(1).of(database).containsContact(txn, contactId);
|
exactly(1).of(database).containsContact(txn, contactId);
|
||||||
will(returnValue(true));
|
will(returnValue(true));
|
||||||
@@ -552,6 +552,13 @@ public class DatabaseComponentImplTest extends BriarTestCase {
|
|||||||
// Expected
|
// Expected
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
db.getGroupMetadata(groupId);
|
||||||
|
fail();
|
||||||
|
} catch (NoSuchSubscriptionException expected) {
|
||||||
|
// Expected
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
db.getMessageStatus(contactId, groupId);
|
db.getMessageStatus(contactId, groupId);
|
||||||
fail();
|
fail();
|
||||||
@@ -566,6 +573,13 @@ public class DatabaseComponentImplTest extends BriarTestCase {
|
|||||||
// Expected
|
// Expected
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
db.mergeGroupMetadata(groupId, metadata);
|
||||||
|
fail();
|
||||||
|
} catch (NoSuchSubscriptionException expected) {
|
||||||
|
// Expected
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
db.removeGroup(group);
|
db.removeGroup(group);
|
||||||
fail();
|
fail();
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ import java.util.concurrent.CountDownLatch;
|
|||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||||
|
import static org.briarproject.api.db.Metadata.REMOVE;
|
||||||
import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
|
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.SyncConstants.MAX_GROUP_DESCRIPTOR_LENGTH;
|
||||||
import static org.briarproject.api.sync.SyncConstants.MAX_MESSAGE_LENGTH;
|
import static org.briarproject.api.sync.SyncConstants.MAX_MESSAGE_LENGTH;
|
||||||
@@ -1031,6 +1032,44 @@ public class H2DatabaseTest extends BriarTestCase {
|
|||||||
db.close();
|
db.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGroupMetadata() throws Exception {
|
||||||
|
Database<Connection> db = open(false);
|
||||||
|
Connection txn = db.startTransaction();
|
||||||
|
|
||||||
|
// Add a group
|
||||||
|
db.addGroup(txn, group);
|
||||||
|
|
||||||
|
// Attach some metadata to the group
|
||||||
|
Metadata metadata = new Metadata();
|
||||||
|
metadata.put("foo", new byte[]{'b', 'a', 'r'});
|
||||||
|
metadata.put("baz", new byte[]{'b', 'a', 'm'});
|
||||||
|
db.mergeGroupMetadata(txn, groupId, metadata);
|
||||||
|
|
||||||
|
// Retrieve the metadata for the group
|
||||||
|
Metadata retrieved = db.getGroupMetadata(txn, groupId);
|
||||||
|
assertEquals(2, retrieved.size());
|
||||||
|
assertTrue(retrieved.containsKey("foo"));
|
||||||
|
assertArrayEquals(metadata.get("foo"), retrieved.get("foo"));
|
||||||
|
assertTrue(retrieved.containsKey("baz"));
|
||||||
|
assertArrayEquals(metadata.get("baz"), retrieved.get("baz"));
|
||||||
|
|
||||||
|
// Update the metadata
|
||||||
|
metadata.put("foo", REMOVE);
|
||||||
|
metadata.put("baz", new byte[] {'q', 'u', 'x'});
|
||||||
|
db.mergeGroupMetadata(txn, groupId, metadata);
|
||||||
|
|
||||||
|
// Retrieve the metadata again
|
||||||
|
retrieved = db.getGroupMetadata(txn, groupId);
|
||||||
|
assertEquals(1, retrieved.size());
|
||||||
|
assertFalse(retrieved.containsKey("foo"));
|
||||||
|
assertTrue(retrieved.containsKey("baz"));
|
||||||
|
assertArrayEquals(metadata.get("baz"), retrieved.get("baz"));
|
||||||
|
|
||||||
|
db.commitTransaction(txn);
|
||||||
|
db.close();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMessageMetadata() throws Exception {
|
public void testMessageMetadata() throws Exception {
|
||||||
Database<Connection> db = open(false);
|
Database<Connection> db = open(false);
|
||||||
@@ -1043,22 +1082,49 @@ public class H2DatabaseTest extends BriarTestCase {
|
|||||||
// Attach some metadata to the message
|
// Attach some metadata to the message
|
||||||
Metadata metadata = new Metadata();
|
Metadata metadata = new Metadata();
|
||||||
metadata.put("foo", new byte[]{'b', 'a', 'r'});
|
metadata.put("foo", new byte[]{'b', 'a', 'r'});
|
||||||
|
metadata.put("baz", new byte[]{'b', 'a', 'm'});
|
||||||
db.mergeMessageMetadata(txn, messageId, metadata);
|
db.mergeMessageMetadata(txn, messageId, metadata);
|
||||||
|
|
||||||
// Retrieve the metadata for the message
|
// Retrieve the metadata for the message
|
||||||
Metadata retrieved = db.getMessageMetadata(txn, messageId);
|
Metadata retrieved = db.getMessageMetadata(txn, messageId);
|
||||||
assertEquals(1, retrieved.size());
|
assertEquals(2, retrieved.size());
|
||||||
assertTrue(retrieved.containsKey("foo"));
|
assertTrue(retrieved.containsKey("foo"));
|
||||||
assertArrayEquals(metadata.get("foo"), retrieved.get("foo"));
|
assertArrayEquals(metadata.get("foo"), retrieved.get("foo"));
|
||||||
|
assertTrue(retrieved.containsKey("baz"));
|
||||||
|
assertArrayEquals(metadata.get("baz"), retrieved.get("baz"));
|
||||||
|
|
||||||
// Retrieve the metadata for the group
|
// Retrieve the metadata for the group
|
||||||
Map<MessageId, Metadata> all = db.getMessageMetadata(txn, groupId);
|
Map<MessageId, Metadata> all = db.getMessageMetadata(txn, groupId);
|
||||||
assertEquals(1, all.size());
|
assertEquals(1, all.size());
|
||||||
assertTrue(all.containsKey(messageId));
|
assertTrue(all.containsKey(messageId));
|
||||||
retrieved = all.get(messageId);
|
retrieved = all.get(messageId);
|
||||||
assertEquals(1, retrieved.size());
|
assertEquals(2, retrieved.size());
|
||||||
assertTrue(retrieved.containsKey("foo"));
|
assertTrue(retrieved.containsKey("foo"));
|
||||||
assertArrayEquals(metadata.get("foo"), retrieved.get("foo"));
|
assertArrayEquals(metadata.get("foo"), retrieved.get("foo"));
|
||||||
|
assertTrue(retrieved.containsKey("baz"));
|
||||||
|
assertArrayEquals(metadata.get("baz"), retrieved.get("baz"));
|
||||||
|
|
||||||
|
// Update the metadata
|
||||||
|
metadata.put("foo", REMOVE);
|
||||||
|
metadata.put("baz", new byte[] {'q', 'u', 'x'});
|
||||||
|
db.mergeMessageMetadata(txn, messageId, metadata);
|
||||||
|
|
||||||
|
// Retrieve the metadata again
|
||||||
|
retrieved = db.getMessageMetadata(txn, messageId);
|
||||||
|
assertEquals(1, retrieved.size());
|
||||||
|
assertFalse(retrieved.containsKey("foo"));
|
||||||
|
assertTrue(retrieved.containsKey("baz"));
|
||||||
|
assertArrayEquals(metadata.get("baz"), retrieved.get("baz"));
|
||||||
|
|
||||||
|
// Retrieve the metadata for the group again
|
||||||
|
all = db.getMessageMetadata(txn, groupId);
|
||||||
|
assertEquals(1, all.size());
|
||||||
|
assertTrue(all.containsKey(messageId));
|
||||||
|
retrieved = all.get(messageId);
|
||||||
|
assertEquals(1, retrieved.size());
|
||||||
|
assertFalse(retrieved.containsKey("foo"));
|
||||||
|
assertTrue(retrieved.containsKey("baz"));
|
||||||
|
assertArrayEquals(metadata.get("baz"), retrieved.get("baz"));
|
||||||
|
|
||||||
db.commitTransaction(txn);
|
db.commitTransaction(txn);
|
||||||
db.close();
|
db.close();
|
||||||
|
|||||||
Reference in New Issue
Block a user