From ec56b123843bf0605a0375ca6453c227e170bc76 Mon Sep 17 00:00:00 2001
From: akwizgran <akwizgran@users.sourceforge.net>
Date: Mon, 17 Oct 2011 22:47:38 +0100
Subject: [PATCH] Converted database events from an enum to classes to allow
 them to carry data.

---
 .../sf/briar/api/db/DatabaseComponent.java    |  1 +
 api/net/sf/briar/api/db/DatabaseListener.java | 15 -----
 .../api/db/event/BatchReceivedEvent.java      |  5 ++
 .../briar/api/db/event/ContactAddedEvent.java | 16 ++++++
 .../api/db/event/ContactRemovedEvent.java     | 10 ++++
 .../sf/briar/api/db/event/DatabaseEvent.java  |  5 ++
 .../briar/api/db/event/DatabaseListener.java  |  8 +++
 .../api/db/event/MessagesAddedEvent.java      |  5 ++
 .../db/event/SubscriptionsUpdatedEvent.java   | 19 +++++++
 .../api/db/event/TransportsUpdatedEvent.java  |  5 ++
 components/net/sf/briar/db/Database.java      |  5 +-
 .../sf/briar/db/DatabaseComponentImpl.java    | 37 ++++++------
 components/net/sf/briar/db/JdbcDatabase.java  |  5 +-
 .../transport/ConnectionRecogniserImpl.java   |  9 ++-
 .../net/sf/briar/transport/stream/Flags.java  |  2 +-
 .../transport/stream/StreamConnection.java    | 57 +++++++++++--------
 .../sf/briar/db/DatabaseComponentTest.java    | 33 ++++++-----
 .../batch/BatchConnectionReadWriteTest.java   |  8 ++-
 18 files changed, 165 insertions(+), 80 deletions(-)
 delete mode 100644 api/net/sf/briar/api/db/DatabaseListener.java
 create mode 100644 api/net/sf/briar/api/db/event/BatchReceivedEvent.java
 create mode 100644 api/net/sf/briar/api/db/event/ContactAddedEvent.java
 create mode 100644 api/net/sf/briar/api/db/event/ContactRemovedEvent.java
 create mode 100644 api/net/sf/briar/api/db/event/DatabaseEvent.java
 create mode 100644 api/net/sf/briar/api/db/event/DatabaseListener.java
 create mode 100644 api/net/sf/briar/api/db/event/MessagesAddedEvent.java
 create mode 100644 api/net/sf/briar/api/db/event/SubscriptionsUpdatedEvent.java
 create mode 100644 api/net/sf/briar/api/db/event/TransportsUpdatedEvent.java

diff --git a/api/net/sf/briar/api/db/DatabaseComponent.java b/api/net/sf/briar/api/db/DatabaseComponent.java
index 80afa5e11f..047ee2a0d4 100644
--- a/api/net/sf/briar/api/db/DatabaseComponent.java
+++ b/api/net/sf/briar/api/db/DatabaseComponent.java
@@ -9,6 +9,7 @@ import net.sf.briar.api.Rating;
 import net.sf.briar.api.TransportConfig;
 import net.sf.briar.api.TransportId;
 import net.sf.briar.api.TransportProperties;
+import net.sf.briar.api.db.event.DatabaseListener;
 import net.sf.briar.api.protocol.Ack;
 import net.sf.briar.api.protocol.AuthorId;
 import net.sf.briar.api.protocol.Batch;
