Contact IDs are now auto-generated.

This commit is contained in:
akwizgran
2011-07-06 14:53:35 +01:00
parent 5e0d580d00
commit 9fbf0f21de
7 changed files with 133 additions and 34 deletions

View File

@@ -32,8 +32,8 @@ public interface DatabaseComponent {
/** Waits for any open transactions to finish and closes the database. */ /** Waits for any open transactions to finish and closes the database. */
void close() throws DbException; void close() throws DbException;
/** Adds a new contact to the database. */ /** Adds a new contact to the database and returns an ID for the contact. */
void addContact(ContactId c) throws DbException; ContactId addContact() throws DbException;
/** Adds a locally generated message to the database. */ /** Adds a locally generated message to the database. */
void addLocallyGeneratedMessage(Message m) throws DbException; void addLocallyGeneratedMessage(Message m) throws DbException;
@@ -44,6 +44,9 @@ public interface DatabaseComponent {
*/ */
void generateBundle(ContactId c, Bundle b) throws DbException; void generateBundle(ContactId c, Bundle b) throws DbException;
/** Returns the IDs of all contacts. */
Set<ContactId> getContacts() throws DbException;
/** Returns the user's rating for the given author. */ /** Returns the user's rating for the given author. */
Rating getRating(AuthorId a) throws DbException; Rating getRating(AuthorId a) throws DbException;

View File

@@ -74,6 +74,13 @@ interface Database<T> {
*/ */
void addBatchToAck(T txn, ContactId c, BatchId b) throws DbException; void addBatchToAck(T txn, ContactId c, BatchId b) throws DbException;
/**
* Adds a new contact to the database and returns an ID for the contact.
* <p>
* Locking: contacts write, messageStatuses write.
*/
ContactId addContact(T txn) throws DbException;
/** /**
* Returns false if the given message is already in the database. Otherwise * Returns false if the given message is already in the database. Otherwise
* stores the message and returns true. * stores the message and returns true.
@@ -82,13 +89,6 @@ interface Database<T> {
*/ */
boolean addMessage(T txn, Message m) throws DbException; boolean addMessage(T txn, Message m) throws DbException;
/**
* Adds a new contact to the database.
* <p>
* Locking: contacts write, messageStatuses write.
*/
void addContact(T txn, ContactId c) throws DbException;
/** /**
* Records a sent batch as needing to be acknowledged. * Records a sent batch as needing to be acknowledged.
* <p> * <p>

View File

@@ -126,7 +126,7 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " (bundleId XXXX NOT NULL," + " (bundleId XXXX NOT NULL,"
+ " contactId INT NOT NULL," + " contactId INT NOT NULL,"
+ " timestamp BIGINT NOT NULL," + " timestamp BIGINT NOT NULL,"
+ " PRIMARY KEY (bundleId)," + " PRIMARY KEY (bundleId, contactId),"
+ " FOREIGN KEY (contactId) REFERENCES contacts (contactId)" + " FOREIGN KEY (contactId) REFERENCES contacts (contactId)"
+ " ON DELETE CASCADE)"; + " ON DELETE CASCADE)";
@@ -363,10 +363,24 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
public void addContact(Connection txn, ContactId c) throws DbException { public ContactId addContact(Connection txn) throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
ResultSet rs = null;
try { try {
String sql = "INSERT INTO contacts" // Get the highest existing contact ID
String sql = "SELECT contactId FROM contacts"
+ " ORDER BY contactId DESC LIMIT ?";
ps = txn.prepareStatement(sql);
ps.setInt(1, 1);
rs = ps.executeQuery();
int nextId = rs.next() ? rs.getInt(1) + 1 : 1;
ContactId c = new ContactId(nextId);
boolean more = rs.next();
assert !more;
rs.close();
ps.close();
// Create a new contact row
sql = "INSERT INTO contacts"
+ " (contactId, lastBundleReceived)" + " (contactId, lastBundleReceived)"
+ " VALUES (?, ?)"; + " VALUES (?, ?)";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
@@ -375,6 +389,7 @@ abstract class JdbcDatabase implements Database<Connection> {
int rowsAffected = ps.executeUpdate(); int rowsAffected = ps.executeUpdate();
assert rowsAffected == 1; assert rowsAffected == 1;
ps.close(); ps.close();
// Create a dummy received bundle row for BundleId.NONE
sql = "INSERT INTO receivedBundles" sql = "INSERT INTO receivedBundles"
+ " (bundleId, contactId, timestamp)" + " (bundleId, contactId, timestamp)"
+ " VALUES (?, ?, ?)"; + " VALUES (?, ?, ?)";
@@ -385,6 +400,7 @@ abstract class JdbcDatabase implements Database<Connection> {
rowsAffected = ps.executeUpdate(); rowsAffected = ps.executeUpdate();
assert rowsAffected == 1; assert rowsAffected == 1;
ps.close(); ps.close();
return c;
} catch(SQLException e) { } catch(SQLException e) {
tryToClose(ps); tryToClose(ps);
tryToClose(txn); tryToClose(txn);

View File

@@ -83,16 +83,19 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
} }
} }
public void addContact(ContactId c) throws DbException { public ContactId addContact() throws DbException {
if(LOG.isLoggable(Level.FINE)) LOG.fine("Adding contact " + c); if(LOG.isLoggable(Level.FINE)) LOG.fine("Adding contact");
contactLock.writeLock().lock(); contactLock.writeLock().lock();
try { try {
messageStatusLock.writeLock().lock(); messageStatusLock.writeLock().lock();
try { try {
Txn txn = db.startTransaction(); Txn txn = db.startTransaction();
try { try {
db.addContact(txn, c); ContactId c = db.addContact(txn);
db.commitTransaction(txn); db.commitTransaction(txn);
if(LOG.isLoggable(Level.FINE))
LOG.fine("Added contact " + c);
return c;
} catch(DbException e) { } catch(DbException e) {
db.abortTransaction(txn); db.abortTransaction(txn);
throw e; throw e;
@@ -307,6 +310,28 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
} }
} }
public Set<ContactId> getContacts() throws DbException {
contactLock.readLock().lock();
try {
messageStatusLock.readLock().lock();
try {
Txn txn = db.startTransaction();
try {
Set<ContactId> contacts = db.getContacts(txn);
db.commitTransaction(txn);
return contacts;
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
}
} finally {
messageStatusLock.readLock().unlock();
}
} finally {
contactLock.readLock().unlock();
}
}
public Rating getRating(AuthorId a) throws DbException { public Rating getRating(AuthorId a) throws DbException {
ratingLock.readLock().lock(); ratingLock.readLock().lock();
try { try {
@@ -329,8 +354,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
try { try {
Txn txn = db.startTransaction(); Txn txn = db.startTransaction();
try { try {
HashSet<GroupId> subs = new HashSet<GroupId>(); Set<GroupId> subs = db.getSubscriptions(txn);
for(GroupId g : db.getSubscriptions(txn)) subs.add(g);
db.commitTransaction(txn); db.commitTransaction(txn);
return subs; return subs;
} catch(DbException e) { } catch(DbException e) {

View File

@@ -62,14 +62,17 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
} }
} }
public void addContact(ContactId c) throws DbException { public ContactId addContact() throws DbException {
if(LOG.isLoggable(Level.FINE)) LOG.fine("Adding contact " + c); if(LOG.isLoggable(Level.FINE)) LOG.fine("Adding contact");
synchronized(contactLock) { synchronized(contactLock) {
synchronized(messageStatusLock) { synchronized(messageStatusLock) {
Txn txn = db.startTransaction(); Txn txn = db.startTransaction();
try { try {
db.addContact(txn, c); ContactId c = db.addContact(txn);
db.commitTransaction(txn); db.commitTransaction(txn);
if(LOG.isLoggable(Level.FINE))
LOG.fine("Added contact " + c);
return c;
} catch(DbException e) { } catch(DbException e) {
db.abortTransaction(txn); db.abortTransaction(txn);
throw e; throw e;
@@ -224,6 +227,22 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
} }
} }
public Set<ContactId> getContacts() throws DbException {
synchronized(contactLock) {
synchronized(messageStatusLock) {
Txn txn = db.startTransaction();
try {
Set<ContactId> contacts = db.getContacts(txn);
db.commitTransaction(txn);
return contacts;
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
}
}
}
}
public Rating getRating(AuthorId a) throws DbException { public Rating getRating(AuthorId a) throws DbException {
synchronized(ratingLock) { synchronized(ratingLock) {
Txn txn = db.startTransaction(); Txn txn = db.startTransaction();
@@ -242,8 +261,7 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
synchronized(subscriptionLock) { synchronized(subscriptionLock) {
Txn txn = db.startTransaction(); Txn txn = db.startTransaction();
try { try {
HashSet<GroupId> subs = new HashSet<GroupId>(); Set<GroupId> subs = db.getSubscriptions(txn);
for(GroupId g : db.getSubscriptions(txn)) subs.add(g);
db.commitTransaction(txn); db.commitTransaction(txn);
return subs; return subs;
} catch(DbException e) { } catch(DbException e) {

View File

@@ -83,7 +83,11 @@ public abstract class DatabaseComponentTest extends TestCase {
oneOf(database).getRating(txn, authorId); oneOf(database).getRating(txn, authorId);
will(returnValue(Rating.UNRATED)); will(returnValue(Rating.UNRATED));
// addContact(contactId) // addContact(contactId)
oneOf(database).addContact(txn, contactId); oneOf(database).addContact(txn);
will(returnValue(contactId));
// getContacts()
oneOf(database).getContacts(txn);
will(returnValue(Collections.singleton(contactId)));
// subscribe(groupId) // subscribe(groupId)
oneOf(database).addSubscription(txn, groupId); oneOf(database).addSubscription(txn, groupId);
// getSubscriptions() // getSubscriptions()
@@ -102,7 +106,8 @@ public abstract class DatabaseComponentTest extends TestCase {
db.open(false); db.open(false);
assertEquals(Rating.UNRATED, db.getRating(authorId)); assertEquals(Rating.UNRATED, db.getRating(authorId));
db.addContact(contactId); assertEquals(contactId, db.addContact());
assertEquals(Collections.singleton(contactId), db.getContacts());
db.subscribe(groupId); db.subscribe(groupId);
assertEquals(Collections.singleton(groupId), db.getSubscriptions()); assertEquals(Collections.singleton(groupId), db.getSubscriptions());
db.unsubscribe(groupId); db.unsubscribe(groupId);

View File

@@ -55,7 +55,7 @@ public class H2DatabaseTest extends TestCase {
super(); super();
authorId = new AuthorId(TestUtils.getRandomId()); authorId = new AuthorId(TestUtils.getRandomId());
batchId = new BatchId(TestUtils.getRandomId()); batchId = new BatchId(TestUtils.getRandomId());
contactId = new ContactId(123); contactId = new ContactId(1);
groupId = new GroupId(TestUtils.getRandomId()); groupId = new GroupId(TestUtils.getRandomId());
messageId = new MessageId(TestUtils.getRandomId()); messageId = new MessageId(TestUtils.getRandomId());
timestamp = System.currentTimeMillis(); timestamp = System.currentTimeMillis();
@@ -80,7 +80,7 @@ public class H2DatabaseTest extends TestCase {
// Store some records // Store some records
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
assertFalse(db.containsContact(txn, contactId)); assertFalse(db.containsContact(txn, contactId));
db.addContact(txn, contactId); assertEquals(contactId, db.addContact(txn));
assertTrue(db.containsContact(txn, contactId)); assertTrue(db.containsContact(txn, contactId));
assertFalse(db.containsSubscription(txn, groupId)); assertFalse(db.containsSubscription(txn, groupId));
db.addSubscription(txn, groupId); db.addSubscription(txn, groupId);
@@ -124,6 +124,39 @@ public class H2DatabaseTest extends TestCase {
db.close(); db.close();
} }
@Test
public void testContactIdsIncrease() throws DbException {
ContactId contactId1 = new ContactId(2);
ContactId contactId2 = new ContactId(3);
ContactId contactId3 = new ContactId(4);
MessageFactory messageFactory = new TestMessageFactory();
// Create a new database
Database<Connection> db = open(false, messageFactory);
// Create three contacts
Connection txn = db.startTransaction();
assertFalse(db.containsContact(txn, contactId));
assertEquals(contactId, db.addContact(txn));
assertTrue(db.containsContact(txn, contactId));
assertFalse(db.containsContact(txn, contactId1));
assertEquals(contactId1, db.addContact(txn));
assertTrue(db.containsContact(txn, contactId1));
assertFalse(db.containsContact(txn, contactId2));
assertEquals(contactId2, db.addContact(txn));
assertTrue(db.containsContact(txn, contactId2));
/*
// Delete one of the contacts
db.removeContact(txn, contactId1);
assertFalse(db.containsContact(txn, contactId1));
*/
// Add another contact - a new ID should be created
assertFalse(db.containsContact(txn, contactId3));
assertEquals(contactId3, db.addContact(txn));
assertTrue(db.containsContact(txn, contactId3));
db.commitTransaction(txn);
db.close();
}
@Test @Test
public void testRatings() throws DbException { public void testRatings() throws DbException {
Mockery context = new Mockery(); Mockery context = new Mockery();
@@ -176,7 +209,7 @@ public class H2DatabaseTest extends TestCase {
Database<Connection> db = open(false, messageFactory); Database<Connection> db = open(false, messageFactory);
// Add a contact, subscribe to a group and store a message // Add a contact, subscribe to a group and store a message
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
db.addContact(txn, contactId); assertEquals(contactId, db.addContact(txn));
db.addSubscription(txn, groupId); db.addSubscription(txn, groupId);
db.addSubscription(txn, contactId, groupId); db.addSubscription(txn, contactId, groupId);
db.addMessage(txn, message); db.addMessage(txn, message);
@@ -219,7 +252,7 @@ public class H2DatabaseTest extends TestCase {
Database<Connection> db = open(false, messageFactory); Database<Connection> db = open(false, messageFactory);
// Add a contact, subscribe to a group and store a message // Add a contact, subscribe to a group and store a message
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
db.addContact(txn, contactId); assertEquals(contactId, db.addContact(txn));
db.addSubscription(txn, groupId); db.addSubscription(txn, groupId);
db.addSubscription(txn, contactId, groupId); db.addSubscription(txn, contactId, groupId);
db.addMessage(txn, message); db.addMessage(txn, message);
@@ -268,7 +301,7 @@ public class H2DatabaseTest extends TestCase {
Database<Connection> db = open(false, messageFactory); Database<Connection> db = open(false, messageFactory);
// Add a contact, subscribe to a group and store a message // Add a contact, subscribe to a group and store a message
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
db.addContact(txn, contactId); assertEquals(contactId, db.addContact(txn));
db.addSubscription(txn, groupId); db.addSubscription(txn, groupId);
db.addMessage(txn, message); db.addMessage(txn, message);
db.setSendability(txn, messageId, 1); db.setSendability(txn, messageId, 1);
@@ -310,7 +343,7 @@ public class H2DatabaseTest extends TestCase {
Database<Connection> db = open(false, messageFactory); Database<Connection> db = open(false, messageFactory);
// Add a contact, subscribe to a group and store a message // Add a contact, subscribe to a group and store a message
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
db.addContact(txn, contactId); assertEquals(contactId, db.addContact(txn));
db.addSubscription(txn, groupId); db.addSubscription(txn, groupId);
db.addSubscription(txn, contactId, groupId); db.addSubscription(txn, contactId, groupId);
db.addMessage(txn, message); db.addMessage(txn, message);
@@ -346,7 +379,7 @@ public class H2DatabaseTest extends TestCase {
Database<Connection> db = open(false, messageFactory); Database<Connection> db = open(false, messageFactory);
// Add a contact and some batches to ack // Add a contact and some batches to ack
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
db.addContact(txn, contactId); assertEquals(contactId, db.addContact(txn));
db.addBatchToAck(txn, contactId, batchId); db.addBatchToAck(txn, contactId, batchId);
db.addBatchToAck(txn, contactId, batchId1); db.addBatchToAck(txn, contactId, batchId1);
db.commitTransaction(txn); db.commitTransaction(txn);
@@ -378,7 +411,7 @@ public class H2DatabaseTest extends TestCase {
Database<Connection> db = open(false, messageFactory); Database<Connection> db = open(false, messageFactory);
// Add a contact, subscribe to a group and store a message // Add a contact, subscribe to a group and store a message
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
db.addContact(txn, contactId); assertEquals(contactId, db.addContact(txn));
db.addSubscription(txn, groupId); db.addSubscription(txn, groupId);
db.addSubscription(txn, contactId, groupId); db.addSubscription(txn, contactId, groupId);
db.addMessage(txn, message); db.addMessage(txn, message);
@@ -422,7 +455,7 @@ public class H2DatabaseTest extends TestCase {
Database<Connection> db = open(false, messageFactory); Database<Connection> db = open(false, messageFactory);
// Add a contact, subscribe to a group and store a message // Add a contact, subscribe to a group and store a message
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
db.addContact(txn, contactId); assertEquals(contactId, db.addContact(txn));
db.addSubscription(txn, groupId); db.addSubscription(txn, groupId);
db.addSubscription(txn, contactId, groupId); db.addSubscription(txn, contactId, groupId);
db.addMessage(txn, message); db.addMessage(txn, message);
@@ -476,7 +509,7 @@ public class H2DatabaseTest extends TestCase {
Database<Connection> db = open(false, messageFactory); Database<Connection> db = open(false, messageFactory);
// Add a contact // Add a contact
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
db.addContact(txn, contactId); assertEquals(contactId, db.addContact(txn));
// Add an oustanding batch (associated with BundleId.NONE) // Add an oustanding batch (associated with BundleId.NONE)
db.addOutstandingBatch(txn, contactId, batchId, empty); db.addOutstandingBatch(txn, contactId, batchId, empty);
// Receive a bundle // Receive a bundle