mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-13 19:29:06 +01:00
More DB support for private messages - needs unit tests.
This commit is contained in:
@@ -92,7 +92,7 @@ interface Database<T> {
|
||||
* <p>
|
||||
* Locking: messages write.
|
||||
*/
|
||||
boolean addMessage(T txn, Message m) throws DbException;
|
||||
boolean addGroupMessage(T txn, Message m) throws DbException;
|
||||
|
||||
/**
|
||||
* Records a sent batch as needing to be acknowledged.
|
||||
@@ -102,6 +102,14 @@ interface Database<T> {
|
||||
void addOutstandingBatch(T txn, ContactId c, BatchId b,
|
||||
Collection<MessageId> sent) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns false if the given message is already in the database. Otherwise
|
||||
* stores the message and returns true.
|
||||
* <p>
|
||||
* Locking: contacts read, messages write.
|
||||
*/
|
||||
boolean addPrivateMessage(T txn, Message m, ContactId c) throws DbException;
|
||||
|
||||
/**
|
||||
* Subscribes to the given group.
|
||||
* <p>
|
||||
@@ -360,8 +368,8 @@ interface Database<T> {
|
||||
/**
|
||||
* Removes a contact (and all associated state) from the database.
|
||||
* <p>
|
||||
* Locking: contacts write, messageStatuses write, subscriptions write,
|
||||
* transports write.
|
||||
* Locking: contacts write, messages write, messageStatuses write,
|
||||
* subscriptions write, transports write.
|
||||
*/
|
||||
void removeContact(T txn, ContactId c) throws DbException;
|
||||
|
||||
|
||||
@@ -179,7 +179,7 @@ DatabaseCleaner.Callback {
|
||||
protected boolean storeGroupMessage(Txn txn, Message m, ContactId sender)
|
||||
throws DbException {
|
||||
if(m.getGroup() == null) throw new IllegalArgumentException();
|
||||
boolean stored = db.addMessage(txn, m);
|
||||
boolean stored = db.addGroupMessage(txn, m);
|
||||
// Mark the message as seen by the sender
|
||||
MessageId id = m.getId();
|
||||
if(sender != null) db.setStatus(txn, sender, id, Status.SEEN);
|
||||
@@ -229,7 +229,7 @@ DatabaseCleaner.Callback {
|
||||
protected boolean storePrivateMessage(Txn txn, Message m, ContactId c,
|
||||
boolean incoming) throws DbException {
|
||||
if(m.getGroup() != null) throw new IllegalArgumentException();
|
||||
if(!db.addMessage(txn, m)) return false;
|
||||
if(!db.addPrivateMessage(txn, m, c)) return false;
|
||||
MessageId id = m.getId();
|
||||
if(incoming) db.setStatus(txn, c, id, Status.SEEN);
|
||||
else db.setStatus(txn, c, id, Status.NEW);
|
||||
|
||||
@@ -49,6 +49,12 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
+ " start BIGINT NOT NULL,"
|
||||
+ " PRIMARY KEY (groupId))";
|
||||
|
||||
private static final String CREATE_CONTACTS =
|
||||
"CREATE TABLE contacts"
|
||||
+ " (contactId INT NOT NULL,"
|
||||
+ " secret BINARY NOT NULL,"
|
||||
+ " PRIMARY KEY (contactId))";
|
||||
|
||||
private static final String CREATE_MESSAGES =
|
||||
"CREATE TABLE messages"
|
||||
+ " (messageId HASH NOT NULL,"
|
||||
@@ -58,9 +64,12 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
+ " timestamp BIGINT NOT NULL,"
|
||||
+ " size INT NOT NULL,"
|
||||
+ " raw BLOB NOT NULL,"
|
||||
+ " sendability INT NOT NULL,"
|
||||
+ " sendability INT,"
|
||||
+ " contactId INT,"
|
||||
+ " PRIMARY KEY (messageId),"
|
||||
+ " FOREIGN KEY (groupId) REFERENCES subscriptions (groupId)"
|
||||
+ " ON DELETE CASCADE,"
|
||||
+ " FOREIGN KEY (contactId) REFERENCES contacts (contactId)"
|
||||
+ " ON DELETE CASCADE)";
|
||||
|
||||
private static final String INDEX_MESSAGES_BY_PARENT =
|
||||
@@ -75,12 +84,6 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
private static final String INDEX_MESSAGES_BY_SENDABILITY =
|
||||
"CREATE INDEX messagesBySendability ON messages (sendability)";
|
||||
|
||||
private static final String CREATE_CONTACTS =
|
||||
"CREATE TABLE contacts"
|
||||
+ " (contactId INT NOT NULL,"
|
||||
+ " secret BINARY NOT NULL,"
|
||||
+ " PRIMARY KEY (contactId))";
|
||||
|
||||
private static final String CREATE_VISIBILITIES =
|
||||
"CREATE TABLE visibilities"
|
||||
+ " (groupId HASH NOT NULL,"
|
||||
@@ -278,12 +281,12 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
try {
|
||||
s = txn.createStatement();
|
||||
s.executeUpdate(insertTypeNames(CREATE_SUBSCRIPTIONS));
|
||||
s.executeUpdate(insertTypeNames(CREATE_CONTACTS));
|
||||
s.executeUpdate(insertTypeNames(CREATE_MESSAGES));
|
||||
s.executeUpdate(INDEX_MESSAGES_BY_PARENT);
|
||||
s.executeUpdate(INDEX_MESSAGES_BY_AUTHOR);
|
||||
s.executeUpdate(INDEX_MESSAGES_BY_BIGINT);
|
||||
s.executeUpdate(INDEX_MESSAGES_BY_SENDABILITY);
|
||||
s.executeUpdate(insertTypeNames(CREATE_CONTACTS));
|
||||
s.executeUpdate(insertTypeNames(CREATE_VISIBILITIES));
|
||||
s.executeUpdate(INDEX_VISIBILITIES_BY_GROUP);
|
||||
s.executeUpdate(insertTypeNames(CREATE_BATCHES_TO_ACK));
|
||||
@@ -527,14 +530,16 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean addMessage(Connection txn, Message m) throws DbException {
|
||||
public boolean addGroupMessage(Connection txn, Message m)
|
||||
throws DbException {
|
||||
assert m.getGroup() != null;
|
||||
if(containsMessage(txn, m.getId())) return false;
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
String sql = "INSERT INTO messages"
|
||||
+ " (messageId, parentId, groupId, authorId, timestamp, size,"
|
||||
+ " raw, sendability)"
|
||||
+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
|
||||
+ " VALUES (?, ?, ?, ?, ?, ?, ?, ZERO())";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setBytes(1, m.getId().getBytes());
|
||||
if(m.getParent() == null) ps.setNull(2, Types.BINARY);
|
||||
@@ -546,7 +551,6 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
ps.setInt(6, m.getSize());
|
||||
byte[] raw = m.getBytes();
|
||||
ps.setBinaryStream(7, new ByteArrayInputStream(raw), raw.length);
|
||||
ps.setInt(8, 0);
|
||||
int affected = ps.executeUpdate();
|
||||
if(affected != 1) throw new DbStateException();
|
||||
ps.close();
|
||||
@@ -616,6 +620,34 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean addPrivateMessage(Connection txn, Message m, ContactId c)
|
||||
throws DbException {
|
||||
assert m.getGroup() == null;
|
||||
if(containsMessage(txn, m.getId())) return false;
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
String sql = "INSERT INTO messages"
|
||||
+ " (messageId, parentId, timestamp, size, raw, contactId)"
|
||||
+ " VALUES (?, ?, ?, ?, ?, ?)";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setBytes(1, m.getId().getBytes());
|
||||
if(m.getParent() == null) ps.setNull(2, Types.BINARY);
|
||||
else ps.setBytes(2, m.getParent().getBytes());
|
||||
ps.setLong(3, m.getTimestamp());
|
||||
ps.setInt(4, m.getSize());
|
||||
byte[] raw = m.getBytes();
|
||||
ps.setBinaryStream(5, new ByteArrayInputStream(raw), raw.length);
|
||||
ps.setInt(6, c.getInt());
|
||||
int affected = ps.executeUpdate();
|
||||
if(affected != 1) throw new DbStateException();
|
||||
ps.close();
|
||||
return true;
|
||||
} catch(SQLException e) {
|
||||
tryToClose(ps);
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void addSubscription(Connection txn, Group g) throws DbException {
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
|
||||
@@ -909,28 +909,33 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
||||
if(LOG.isLoggable(Level.FINE)) LOG.fine("Removing contact " + c);
|
||||
contactLock.writeLock().lock();
|
||||
try {
|
||||
messageStatusLock.writeLock().lock();
|
||||
messageLock.writeLock().lock();
|
||||
try {
|
||||
subscriptionLock.writeLock().lock();
|
||||
messageStatusLock.writeLock().lock();
|
||||
try {
|
||||
transportLock.writeLock().lock();
|
||||
subscriptionLock.writeLock().lock();
|
||||
try {
|
||||
Txn txn = db.startTransaction();
|
||||
transportLock.writeLock().lock();
|
||||
try {
|
||||
db.removeContact(txn, c);
|
||||
db.commitTransaction(txn);
|
||||
} catch(DbException e) {
|
||||
db.abortTransaction(txn);
|
||||
throw e;
|
||||
Txn txn = db.startTransaction();
|
||||
try {
|
||||
db.removeContact(txn, c);
|
||||
db.commitTransaction(txn);
|
||||
} catch(DbException e) {
|
||||
db.abortTransaction(txn);
|
||||
throw e;
|
||||
}
|
||||
} finally {
|
||||
transportLock.writeLock().unlock();
|
||||
}
|
||||
} finally {
|
||||
transportLock.writeLock().unlock();
|
||||
subscriptionLock.writeLock().unlock();
|
||||
}
|
||||
} finally {
|
||||
subscriptionLock.writeLock().unlock();
|
||||
messageStatusLock.writeLock().unlock();
|
||||
}
|
||||
} finally {
|
||||
messageStatusLock.writeLock().unlock();
|
||||
messageLock.writeLock().unlock();
|
||||
}
|
||||
} finally {
|
||||
contactLock.writeLock().unlock();
|
||||
|
||||
@@ -689,16 +689,18 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
||||
public void removeContact(ContactId c) throws DbException {
|
||||
if(LOG.isLoggable(Level.FINE)) LOG.fine("Removing contact " + c);
|
||||
synchronized(contactLock) {
|
||||
synchronized(messageStatusLock) {
|
||||
synchronized(subscriptionLock) {
|
||||
synchronized(transportLock) {
|
||||
Txn txn = db.startTransaction();
|
||||
try {
|
||||
db.removeContact(txn, c);
|
||||
db.commitTransaction(txn);
|
||||
} catch(DbException e) {
|
||||
db.abortTransaction(txn);
|
||||
throw e;
|
||||
synchronized(messageLock) {
|
||||
synchronized(messageStatusLock) {
|
||||
synchronized(subscriptionLock) {
|
||||
synchronized(transportLock) {
|
||||
Txn txn = db.startTransaction();
|
||||
try {
|
||||
db.removeContact(txn, c);
|
||||
db.commitTransaction(txn);
|
||||
} catch(DbException e) {
|
||||
db.abortTransaction(txn);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user