diff --git a/api/net/sf/briar/api/db/DatabaseListener.java b/api/net/sf/briar/api/db/DatabaseListener.java
deleted file mode 100644
index 6dc1a31ff0..0000000000
--- a/api/net/sf/briar/api/db/DatabaseListener.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package net.sf.briar.api.db;
-
-/** An interface for receiving notifications when database events occur. */
-public interface DatabaseListener {
-
-	static enum Event {
-		BATCH_RECEIVED,
-		CONTACTS_UPDATED,
-		MESSAGES_ADDED,
-		SUBSCRIPTIONS_UPDATED,
-		TRANSPORTS_UPDATED
-	};
-
-	void eventOccurred(Event e);
-}
diff --git a/api/net/sf/briar/api/db/event/BatchReceivedEvent.java b/api/net/sf/briar/api/db/event/BatchReceivedEvent.java
new file mode 100644
index 0000000000..6791a53108
--- /dev/null
+++ b/api/net/sf/briar/api/db/event/BatchReceivedEvent.java
@@ -0,0 +1,5 @@
+package net.sf.briar.api.db.event;
+
+public class BatchReceivedEvent extends DatabaseEvent {
+
+}
diff --git a/api/net/sf/briar/api/db/event/ContactAddedEvent.java b/api/net/sf/briar/api/db/event/ContactAddedEvent.java
new file mode 100644
index 0000000000..2d02c03de9
--- /dev/null
+++ b/api/net/sf/briar/api/db/event/ContactAddedEvent.java
@@ -0,0 +1,16 @@
+package net.sf.briar.api.db.event;
+
+import net.sf.briar.api.ContactId;
+
+public class ContactAddedEvent extends DatabaseEvent {
+
+	private final ContactId contactId;
+
+	public ContactAddedEvent(ContactId contactId) {
+		this.contactId = contactId;
+	}
+
+	public ContactId getContactId() {
+		return contactId;
+	}
+}
diff --git a/api/net/sf/briar/api/db/event/ContactRemovedEvent.java b/api/net/sf/briar/api/db/event/ContactRemovedEvent.java
new file mode 100644
index 0000000000..e9322fcaa5
--- /dev/null
+++ b/api/net/sf/briar/api/db/event/ContactRemovedEvent.java
@@ -0,0 +1,10 @@
+package net.sf.briar.api.db.event;
+
+import net.sf.briar.api.ContactId;
+
+public class ContactRemovedEvent extends ContactAddedEvent {
+
+	public ContactRemovedEvent(ContactId contactId) {
+		super(contactId);
+	}
+}
diff --git a/api/net/sf/briar/api/db/event/DatabaseEvent.java b/api/net/sf/briar/api/db/event/DatabaseEvent.java
new file mode 100644
index 0000000000..ff3b363dc8
--- /dev/null
+++ b/api/net/sf/briar/api/db/event/DatabaseEvent.java
@@ -0,0 +1,5 @@
+package net.sf.briar.api.db.event;
+
+public abstract class DatabaseEvent {
+
+}
diff --git a/api/net/sf/briar/api/db/event/DatabaseListener.java b/api/net/sf/briar/api/db/event/DatabaseListener.java
new file mode 100644
index 0000000000..d55be9b767
--- /dev/null
+++ b/api/net/sf/briar/api/db/event/DatabaseListener.java
@@ -0,0 +1,8 @@
+package net.sf.briar.api.db.event;
+
+
+/** An interface for receiving notifications when database events occur. */
+public interface DatabaseListener {
+
+	void eventOccurred(DatabaseEvent e);
+}
diff --git a/api/net/sf/briar/api/db/event/MessagesAddedEvent.java b/api/net/sf/briar/api/db/event/MessagesAddedEvent.java
new file mode 100644
index 0000000000..d8efb677be
--- /dev/null
+++ b/api/net/sf/briar/api/db/event/MessagesAddedEvent.java
@@ -0,0 +1,5 @@
+package net.sf.briar.api.db.event;
+
+public class MessagesAddedEvent extends DatabaseEvent {
+
+}
diff --git a/api/net/sf/briar/api/db/event/SubscriptionsUpdatedEvent.java b/api/net/sf/briar/api/db/event/SubscriptionsUpdatedEvent.java
new file mode 100644
index 0000000000..416a49752a
--- /dev/null
+++ b/api/net/sf/briar/api/db/event/SubscriptionsUpdatedEvent.java
@@ -0,0 +1,19 @@
+package net.sf.briar.api.db.event;
+
+import java.util.Collection;
+
+import net.sf.briar.api.ContactId;
+
+public class SubscriptionsUpdatedEvent extends DatabaseEvent {
+
+	private final Collection<ContactId> affectedContacts;
+
+	// FIXME: Replace this constructor
+	public SubscriptionsUpdatedEvent() {
+		affectedContacts = null;
+	}
+
+	public Collection<ContactId> getAffectedContacts() {
+		return affectedContacts;
+	}
+}
diff --git a/api/net/sf/briar/api/db/event/TransportsUpdatedEvent.java b/api/net/sf/briar/api/db/event/TransportsUpdatedEvent.java
new file mode 100644
index 0000000000..0778eacf83
--- /dev/null
+++ b/api/net/sf/briar/api/db/event/TransportsUpdatedEvent.java
@@ -0,0 +1,5 @@
+package net.sf.briar.api.db.event;
+
+public class TransportsUpdatedEvent extends DatabaseEvent {
+
+}
diff --git a/components/net/sf/briar/db/Database.java b/components/net/sf/briar/db/Database.java
index 542c04729b..7a423ae9a3 100644
--- a/components/net/sf/briar/db/Database.java
+++ b/components/net/sf/briar/db/Database.java
@@ -406,13 +406,14 @@ interface Database<T> {
 	void removeMessage(T txn, MessageId m) throws DbException;
 
 	/**
-	 * Unsubscribes from the given group. Any messages belonging to the group
+	 * Unsubscribes from the given group and returns true if a subscription
+	 * previously existed. Any messages belonging to the group
 	 * are deleted from the database.
 	 * <p>
 	 * Locking: contacts read, messages write, messageStatuses write,
 	 * subscriptions write.
 	 */
-	void removeSubscription(T txn, GroupId g) throws DbException;
+	boolean removeSubscription(T txn, GroupId g) throws DbException;
 
 	/**
 	 * Sets the configuration for the given transport, replacing any existing
diff --git a/components/net/sf/briar/db/DatabaseComponentImpl.java b/components/net/sf/briar/db/DatabaseComponentImpl.java
index 59848a6271..927ca49ce8 100644
--- a/components/net/sf/briar/db/DatabaseComponentImpl.java
+++ b/components/net/sf/briar/db/DatabaseComponentImpl.java
@@ -25,11 +25,17 @@ import net.sf.briar.api.TransportConfig;
 import net.sf.briar.api.TransportId;
 import net.sf.briar.api.TransportProperties;
 import net.sf.briar.api.db.DatabaseComponent;
-import net.sf.briar.api.db.DatabaseListener;
-import net.sf.briar.api.db.DatabaseListener.Event;
 import net.sf.briar.api.db.DbException;
 import net.sf.briar.api.db.NoSuchContactException;
 import net.sf.briar.api.db.Status;
+import net.sf.briar.api.db.event.BatchReceivedEvent;
+import net.sf.briar.api.db.event.ContactAddedEvent;
+import net.sf.briar.api.db.event.ContactRemovedEvent;
+import net.sf.briar.api.db.event.DatabaseEvent;
+import net.sf.briar.api.db.event.DatabaseListener;
+import net.sf.briar.api.db.event.MessagesAddedEvent;
+import net.sf.briar.api.db.event.SubscriptionsUpdatedEvent;
+import net.sf.briar.api.db.event.TransportsUpdatedEvent;
 import net.sf.briar.api.protocol.Ack;
 import net.sf.briar.api.protocol.AuthorId;
 import net.sf.briar.api.protocol.Batch;
@@ -146,12 +152,12 @@ DatabaseCleaner.Callback {
 			contactLock.writeLock().unlock();
 		}
 		// Call the listeners outside the lock
-		callListeners(Event.CONTACTS_UPDATED);
+		callListeners(new ContactAddedEvent(c));
 		return c;
 	}
 
 	/** Notifies all listeners of a database event. */
-	private void callListeners(DatabaseListener.Event e) {
+	private void callListeners(DatabaseEvent e) {
 		synchronized(listeners) {
 			if(!listeners.isEmpty()) {
 				// Shuffle the listeners so we don't always send new messages
@@ -199,7 +205,7 @@ DatabaseCleaner.Callback {
 			contactLock.readLock().unlock();
 		}
 		// Call the listeners outside the lock
-		if(added) callListeners(Event.MESSAGES_ADDED);
+		if(added) callListeners(new MessagesAddedEvent());
 	}
 
 	/**
@@ -316,7 +322,7 @@ DatabaseCleaner.Callback {
 			contactLock.readLock().unlock();
 		}
 		// Call the listeners outside the lock
-		if(added) callListeners(Event.MESSAGES_ADDED);
+		if(added) callListeners(new MessagesAddedEvent());
 	}
 
 	/**
@@ -937,8 +943,8 @@ DatabaseCleaner.Callback {
 			contactLock.readLock().unlock();
 		}
 		// Call the listeners outside the lock
-		callListeners(Event.BATCH_RECEIVED);
-		if(anyAdded) callListeners(Event.MESSAGES_ADDED);
+		callListeners(new BatchReceivedEvent());
+		if(anyAdded) callListeners(new MessagesAddedEvent());
 	}
 
 	/**
@@ -1102,7 +1108,7 @@ DatabaseCleaner.Callback {
 			contactLock.writeLock().unlock();
 		}
 		// Call the listeners outside the lock
-		callListeners(Event.CONTACTS_UPDATED);
+		callListeners(new ContactRemovedEvent(c));
 	}
 
 	public void setConfig(TransportId t, TransportConfig config)
@@ -1126,7 +1132,7 @@ DatabaseCleaner.Callback {
 			transportLock.writeLock().unlock();
 		}
 		// Call the listeners outside the lock
-		if(changed) callListeners(Event.TRANSPORTS_UPDATED);
+		if(changed) callListeners(new TransportsUpdatedEvent());
 	}
 
 	public void setConnectionWindow(ContactId c, TransportId t,
@@ -1172,7 +1178,7 @@ DatabaseCleaner.Callback {
 			transportLock.writeLock().unlock();
 		}
 		// Call the listeners outside the lock
-		if(changed) callListeners(Event.TRANSPORTS_UPDATED);
+		if(changed) callListeners(new TransportsUpdatedEvent());
 	}
 
 	public void setRating(AuthorId a, Rating r) throws DbException {
@@ -1317,7 +1323,7 @@ DatabaseCleaner.Callback {
 			subscriptionLock.writeLock().unlock();
 		}
 		// Call the listeners outside the lock
-		if(added) callListeners(Event.SUBSCRIPTIONS_UPDATED);
+		if(added) callListeners(new SubscriptionsUpdatedEvent());
 	}
 
 	public void unsubscribe(GroupId g) throws DbException {
@@ -1333,10 +1339,7 @@ DatabaseCleaner.Callback {
 					try {
 						T txn = db.startTransaction();
 						try {
-							if(db.containsSubscription(txn, g)) {
-								db.removeSubscription(txn, g);
-								removed = true;
-							}
+							removed = db.removeSubscription(txn, g);
 							db.commitTransaction(txn);
 						} catch(DbException e) {
 							db.abortTransaction(txn);
@@ -1355,7 +1358,7 @@ DatabaseCleaner.Callback {
 			contactLock.readLock().unlock();
 		}
 		// Call the listeners outside the lock
-		if(removed) callListeners(Event.SUBSCRIPTIONS_UPDATED);
+		if(removed) callListeners(new SubscriptionsUpdatedEvent());
 	}
 
 	public void checkFreeSpaceAndClean() throws DbException {
diff --git a/components/net/sf/briar/db/JdbcDatabase.java b/components/net/sf/briar/db/JdbcDatabase.java
index f3e901d128..59c5fd7e05 100644
--- a/components/net/sf/briar/db/JdbcDatabase.java
+++ b/components/net/sf/briar/db/JdbcDatabase.java
@@ -1754,7 +1754,7 @@ abstract class JdbcDatabase implements Database<Connection> {
 		}
 	}
 
-	public void removeSubscription(Connection txn, GroupId g)
+	public boolean removeSubscription(Connection txn, GroupId g)
 	throws DbException {
 		PreparedStatement ps = null;
 		try {
@@ -1762,8 +1762,9 @@ abstract class JdbcDatabase implements Database<Connection> {
 			ps = txn.prepareStatement(sql);
 			ps.setBytes(1, g.getBytes());
 			int affected = ps.executeUpdate();
-			if(affected != 1) throw new DbStateException();
+			if(affected > 1) throw new DbStateException();
 			ps.close();
+			return affected > 0;
 		} catch(SQLException e) {
 			tryToClose(ps);
 			throw new DbException(e);
diff --git a/components/net/sf/briar/transport/ConnectionRecogniserImpl.java b/components/net/sf/briar/transport/ConnectionRecogniserImpl.java
index db36cca779..e2787b0baf 100644
--- a/components/net/sf/briar/transport/ConnectionRecogniserImpl.java
+++ b/components/net/sf/briar/transport/ConnectionRecogniserImpl.java
@@ -16,9 +16,12 @@ import net.sf.briar.api.ContactId;
 import net.sf.briar.api.TransportId;
 import net.sf.briar.api.crypto.CryptoComponent;
 import net.sf.briar.api.db.DatabaseComponent;
-import net.sf.briar.api.db.DatabaseListener;
 import net.sf.briar.api.db.DbException;
 import net.sf.briar.api.db.NoSuchContactException;
+import net.sf.briar.api.db.event.ContactAddedEvent;
+import net.sf.briar.api.db.event.ContactRemovedEvent;
+import net.sf.briar.api.db.event.DatabaseEvent;
+import net.sf.briar.api.db.event.DatabaseListener;
 import net.sf.briar.api.transport.ConnectionRecogniser;
 import net.sf.briar.api.transport.ConnectionWindow;
 
@@ -127,9 +130,9 @@ DatabaseListener {
 		return contactId;
 	}
 
-	public void eventOccurred(Event e) {
+	public void eventOccurred(DatabaseEvent e) {
 		// When the set of contacts changes we need to re-initialise everything
-		if(e == Event.CONTACTS_UPDATED) {
+		if(e instanceof ContactAddedEvent || e instanceof ContactRemovedEvent) {
 			synchronized(this) {
 				initialised = false;
 			}
diff --git a/components/net/sf/briar/transport/stream/Flags.java b/components/net/sf/briar/transport/stream/Flags.java
index fa43ea507b..422c41e8ff 100644
--- a/components/net/sf/briar/transport/stream/Flags.java
+++ b/components/net/sf/briar/transport/stream/Flags.java
@@ -4,7 +4,7 @@ interface Flags {
 
 	// Flags raised by the database listener
 	static final int BATCH_RECEIVED = 1;
-	static final int CONTACTS_UPDATED = 2;
+	static final int CONTACT_REMOVED = 2;
 	static final int MESSAGES_ADDED = 4;
 	static final int SUBSCRIPTIONS_UPDATED = 8;
 	static final int TRANSPORTS_UPDATED = 16;
diff --git a/components/net/sf/briar/transport/stream/StreamConnection.java b/components/net/sf/briar/transport/stream/StreamConnection.java
index c533c443bc..8683d25fd1 100644
--- a/components/net/sf/briar/transport/stream/StreamConnection.java
+++ b/components/net/sf/briar/transport/stream/StreamConnection.java
@@ -14,8 +14,14 @@ import net.sf.briar.api.ContactId;
 import net.sf.briar.api.FormatException;
 import net.sf.briar.api.TransportId;
 import net.sf.briar.api.db.DatabaseComponent;
-import net.sf.briar.api.db.DatabaseListener;
 import net.sf.briar.api.db.DbException;
+import net.sf.briar.api.db.event.BatchReceivedEvent;
+import net.sf.briar.api.db.event.ContactRemovedEvent;
+import net.sf.briar.api.db.event.DatabaseEvent;
+import net.sf.briar.api.db.event.DatabaseListener;
+import net.sf.briar.api.db.event.MessagesAddedEvent;
+import net.sf.briar.api.db.event.SubscriptionsUpdatedEvent;
+import net.sf.briar.api.db.event.TransportsUpdatedEvent;
 import net.sf.briar.api.protocol.Ack;
 import net.sf.briar.api.protocol.Batch;
 import net.sf.briar.api.protocol.MessageId;
@@ -81,19 +87,28 @@ abstract class StreamConnection implements DatabaseListener {
 	protected abstract ConnectionWriter createConnectionWriter()
 	throws DbException, IOException ;
 
-	public void eventOccurred(Event e) {
+	public void eventOccurred(DatabaseEvent e) {
 		synchronized(this) {
-			if(e == Event.BATCH_RECEIVED)
+			if(e instanceof BatchReceivedEvent) {
 				writerFlags |= Flags.BATCH_RECEIVED;
-			else if(e == Event.CONTACTS_UPDATED)
-				writerFlags |= Flags.CONTACTS_UPDATED;
-			else if(e == Event.MESSAGES_ADDED)
+				notifyAll();
+			} else if(e instanceof ContactRemovedEvent) {
+				ContactId c = ((ContactRemovedEvent) e).getContactId();
+				if(contactId.equals(c)) {
+					writerFlags |= Flags.CONTACT_REMOVED;
+					notifyAll();
+				}
+			} else if(e instanceof MessagesAddedEvent) {
 				writerFlags |= Flags.MESSAGES_ADDED;
-			else if(e == Event.SUBSCRIPTIONS_UPDATED)
+				notifyAll();
+			} else if(e instanceof SubscriptionsUpdatedEvent) {
+				// FIXME: Check whether the change affected this contact
 				writerFlags |= Flags.SUBSCRIPTIONS_UPDATED;
-			else if(e == Event.TRANSPORTS_UPDATED)
+				notifyAll();
+			} else if(e instanceof TransportsUpdatedEvent) {
 				writerFlags |= Flags.TRANSPORTS_UPDATED;
-			notifyAll();
+				notifyAll();
+			}
 		}
 	}
 
@@ -207,11 +222,9 @@ abstract class StreamConnection implements DatabaseListener {
 						writerFlags = 0;
 					}
 					// Handle the flags in approximate order of urgency
-					if((flags & Flags.CONTACTS_UPDATED) != 0) {
-						if(!db.getContacts().contains(contactId)) {
-							connection.dispose(true);
-							return;
-						}
+					if((flags & Flags.CONTACT_REMOVED) != 0) {
+						connection.dispose(true);
+						return;
 					}
 					if((flags & Flags.TRANSPORTS_UPDATED) != 0) {
 						sendTransports(transportWriter);
@@ -246,11 +259,9 @@ abstract class StreamConnection implements DatabaseListener {
 						writerFlags = 0;
 					}
 					// Handle the flags in approximate order of urgency
-					if((flags & Flags.CONTACTS_UPDATED) != 0) {
-						if(!db.getContacts().contains(contactId)) {
-							connection.dispose(true);
-							return;
-						}
+					if((flags & Flags.CONTACT_REMOVED) != 0) {
+						connection.dispose(true);
+						return;
 					}
 					if((flags & Flags.TRANSPORTS_UPDATED) != 0) {
 						sendTransports(transportWriter);
@@ -279,11 +290,9 @@ abstract class StreamConnection implements DatabaseListener {
 						writerFlags = 0;
 					}
 					// Handle the flags in approximate order of urgency
-					if((flags & Flags.CONTACTS_UPDATED) != 0) {
-						if(!db.getContacts().contains(contactId)) {
-							connection.dispose(true);
-							return;
-						}
+					if((flags & Flags.CONTACT_REMOVED) != 0) {
+						connection.dispose(true);
+						return;
 					}
 					if((flags & Flags.TRANSPORTS_UPDATED) != 0) {
 						sendTransports(transportWriter);
diff --git a/test/net/sf/briar/db/DatabaseComponentTest.java b/test/net/sf/briar/db/DatabaseComponentTest.java
index 476cc49f99..f629c64512 100644
--- a/test/net/sf/briar/db/DatabaseComponentTest.java
+++ b/test/net/sf/briar/db/DatabaseComponentTest.java
@@ -14,10 +14,14 @@ import net.sf.briar.api.TransportConfig;
 import net.sf.briar.api.TransportId;
 import net.sf.briar.api.TransportProperties;
 import net.sf.briar.api.db.DatabaseComponent;
-import net.sf.briar.api.db.DatabaseListener;
-import net.sf.briar.api.db.DatabaseListener.Event;
 import net.sf.briar.api.db.NoSuchContactException;
 import net.sf.briar.api.db.Status;
+import net.sf.briar.api.db.event.ContactAddedEvent;
+import net.sf.briar.api.db.event.ContactRemovedEvent;
+import net.sf.briar.api.db.event.DatabaseListener;
+import net.sf.briar.api.db.event.MessagesAddedEvent;
+import net.sf.briar.api.db.event.SubscriptionsUpdatedEvent;
+import net.sf.briar.api.db.event.TransportsUpdatedEvent;
 import net.sf.briar.api.protocol.Ack;
 import net.sf.briar.api.protocol.AuthorId;
 import net.sf.briar.api.protocol.Batch;
@@ -112,7 +116,7 @@ public abstract class DatabaseComponentTest extends TestCase {
 			// addContact(transports)
 			oneOf(database).addContact(txn, transports, secret);
 			will(returnValue(contactId));
-			oneOf(listener).eventOccurred(Event.CONTACTS_UPDATED);
+			oneOf(listener).eventOccurred(with(any(ContactAddedEvent.class)));
 			// getContacts()
 			oneOf(database).getContacts(txn);
 			will(returnValue(Collections.singletonList(contactId)));
@@ -135,7 +139,8 @@ public abstract class DatabaseComponentTest extends TestCase {
 			oneOf(database).containsSubscription(txn, groupId);
 			will(returnValue(false));
 			oneOf(database).addSubscription(txn, group);
-			oneOf(listener).eventOccurred(Event.SUBSCRIPTIONS_UPDATED);
+			oneOf(listener).eventOccurred(with(any(
+					SubscriptionsUpdatedEvent.class)));
 			// subscribe(group) again
 			oneOf(group).getId();
 			will(returnValue(groupId));
@@ -145,12 +150,12 @@ public abstract class DatabaseComponentTest extends TestCase {
 			oneOf(database).getSubscriptions(txn);
 			will(returnValue(Collections.singletonList(groupId)));
 			// unsubscribe(groupId)
-			oneOf(database).containsSubscription(txn, groupId);
-			will(returnValue(true));
 			oneOf(database).removeSubscription(txn, groupId);
-			oneOf(listener).eventOccurred(Event.SUBSCRIPTIONS_UPDATED);
+			will(returnValue(true));
+			oneOf(listener).eventOccurred(with(any(
+					SubscriptionsUpdatedEvent.class)));
 			// unsubscribe(groupId) again
-			oneOf(database).containsSubscription(txn, groupId);
+			oneOf(database).removeSubscription(txn, groupId);
 			will(returnValue(false));
 			// setConnectionWindow(contactId, 123, connectionWindow)
 			oneOf(database).containsContact(txn, contactId);
@@ -159,7 +164,7 @@ public abstract class DatabaseComponentTest extends TestCase {
 					connectionWindow);
 			// removeContact(contactId)
 			oneOf(database).removeContact(txn, contactId);
-			oneOf(listener).eventOccurred(Event.CONTACTS_UPDATED);
+			oneOf(listener).eventOccurred(with(any(ContactRemovedEvent.class)));
 			// close()
 			oneOf(cleaner).stopCleaning();
 			oneOf(database).close();
@@ -1177,7 +1182,7 @@ public abstract class DatabaseComponentTest extends TestCase {
 			oneOf(database).setSendability(txn, messageId, 0);
 			oneOf(database).commitTransaction(txn);
 			// The message was added, so the listener should be called
-			oneOf(listener).eventOccurred(Event.MESSAGES_ADDED);
+			oneOf(listener).eventOccurred(with(any(MessagesAddedEvent.class)));
 		}});
 		DatabaseComponent db = createDatabaseComponent(database, cleaner);
 
@@ -1205,7 +1210,7 @@ public abstract class DatabaseComponentTest extends TestCase {
 			will(returnValue(true));
 			oneOf(database).setStatus(txn, contactId, messageId, Status.NEW);
 			// The message was added, so the listener should be called
-			oneOf(listener).eventOccurred(Event.MESSAGES_ADDED);
+			oneOf(listener).eventOccurred(with(any(MessagesAddedEvent.class)));
 		}});
 		DatabaseComponent db = createDatabaseComponent(database, cleaner);
 
@@ -1290,7 +1295,8 @@ public abstract class DatabaseComponentTest extends TestCase {
 			oneOf(database).setLocalProperties(txn, transportId,
 					properties1);
 			oneOf(database).commitTransaction(txn);
-			oneOf(listener).eventOccurred(Event.TRANSPORTS_UPDATED);
+			oneOf(listener).eventOccurred(with(any(
+					TransportsUpdatedEvent.class)));
 		}});
 		DatabaseComponent db = createDatabaseComponent(database, cleaner);
 
@@ -1344,7 +1350,8 @@ public abstract class DatabaseComponentTest extends TestCase {
 			will(returnValue(config));
 			oneOf(database).setConfig(txn, transportId, config1);
 			oneOf(database).commitTransaction(txn);
-			oneOf(listener).eventOccurred(Event.TRANSPORTS_UPDATED);
+			oneOf(listener).eventOccurred(with(any(
+					TransportsUpdatedEvent.class)));
 		}});
 		DatabaseComponent db = createDatabaseComponent(database, cleaner);
 
diff --git a/test/net/sf/briar/transport/batch/BatchConnectionReadWriteTest.java b/test/net/sf/briar/transport/batch/BatchConnectionReadWriteTest.java
index a495686c99..0973b16757 100644
--- a/test/net/sf/briar/transport/batch/BatchConnectionReadWriteTest.java
+++ b/test/net/sf/briar/transport/batch/BatchConnectionReadWriteTest.java
@@ -17,7 +17,9 @@ import net.sf.briar.api.ContactId;
 import net.sf.briar.api.TransportId;
 import net.sf.briar.api.TransportProperties;
 import net.sf.briar.api.db.DatabaseComponent;
-import net.sf.briar.api.db.DatabaseListener;
+import net.sf.briar.api.db.event.DatabaseEvent;
+import net.sf.briar.api.db.event.DatabaseListener;
+import net.sf.briar.api.db.event.MessagesAddedEvent;
 import net.sf.briar.api.protocol.Message;
 import net.sf.briar.api.protocol.MessageEncoder;
 import net.sf.briar.api.protocol.ProtocolReaderFactory;
@@ -163,8 +165,8 @@ public class BatchConnectionReadWriteTest extends TestCase {
 
 		private boolean messagesAdded = false;
 
-		public void eventOccurred(Event e) {
-			if(e == Event.MESSAGES_ADDED) messagesAdded = true;
+		public void eventOccurred(DatabaseEvent e) {
+			if(e instanceof MessagesAddedEvent) messagesAdded = true;
 		}
 	}
 
-- 
GitLab