Exposed message handling methods through DatabaseComponent interface.

This commit is contained in:
akwizgran
2013-01-30 18:23:53 +00:00
parent 9a78071bde
commit 520b6da5ac
7 changed files with 337 additions and 144 deletions

View File

@@ -233,13 +233,6 @@ interface Database<T> {
TransportProperties getLocalProperties(T txn, TransportId t)
throws DbException;
/**
* Returns the message identified by the given ID, in serialised form.
* <p>
* Locking: message read.
*/
byte[] getMessage(T txn, MessageId m) throws DbException;
/**
* Returns the body of the message identified by the given ID.
* <p>
@@ -247,6 +240,13 @@ interface Database<T> {
*/
byte[] getMessageBody(T txn, MessageId m) throws DbException;
/**
* Returns the header of the message identified by the given ID.
* <p>
* Locking: message read.
*/
MessageHeader getMessageHeader(T txn, MessageId m) throws DbException;
/**
* Returns the headers of all messages in the given group.
* <p>
@@ -255,16 +255,6 @@ interface Database<T> {
Collection<MessageHeader> getMessageHeaders(T txn, GroupId g)
throws DbException;
/**
* Returns the message identified by the given ID, in raw format, or null
* if the message is not present in the database or is not sendable to the
* given contact.
* <p>
* Locking: contact read, message read, subscription read.
*/
byte[] getMessageIfSendable(T txn, ContactId c, MessageId m)
throws DbException;
/**
* Returns the IDs of all messages signed by the given author.
* <p>
@@ -300,6 +290,23 @@ interface Database<T> {
*/
int getNumberOfSendableChildren(T txn, MessageId m) throws DbException;
/**
* Returns the message identified by the given ID, in serialised form.
* <p>
* Locking: message read.
*/
byte[] getRawMessage(T txn, MessageId m) throws DbException;
/**
* Returns the message identified by the given ID, in serialised form, or
* null if the message is not present in the database or is not sendable to
* the given contact.
* <p>
* Locking: contact read, message read, subscription read.
*/
byte[] getRawMessageIfSendable(T txn, ContactId c, MessageId m)
throws DbException;
/**
* Returns the IDs of the oldest messages in the database, with a total
* size less than or equal to the given size.
@@ -569,7 +576,7 @@ interface Database<T> {
* <p>
* Locking: message write.
*/
boolean setRead(T txn, MessageId m, boolean read) throws DbException;
boolean setReadFlag(T txn, MessageId m, boolean read) throws DbException;
/**
* Updates the remote transport properties for the given contact and the
@@ -605,7 +612,8 @@ interface Database<T> {
* <p>
* Locking: message write.
*/
boolean setStarred(T txn, MessageId m, boolean starred) throws DbException;
boolean setStarredFlag(T txn, MessageId m, boolean starred)
throws DbException;
/**
* Sets the status of the given message with respect to the given contact.

View File

@@ -29,6 +29,7 @@ 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.NoSuchMessageException;
import net.sf.briar.api.db.NoSuchSubscriptionException;
import net.sf.briar.api.db.NoSuchTransportException;
import net.sf.briar.api.db.event.ContactAddedEvent;
@@ -503,7 +504,7 @@ DatabaseCleaner.Callback {
throw new NoSuchContactException();
ids = db.getSendableMessages(txn, c, maxLength);
for(MessageId m : ids) {
messages.add(db.getMessage(txn, m));
messages.add(db.getRawMessage(txn, m));
}
db.commitTransaction(txn);
} catch(DbException e) {
@@ -555,7 +556,7 @@ DatabaseCleaner.Callback {
Iterator<MessageId> it = requested.iterator();
while(it.hasNext()) {
MessageId m = it.next();
byte[] raw = db.getMessageIfSendable(txn, c, m);
byte[] raw = db.getRawMessageIfSendable(txn, c, m);
if(raw != null) {
if(raw.length > maxLength) break;
messages.add(raw);
@@ -828,6 +829,44 @@ DatabaseCleaner.Callback {
}
}
public byte[] getMessageBody(MessageId m) throws DbException {
messageLock.readLock().lock();
try {
T txn = db.startTransaction();
try {
if(!db.containsMessage(txn, m))
throw new NoSuchMessageException();
byte[] body = db.getMessageBody(txn, m);
db.commitTransaction(txn);
return body;
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
}
} finally {
messageLock.readLock().unlock();
}
}
public MessageHeader getMessageHeader(MessageId m) throws DbException {
messageLock.readLock().lock();
try {
T txn = db.startTransaction();
try {
if(!db.containsMessage(txn, m))
throw new NoSuchMessageException();
MessageHeader h = db.getMessageHeader(txn, m);
db.commitTransaction(txn);
return h;
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
}
} finally {
messageLock.readLock().unlock();
}
}
public Collection<MessageHeader> getMessageHeaders(GroupId g)
throws DbException {
messageLock.readLock().lock();
@@ -866,6 +905,25 @@ DatabaseCleaner.Callback {
}
}
public boolean getReadFlag(MessageId m) throws DbException {
messageLock.readLock().lock();
try {
T txn = db.startTransaction();
try {
if(!db.containsMessage(txn, m))
throw new NoSuchMessageException();
boolean read = db.getReadFlag(txn, m);
db.commitTransaction(txn);
return read;
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
}
} finally {
messageLock.readLock().unlock();
}
}
public Map<ContactId, TransportProperties> getRemoteProperties(
TransportId t) throws DbException {
contactLock.readLock().lock();
@@ -918,6 +976,25 @@ DatabaseCleaner.Callback {
}
}
public boolean getStarredFlag(MessageId m) throws DbException {
messageLock.readLock().lock();
try {
T txn = db.startTransaction();
try {
if(!db.containsMessage(txn, m))
throw new NoSuchMessageException();
boolean starred = db.getStarredFlag(txn, m);
db.commitTransaction(txn);
return starred;
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
}
} finally {
messageLock.readLock().unlock();
}
}
public Collection<Group> getSubscriptions() throws DbException {
subscriptionLock.readLock().lock();
try {
@@ -1494,6 +1571,25 @@ DatabaseCleaner.Callback {
if(changed) callListeners(new RatingChangedEvent(a, r));
}
public boolean setReadFlag(MessageId m, boolean read) throws DbException {
messageLock.writeLock().lock();
try {
T txn = db.startTransaction();
try {
if(!db.containsMessage(txn, m))
throw new NoSuchMessageException();
boolean wasRead = db.setReadFlag(txn, m, read);
db.commitTransaction(txn);
return wasRead;
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
}
} finally {
messageLock.writeLock().unlock();
}
}
public void setSeen(ContactId c, Collection<MessageId> seen)
throws DbException {
contactLock.readLock().lock();
@@ -1506,9 +1602,8 @@ DatabaseCleaner.Callback {
try {
if(!db.containsContact(txn, c))
throw new NoSuchContactException();
for(MessageId m : seen) {
for(MessageId m : seen)
db.setStatusSeenIfVisible(txn, c, m);
}
db.commitTransaction(txn);
} catch(DbException e) {
db.abortTransaction(txn);
@@ -1550,6 +1645,26 @@ DatabaseCleaner.Callback {
}
}
public boolean setStarredFlag(MessageId m, boolean starred)
throws DbException {
messageLock.writeLock().lock();
try {
T txn = db.startTransaction();
try {
if(!db.containsMessage(txn, m))
throw new NoSuchMessageException();
boolean wasStarred = db.setStarredFlag(txn, m, starred);
db.commitTransaction(txn);
return wasStarred;
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
}
} finally {
messageLock.writeLock().unlock();
}
}
public void setVisibility(GroupId g, Collection<ContactId> visible)
throws DbException {
Collection<ContactId> affected = new ArrayList<ContactId>();

View File

@@ -1074,29 +1074,6 @@ abstract class JdbcDatabase implements Database<Connection> {
}
}
public byte[] getMessage(Connection txn, MessageId m) throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT length, raw FROM messages WHERE messageId = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, m.getBytes());
rs = ps.executeQuery();
if(!rs.next()) throw new DbStateException();
int length = rs.getInt(1);
byte[] raw = rs.getBlob(2).getBytes(1, length);
if(raw.length != length) throw new DbStateException();
if(rs.next()) throw new DbStateException();
rs.close();
ps.close();
return raw;
} catch(SQLException e) {
tryToClose(rs);
tryToClose(ps);
throw new DbException(e);
}
}
public byte[] getMessageBody(Connection txn, MessageId m)
throws DbException {
PreparedStatement ps = null;
@@ -1123,14 +1100,48 @@ abstract class JdbcDatabase implements Database<Connection> {
}
}
public MessageHeader getMessageHeader(Connection txn, MessageId m)
throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT parentId, authorId, groupId, subject,"
+ " timestamp, read, starred"
+ " FROM messages"
+ " WHERE messageId = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, m.getBytes());
rs = ps.executeQuery();
if(!rs.next()) throw new DbStateException();
byte[] b = rs.getBytes(1);
MessageId parent = b == null ? null : new MessageId(b);
AuthorId author = new AuthorId(rs.getBytes(2));
b = rs.getBytes(3);
GroupId group = b == null ? null : new GroupId(b);
String subject = rs.getString(4);
long timestamp = rs.getLong(5);
boolean read = rs.getBoolean(6);
boolean starred = rs.getBoolean(7);
if(rs.next()) throw new DbStateException();
rs.close();
ps.close();
return new MessageHeaderImpl(m, parent, group, author, subject,
timestamp, read, starred);
} catch(SQLException e) {
tryToClose(rs);
tryToClose(ps);
throw new DbException(e);
}
}
public Collection<MessageHeader> getMessageHeaders(Connection txn,
GroupId g) throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT m.messageId, parentId, authorId,"
+ " subject, timestamp, read, starred"
+ " FROM messages AS m"
String sql = "SELECT messageId, parentId, authorId, subject,"
+ " timestamp, read, starred"
+ " FROM messages"
+ " WHERE groupId = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, g.getBytes());
@@ -1143,8 +1154,8 @@ abstract class JdbcDatabase implements Database<Connection> {
AuthorId author = new AuthorId(rs.getBytes(3));
String subject = rs.getString(4);
long timestamp = rs.getLong(5);
boolean read = rs.getBoolean(6); // False if absent
boolean starred = rs.getBoolean(7); // False if absent
boolean read = rs.getBoolean(6);
boolean starred = rs.getBoolean(7);
headers.add(new MessageHeaderImpl(id, parent, g, author,
subject, timestamp, read, starred));
}
@@ -1158,70 +1169,6 @@ abstract class JdbcDatabase implements Database<Connection> {
}
}
public byte[] getMessageIfSendable(Connection txn, ContactId c, MessageId m)
throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
// Do we have a sendable private message with the given ID?
String sql = "SELECT length, raw FROM messages AS m"
+ " JOIN statuses AS s"
+ " ON m.messageId = s.messageId"
+ " WHERE m.messageId = ? AND m.contactId = ?"
+ " AND status = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, m.getBytes());
ps.setInt(2, c.getInt());
ps.setShort(3, (short) Status.NEW.ordinal());
rs = ps.executeQuery();
byte[] raw = null;
if(rs.next()) {
int length = rs.getInt(1);
raw = rs.getBlob(2).getBytes(1, length);
if(raw.length != length) throw new DbStateException();
}
if(rs.next()) throw new DbStateException();
rs.close();
ps.close();
if(raw != null) return raw;
// Do we have a sendable group message with the given ID?
sql = "SELECT length, raw FROM messages AS m"
+ " JOIN contactGroups AS cg"
+ " ON m.groupId = cg.groupId"
+ " JOIN groupVisibilities AS gv"
+ " ON m.groupId = gv.groupId"
+ " AND cg.contactId = gv.contactId"
+ " JOIN retentionVersions AS rv"
+ " ON cg.contactId = rv.contactId"
+ " JOIN statuses AS s"
+ " ON m.messageId = s.messageId"
+ " AND cg.contactId = s.contactId"
+ " WHERE m.messageId = ?"
+ " AND cg.contactId = ?"
+ " AND timestamp >= retention"
+ " AND status = ?"
+ " AND sendability > ZERO()";
ps = txn.prepareStatement(sql);
ps.setBytes(1, m.getBytes());
ps.setInt(2, c.getInt());
ps.setShort(3, (short) Status.NEW.ordinal());
rs = ps.executeQuery();
if(rs.next()) {
int length = rs.getInt(1);
raw = rs.getBlob(2).getBytes(1, length);
if(raw.length != length) throw new DbStateException();
}
if(rs.next()) throw new DbStateException();
rs.close();
ps.close();
return raw;
} catch(SQLException e) {
tryToClose(rs);
tryToClose(ps);
throw new DbException(e);
}
}
public Collection<MessageId> getMessagesByAuthor(Connection txn, AuthorId a)
throws DbException {
PreparedStatement ps = null;
@@ -1406,6 +1353,94 @@ abstract class JdbcDatabase implements Database<Connection> {
}
}
public byte[] getRawMessage(Connection txn, MessageId m)
throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT length, raw FROM messages WHERE messageId = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, m.getBytes());
rs = ps.executeQuery();
if(!rs.next()) throw new DbStateException();
int length = rs.getInt(1);
byte[] raw = rs.getBlob(2).getBytes(1, length);
if(raw.length != length) throw new DbStateException();
if(rs.next()) throw new DbStateException();
rs.close();
ps.close();
return raw;
} catch(SQLException e) {
tryToClose(rs);
tryToClose(ps);
throw new DbException(e);
}
}
public byte[] getRawMessageIfSendable(Connection txn, ContactId c,
MessageId m) throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
// Do we have a sendable private message with the given ID?
String sql = "SELECT length, raw FROM messages AS m"
+ " JOIN statuses AS s"
+ " ON m.messageId = s.messageId"
+ " WHERE m.messageId = ? AND m.contactId = ?"
+ " AND status = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, m.getBytes());
ps.setInt(2, c.getInt());
ps.setShort(3, (short) Status.NEW.ordinal());
rs = ps.executeQuery();
byte[] raw = null;
if(rs.next()) {
int length = rs.getInt(1);
raw = rs.getBlob(2).getBytes(1, length);
if(raw.length != length) throw new DbStateException();
}
if(rs.next()) throw new DbStateException();
rs.close();
ps.close();
if(raw != null) return raw;
// Do we have a sendable group message with the given ID?
sql = "SELECT length, raw FROM messages AS m"
+ " JOIN contactGroups AS cg"
+ " ON m.groupId = cg.groupId"
+ " JOIN groupVisibilities AS gv"
+ " ON m.groupId = gv.groupId"
+ " AND cg.contactId = gv.contactId"
+ " JOIN retentionVersions AS rv"
+ " ON cg.contactId = rv.contactId"
+ " JOIN statuses AS s"
+ " ON m.messageId = s.messageId"
+ " AND cg.contactId = s.contactId"
+ " WHERE m.messageId = ?"
+ " AND cg.contactId = ?"
+ " AND timestamp >= retention"
+ " AND status = ?"
+ " AND sendability > ZERO()";
ps = txn.prepareStatement(sql);
ps.setBytes(1, m.getBytes());
ps.setInt(2, c.getInt());
ps.setShort(3, (short) Status.NEW.ordinal());
rs = ps.executeQuery();
if(rs.next()) {
int length = rs.getInt(1);
raw = rs.getBlob(2).getBytes(1, length);
if(raw.length != length) throw new DbStateException();
}
if(rs.next()) throw new DbStateException();
rs.close();
ps.close();
return raw;
} catch(SQLException e) {
tryToClose(rs);
tryToClose(ps);
throw new DbException(e);
}
}
public boolean getReadFlag(Connection txn, MessageId m) throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
@@ -2415,7 +2450,7 @@ abstract class JdbcDatabase implements Database<Connection> {
}
}
public boolean setRead(Connection txn, MessageId m, boolean read)
public boolean setReadFlag(Connection txn, MessageId m, boolean read)
throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
@@ -2543,7 +2578,7 @@ abstract class JdbcDatabase implements Database<Connection> {
}
}
public boolean setStarred(Connection txn, MessageId m, boolean starred)
public boolean setStarredFlag(Connection txn, MessageId m, boolean starred)
throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;