Added lastConnected timestamp to Contact, for display in contact list.

This commit is contained in:
akwizgran
2013-02-27 15:12:01 +00:00
parent b4de633823
commit 1e8a8d985d
5 changed files with 152 additions and 37 deletions

View File

@@ -4,10 +4,12 @@ public class Contact {
private final ContactId id; private final ContactId id;
private final String name; private final String name;
private final long lastConnected;
public Contact(ContactId id, String name) { public Contact(ContactId id, String name, long lastConnected) {
this.id = id; this.id = id;
this.name = name; this.name = name;
this.lastConnected = lastConnected;
} }
public ContactId getId() { public ContactId getId() {
@@ -18,6 +20,10 @@ public class Contact {
return name; return name;
} }
public long getLastConnected() {
return lastConnected;
}
@Override @Override
public int hashCode() { public int hashCode() {
return id.hashCode(); return id.hashCode();

View File

@@ -81,7 +81,8 @@ interface Database<T> {
* Adds a contact with the given name to the database and returns an ID for * Adds a contact with the given name to the database and returns an ID for
* the contact. * the contact.
* <p> * <p>
* Locking: contact write, subscription write. * Locking: contact write, retention write, subscription write, transport
* write, window write.
*/ */
ContactId addContact(T txn, String name) throws DbException; ContactId addContact(T txn, String name) throws DbException;
@@ -210,7 +211,7 @@ interface Database<T> {
/** /**
* Returns all contacts. * Returns all contacts.
* <p> * <p>
* Locking: contact read. * Locking: contact read, window read.
*/ */
Collection<Contact> getContacts(T txn) throws DbException; Collection<Contact> getContacts(T txn) throws DbException;
@@ -239,6 +240,14 @@ interface Database<T> {
*/ */
MessageId getGroupMessageParent(T txn, MessageId m) throws DbException; MessageId getGroupMessageParent(T txn, MessageId m) throws DbException;
/**
* Returns the time at which a connection to the given contact was last
* opened or closed.
* <p>
* Locking: contact read, window read.
*/
long getLastConnected(T txn, ContactId c) throws DbException;
/** /**
* Returns the local transport properties for the given transport. * Returns the local transport properties for the given transport.
* <p> * <p>
@@ -526,8 +535,8 @@ interface Database<T> {
/** /**
* Removes a contact (and all associated state) from the database. * Removes a contact (and all associated state) from the database.
* <p> * <p>
* Locking: contact write, message write, subscription write, * Locking: contact write, message write, retention write,
* transport write, window write. * subscription write, transport write, window write.
*/ */
void removeContact(T txn, ContactId c) throws DbException; void removeContact(T txn, ContactId c) throws DbException;
@@ -587,6 +596,14 @@ interface Database<T> {
void setConnectionWindow(T txn, ContactId c, TransportId t, long period, void setConnectionWindow(T txn, ContactId c, TransportId t, long period,
long centre, byte[] bitmap) throws DbException; long centre, byte[] bitmap) throws DbException;
/**
* Sets the time at which a connection to the given contact was last
* opened or closed.
* <p>
* Locking: contact read, window write.
*/
void setLastConnected(T txn, ContactId c, long now) throws DbException;
/** /**
* Sets the user's rating for the given author. * Sets the user's rating for the given author.
* <p> * <p>

View File

@@ -176,18 +176,33 @@ DatabaseCleaner.Callback {
ContactId c; ContactId c;
contactLock.writeLock().lock(); contactLock.writeLock().lock();
try { try {
subscriptionLock.writeLock().lock(); retentionLock.writeLock().lock();
try { try {
T txn = db.startTransaction(); subscriptionLock.writeLock().lock();
try { try {
c = db.addContact(txn, name); transportLock.writeLock().lock();
db.commitTransaction(txn); try {
} catch(DbException e) { windowLock.writeLock().lock();
db.abortTransaction(txn); try {
throw e; T txn = db.startTransaction();
try {
c = db.addContact(txn, name);
db.commitTransaction(txn);
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
}
} finally {
windowLock.writeLock().unlock();
}
} finally {
transportLock.writeLock().unlock();
}
} finally {
subscriptionLock.writeLock().unlock();
} }
} finally { } finally {
subscriptionLock.writeLock().unlock(); retentionLock.writeLock().unlock();
} }
} finally { } finally {
contactLock.writeLock().unlock(); contactLock.writeLock().unlock();
@@ -805,14 +820,19 @@ DatabaseCleaner.Callback {
public Collection<Contact> getContacts() throws DbException { public Collection<Contact> getContacts() throws DbException {
contactLock.readLock().lock(); contactLock.readLock().lock();
try { try {
T txn = db.startTransaction(); windowLock.readLock().lock();
try { try {
Collection<Contact> contacts = db.getContacts(txn); T txn = db.startTransaction();
db.commitTransaction(txn); try {
return contacts; Collection<Contact> contacts = db.getContacts(txn);
} catch(DbException e) { db.commitTransaction(txn);
db.abortTransaction(txn); return contacts;
throw e; } catch(DbException e) {
db.abortTransaction(txn);
throw e;
}
} finally {
windowLock.readLock().unlock();
} }
} finally { } finally {
contactLock.readLock().unlock(); contactLock.readLock().unlock();
@@ -1140,6 +1160,7 @@ DatabaseCleaner.Callback {
throw new NoSuchTransportException(); throw new NoSuchTransportException();
long counter = db.incrementConnectionCounter(txn, c, t, long counter = db.incrementConnectionCounter(txn, c, t,
period); period);
db.setLastConnected(txn, c, clock.currentTimeMillis());
db.commitTransaction(txn); db.commitTransaction(txn);
return counter; return counter;
} catch(DbException e) { } catch(DbException e) {
@@ -1467,30 +1488,35 @@ DatabaseCleaner.Callback {
try { try {
messageLock.writeLock().lock(); messageLock.writeLock().lock();
try { try {
subscriptionLock.writeLock().lock(); retentionLock.writeLock().lock();
try { try {
transportLock.writeLock().lock(); subscriptionLock.writeLock().lock();
try { try {
windowLock.writeLock().lock(); transportLock.writeLock().lock();
try { try {
T txn = db.startTransaction(); windowLock.writeLock().lock();
try { try {
if(!db.containsContact(txn, c)) T txn = db.startTransaction();
throw new NoSuchContactException(); try {
db.removeContact(txn, c); if(!db.containsContact(txn, c))
db.commitTransaction(txn); throw new NoSuchContactException();
} catch(DbException e) { db.removeContact(txn, c);
db.abortTransaction(txn); db.commitTransaction(txn);
throw e; } catch(DbException e) {
db.abortTransaction(txn);
throw e;
}
} finally {
windowLock.writeLock().unlock();
} }
} finally { } finally {
windowLock.writeLock().unlock(); transportLock.writeLock().unlock();
} }
} finally { } finally {
transportLock.writeLock().unlock(); subscriptionLock.writeLock().unlock();
} }
} finally { } finally {
subscriptionLock.writeLock().unlock(); retentionLock.writeLock().unlock();
} }
} finally { } finally {
messageLock.writeLock().unlock(); messageLock.writeLock().unlock();
@@ -1536,6 +1562,7 @@ DatabaseCleaner.Callback {
throw new NoSuchTransportException(); throw new NoSuchTransportException();
db.setConnectionWindow(txn, c, t, period, centre, db.setConnectionWindow(txn, c, t, period, centre,
bitmap); bitmap);
db.setLastConnected(txn, c, clock.currentTimeMillis());
db.commitTransaction(txn); db.commitTransaction(txn);
} catch(DbException e) { } catch(DbException e) {
db.abortTransaction(txn); db.abortTransaction(txn);

View File

@@ -305,6 +305,16 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " REFERENCES transports (transportId)" + " REFERENCES transports (transportId)"
+ " ON DELETE CASCADE)"; + " ON DELETE CASCADE)";
// Locking: contact read, window
private static final String CREATE_CONNECTION_TIMES =
"CREATE TABLE connectionTimes"
+ " (contactId INT NOT NULL,"
+ " lastConnected BIGINT NOT NULL,"
+ " PRIMARY KEY (contactId),"
+ " FOREIGN KEY (contactId)"
+ " REFERENCES contacts (contactId)"
+ " ON DELETE CASCADE)";
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(JdbcDatabase.class.getName()); Logger.getLogger(JdbcDatabase.class.getName());
@@ -383,6 +393,7 @@ abstract class JdbcDatabase implements Database<Connection> {
s.executeUpdate(insertTypeNames(CREATE_CONTACT_TRANSPORT_VERSIONS)); s.executeUpdate(insertTypeNames(CREATE_CONTACT_TRANSPORT_VERSIONS));
s.executeUpdate(insertTypeNames(CREATE_ENDPOINTS)); s.executeUpdate(insertTypeNames(CREATE_ENDPOINTS));
s.executeUpdate(insertTypeNames(CREATE_SECRETS)); s.executeUpdate(insertTypeNames(CREATE_SECRETS));
s.executeUpdate(insertTypeNames(CREATE_CONNECTION_TIMES));
s.close(); s.close();
} catch(SQLException e) { } catch(SQLException e) {
tryToClose(s); tryToClose(s);
@@ -519,6 +530,15 @@ abstract class JdbcDatabase implements Database<Connection> {
if(rs.next()) throw new DbStateException(); if(rs.next()) throw new DbStateException();
rs.close(); rs.close();
ps.close(); ps.close();
// Create a connection time row
sql = "INSERT INTO connectionTimes (contactId, lastConnected)"
+ " VALUES (?, ?)";
ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt());
ps.setLong(2, clock.currentTimeMillis());
affected = ps.executeUpdate();
if(affected != 1) throw new DbStateException();
ps.close();
// Create a retention version row // Create a retention version row
sql = "INSERT INTO retentionVersions (contactId, retention," sql = "INSERT INTO retentionVersions (contactId, retention,"
+ " localVersion, localAcked, remoteVersion, remoteAcked," + " localVersion, localAcked, remoteVersion, remoteAcked,"
@@ -1032,14 +1052,18 @@ abstract class JdbcDatabase implements Database<Connection> {
PreparedStatement ps = null; PreparedStatement ps = null;
ResultSet rs = null; ResultSet rs = null;
try { try {
String sql = "SELECT contactId, name FROM contacts"; String sql = "SELECT contactId, name, lastConnected"
+ " FROM contacts AS c"
+ " JOIN connectionTimes AS ct"
+ " ON c.contactId = ct.contactId";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
rs = ps.executeQuery(); rs = ps.executeQuery();
List<Contact> contacts = new ArrayList<Contact>(); List<Contact> contacts = new ArrayList<Contact>();
while(rs.next()) { while(rs.next()) {
ContactId id = new ContactId(rs.getInt(1)); ContactId id = new ContactId(rs.getInt(1));
String name = rs.getString(2); String name = rs.getString(2);
contacts.add(new Contact(id, name)); long lastConnected = rs.getLong(3);
contacts.add(new Contact(id, name, lastConnected));
} }
rs.close(); rs.close();
ps.close(); ps.close();
@@ -1116,6 +1140,29 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
public long getLastConnected(Connection txn, ContactId c)
throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT lastConnected FROM connectionTimes"
+ " WHERE contactId = ?";
ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt());
rs = ps.executeQuery();
if(!rs.next()) throw new DbStateException();
long lastConnected = rs.getLong(1);
if(rs.next()) throw new DbStateException();
rs.close();
ps.close();
return lastConnected;
} catch(SQLException e) {
tryToClose(rs);
tryToClose(ps);
throw new DbException(e);
}
}
public TransportProperties getLocalProperties(Connection txn, TransportId t) public TransportProperties getLocalProperties(Connection txn, TransportId t)
throws DbException { throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
@@ -2508,6 +2555,24 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
public void setLastConnected(Connection txn, ContactId c, long now)
throws DbException {
PreparedStatement ps = null;
try {
String sql = "UPDATE connectionTimes SET lastConnected = ?"
+ " WHERE contactId = ?";
ps = txn.prepareStatement(sql);
ps.setLong(1, now);
ps.setInt(2, c.getInt());
int affected = ps.executeUpdate();
if(affected < 1) throw new DbStateException();
ps.close();
} catch(SQLException e) {
tryToClose(ps);
throw new DbException(e);
}
}
public Rating setRating(Connection txn, AuthorId a, Rating r) public Rating setRating(Connection txn, AuthorId a, Rating r)
throws DbException { throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;

View File

@@ -81,7 +81,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
timestamp = System.currentTimeMillis(); timestamp = System.currentTimeMillis();
size = 1234; size = 1234;
raw = new byte[size]; raw = new byte[size];
contact = new Contact(contactId, contactName); contact = new Contact(contactId, contactName, timestamp);
message = new TestMessage(messageId, null, groupId, authorId, subject, message = new TestMessage(messageId, null, groupId, authorId, subject,
timestamp, raw); timestamp, raw);
privateMessage = new TestMessage(messageId, null, null, null, subject, privateMessage = new TestMessage(messageId, null, null, null, subject,