diff --git a/api/net/sf/briar/api/db/DatabaseComponent.java b/api/net/sf/briar/api/db/DatabaseComponent.java index 5d4e595a93cb3f8de23d589a1dbe6bdd720bdd51..db220b51220282505f11edf4206889dc2abb5e86 100644 --- a/api/net/sf/briar/api/db/DatabaseComponent.java +++ b/api/net/sf/briar/api/db/DatabaseComponent.java @@ -98,6 +98,12 @@ public interface DatabaseComponent { void generateTransportUpdate(ContactId c, TransportWriter t) throws DbException, IOException; + /** + * Returns an outgoing connection number for the given contact and + * transport. + */ + long getConnectionNumber(ContactId c, int transportId) throws DbException; + /** * Returns the connection reordering window for the given contact and * transport. diff --git a/components/net/sf/briar/db/Database.java b/components/net/sf/briar/db/Database.java index 648f231d779d6eeb48e0faf3b92a1803bc3fcb3b..6f1141d27ef3873c161befb3793928e78c67cffc 100644 --- a/components/net/sf/briar/db/Database.java +++ b/components/net/sf/briar/db/Database.java @@ -165,6 +165,15 @@ interface Database<T> { */ Collection<BatchId> getBatchesToAck(T txn, ContactId c) throws DbException; + /** + * Allocates and returns a connection number for the given contact and + * transport. + * <p> + * Locking: contacts read, windows write. + */ + long getConnectionNumber(T txn, ContactId c, int transportId) + throws DbException; + /** * Returns the connection reordering window for the given contact and * transport. diff --git a/components/net/sf/briar/db/DatabaseComponentImpl.java b/components/net/sf/briar/db/DatabaseComponentImpl.java index 7ef8202db821f47106ff7df66f4128c27e4afe19..25e90252778f07b204279190d25259daab64d98a 100644 --- a/components/net/sf/briar/db/DatabaseComponentImpl.java +++ b/components/net/sf/briar/db/DatabaseComponentImpl.java @@ -632,6 +632,30 @@ DatabaseCleaner.Callback { LOG.fine("Added " + transports.size() + " transports to update"); } + public long getConnectionNumber(ContactId c, int transportId) + throws DbException { + contactLock.readLock().lock(); + try { + if(!containsContact(c)) throw new NoSuchContactException(); + windowLock.writeLock().lock(); + try { + T txn = db.startTransaction(); + try { + long outgoing = db.getConnectionNumber(txn, c, transportId); + db.commitTransaction(txn); + return outgoing; + } catch(DbException e) { + db.abortTransaction(txn); + throw e; + } + } finally { + windowLock.writeLock().unlock(); + } + } finally { + contactLock.readLock().unlock(); + } + } + public ConnectionWindow getConnectionWindow(ContactId c, int transportId) throws DbException { contactLock.readLock().lock(); diff --git a/components/net/sf/briar/db/JdbcDatabase.java b/components/net/sf/briar/db/JdbcDatabase.java index 8e29f1f942255c81d17ce0bb97b9d413130c19b2..65f8b40e16e0d98727e44ad0af3fac4bdc958dbb 100644 --- a/components/net/sf/briar/db/JdbcDatabase.java +++ b/components/net/sf/briar/db/JdbcDatabase.java @@ -194,6 +194,7 @@ abstract class JdbcDatabase implements Database<Connection> { + " transportId INT NOT NULL," + " centre BIGINT NOT NULL," + " bitmap INT NOT NULL," + + " outgoing BIGINT NOT NULL," + " PRIMARY KEY (contactId, transportId)," + " FOREIGN KEY (contactId) REFERENCES contacts (contactId)" + " ON DELETE CASCADE)"; @@ -805,6 +806,56 @@ abstract class JdbcDatabase implements Database<Connection> { } } + public long getConnectionNumber(Connection txn, ContactId c, + int transportId) throws DbException { + PreparedStatement ps = null; + ResultSet rs = null; + try { + String sql = "SELECT outgoing FROM connectionWindows" + + " WHERE contactId = ? AND transportId = ?"; + ps = txn.prepareStatement(sql); + ps.setInt(1, c.getInt()); + ps.setInt(2, transportId); + rs = ps.executeQuery(); + if(rs.next()) { + long outgoing = rs.getLong(1); + if(rs.next()) throw new DbStateException(); + rs.close(); + ps.close(); + sql = "UPDATE connectionWindows SET outgoing = ?" + + " WHERE contactId = ? AND transportId = ?"; + ps = txn.prepareStatement(sql); + ps.setLong(1, outgoing + 1); + ps.setInt(2, c.getInt()); + ps.setInt(3, transportId); + int affected = ps.executeUpdate(); + if(affected != 1) throw new DbStateException(); + ps.close(); + return outgoing; + } else { + rs.close(); + ps.close(); + sql = "INSERT INTO connectionWindows" + + " (contactId, transportId, centre, bitmap, outgoing)" + + " VALUES(?, ?, ?, ?, ?)"; + ps = txn.prepareStatement(sql); + ps.setInt(1, c.getInt()); + ps.setInt(2, transportId); + ps.setLong(3, 0L); + ps.setInt(4, 0); + ps.setLong(5, 0L); + int affected = ps.executeUpdate(); + if(affected != 1) throw new DbStateException(); + ps.close(); + return 0L; + } + } catch(SQLException e) { + tryToClose(rs); + tryToClose(ps); + throw new DbException(e); + } + } + public ConnectionWindow getConnectionWindow(Connection txn, ContactId c, int transportId) throws DbException { PreparedStatement ps = null; @@ -1733,23 +1784,29 @@ abstract class JdbcDatabase implements Database<Connection> { sql = "UPDATE connectionWindows SET centre = ?, bitmap = ?" + " WHERE contactId = ? AND transportId = ?"; ps = txn.prepareStatement(sql); + ps.setLong(1, w.getCentre()); + ps.setInt(2, w.getBitmap()); + ps.setInt(3, c.getInt()); + ps.setInt(4, transportId); int affected = ps.executeUpdate(); if(affected != 1) throw new DbStateException(); + ps.close(); } else { rs.close(); ps.close(); sql = "INSERT INTO connectionWindows" - + " (contactId, transportId, centre, bitmap)" - + " VALUES(?, ?, ?, ?)"; + + " (contactId, transportId, centre, bitmap, outgoing)" + + " VALUES(?, ?, ?, ?, ?)"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); ps.setInt(2, transportId); ps.setLong(3, w.getCentre()); ps.setInt(4, w.getBitmap()); + ps.setLong(5, 0L); int affected = ps.executeUpdate(); if(affected != 1) throw new DbStateException(); + ps.close(); } - ps.close(); } catch(SQLException e) { tryToClose(rs); tryToClose(ps);