From 94722a9f2a0e2bcf26f7ab06e9206ba882facd1d Mon Sep 17 00:00:00 2001
From: akwizgran <akwizgran@users.sourceforge.net>
Date: Wed, 26 Oct 2011 15:40:38 +0100
Subject: [PATCH] Broadcast an event when an author's rating changes.

---
 .../api/db/event/RatingChangedEvent.java      | 23 +++++++++++++++
 .../sf/briar/db/DatabaseComponentImpl.java    |  5 ++++
 .../sf/briar/db/DatabaseComponentTest.java    | 29 ++++++++++++++-----
 3 files changed, 50 insertions(+), 7 deletions(-)
 create mode 100644 api/net/sf/briar/api/db/event/RatingChangedEvent.java

diff --git a/api/net/sf/briar/api/db/event/RatingChangedEvent.java b/api/net/sf/briar/api/db/event/RatingChangedEvent.java
new file mode 100644
index 0000000000..a3395ef27e
--- /dev/null
+++ b/api/net/sf/briar/api/db/event/RatingChangedEvent.java
@@ -0,0 +1,23 @@
+package net.sf.briar.api.db.event;
+
+import net.sf.briar.api.Rating;
+import net.sf.briar.api.protocol.AuthorId;
+
+public class RatingChangedEvent extends DatabaseEvent {
+
+	private final AuthorId author;
+	private final Rating rating;
+
+	public RatingChangedEvent(AuthorId author, Rating rating) {
+		this.author = author;
+		this.rating = rating;
+	}
+
+	public AuthorId getAuthor() {
+		return author;
+	}
+
+	public Rating getRating() {
+		return rating;
+	}
+}
diff --git a/components/net/sf/briar/db/DatabaseComponentImpl.java b/components/net/sf/briar/db/DatabaseComponentImpl.java
index b9ae4b7c60..6bce3e9d8b 100644
--- a/components/net/sf/briar/db/DatabaseComponentImpl.java
+++ b/components/net/sf/briar/db/DatabaseComponentImpl.java
@@ -35,6 +35,7 @@ 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.RatingChangedEvent;
 import net.sf.briar.api.db.event.SubscriptionsUpdatedEvent;
 import net.sf.briar.api.db.event.TransportsUpdatedEvent;
 import net.sf.briar.api.protocol.Ack;
@@ -1233,6 +1234,7 @@ DatabaseCleaner.Callback {
 	}
 
 	public void setRating(AuthorId a, Rating r) throws DbException {
+		boolean changed;
 		messageLock.writeLock().lock();
 		try {
 			ratingLock.writeLock().lock();
@@ -1240,6 +1242,7 @@ DatabaseCleaner.Callback {
 				T txn = db.startTransaction();
 				try {
 					Rating old = db.setRating(txn, a, r);
+					changed = (old != r);
 					// Update the sendability of the author's messages
 					if(r == Rating.GOOD && old != Rating.GOOD)
 						updateAuthorSendability(txn, a, true);
@@ -1256,6 +1259,8 @@ DatabaseCleaner.Callback {
 		} finally {
 			messageLock.writeLock().unlock();
 		}
+		// Call the listeners outside the lock
+		if(changed) callListeners(new RatingChangedEvent(a, r));
 	}
 
 	public void setSeen(ContactId c, Collection<MessageId> seen)
diff --git a/test/net/sf/briar/db/DatabaseComponentTest.java b/test/net/sf/briar/db/DatabaseComponentTest.java
index 7d4efe5703..e2618ad7c3 100644
--- a/test/net/sf/briar/db/DatabaseComponentTest.java
+++ b/test/net/sf/briar/db/DatabaseComponentTest.java
@@ -19,6 +19,7 @@ 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.RatingChangedEvent;
 import net.sf.briar.api.db.event.TransportsUpdatedEvent;
 import net.sf.briar.api.protocol.Ack;
 import net.sf.briar.api.protocol.AuthorId;
@@ -114,6 +115,15 @@ public abstract class DatabaseComponentTest extends TestCase {
 			// getRating(authorId)
 			oneOf(database).getRating(txn, authorId);
 			will(returnValue(Rating.UNRATED));
+			// setRating(authorId, Rating.GOOD)
+			oneOf(database).setRating(txn, authorId, Rating.GOOD);
+			will(returnValue(Rating.UNRATED));
+			oneOf(database).getMessagesByAuthor(txn, authorId);
+			will(returnValue(Collections.<MessageId>emptyList()));
+			oneOf(listener).eventOccurred(with(any(RatingChangedEvent.class)));
+			// setRating(authorId, Rating.GOOD) again
+			oneOf(database).setRating(txn, authorId, Rating.GOOD);
+			will(returnValue(Rating.GOOD));
 			// addContact(transports)
 			oneOf(database).addContact(txn, transports, secret);
 			will(returnValue(contactId));
@@ -177,18 +187,20 @@ public abstract class DatabaseComponentTest extends TestCase {
 		db.open(false);
 		db.addListener(listener);
 		assertEquals(Rating.UNRATED, db.getRating(authorId));
+		db.setRating(authorId, Rating.GOOD); // First time - listeners called
+		db.setRating(authorId, Rating.GOOD); // Second time - not called
 		assertEquals(contactId, db.addContact(transports, secret));
 		assertEquals(Collections.singletonList(contactId), db.getContacts());
 		assertEquals(connectionWindow,
 				db.getConnectionWindow(contactId, transportId));
 		assertEquals(secret, db.getSharedSecret(contactId));
 		assertEquals(remoteProperties, db.getRemoteProperties(transportId));
-		db.subscribe(group); // First time - check listeners are called
-		db.subscribe(group); // Second time - check listeners aren't called
+		db.subscribe(group); // First time - listeners called
+		db.subscribe(group); // Second time - not called
 		assertEquals(Collections.emptyList(), db.getMessageHeaders(groupId));
 		assertEquals(Collections.singletonList(groupId), db.getSubscriptions());
-		db.unsubscribe(groupId); // First time - check listeners are called
-		db.unsubscribe(groupId); // Second time - check listeners aren't called
+		db.unsubscribe(groupId); // First time - listeners called
+		db.unsubscribe(groupId); // Second time - not called
 		db.setConnectionWindow(contactId, transportId, connectionWindow);
 		db.removeContact(contactId);
 		db.removeListener(listener);
@@ -204,10 +216,11 @@ public abstract class DatabaseComponentTest extends TestCase {
 		final Database<Object> database = context.mock(Database.class);
 		final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
 		context.checking(new Expectations() {{
-			// setRating(Rating.GOOD)
+			// setRating(authorId, Rating.GOOD)
 			allowing(database).startTransaction();
 			will(returnValue(txn));
 			oneOf(database).setRating(txn, authorId, Rating.GOOD);
+			will(returnValue(Rating.UNRATED));
 			// The sendability of the author's messages should be incremented
 			oneOf(database).getMessagesByAuthor(txn, authorId);
 			will(returnValue(Collections.singletonList(messageId)));
@@ -233,10 +246,11 @@ public abstract class DatabaseComponentTest extends TestCase {
 		final Database<Object> database = context.mock(Database.class);
 		final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
 		context.checking(new Expectations() {{
-			// setRating(Rating.GOOD)
+			// setRating(authorId, Rating.GOOD)
 			oneOf(database).startTransaction();
 			will(returnValue(txn));
 			oneOf(database).setRating(txn, authorId, Rating.GOOD);
+			will(returnValue(Rating.UNRATED));
 			// The sendability of the author's messages should be incremented
 			oneOf(database).getMessagesByAuthor(txn, authorId);
 			will(returnValue(Collections.singletonList(messageId)));
@@ -267,10 +281,11 @@ public abstract class DatabaseComponentTest extends TestCase {
 		final Database<Object> database = context.mock(Database.class);
 		final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
 		context.checking(new Expectations() {{
-			// setRating(Rating.GOOD)
+			// setRating(authorId, Rating.GOOD)
 			oneOf(database).startTransaction();
 			will(returnValue(txn));
 			oneOf(database).setRating(txn, authorId, Rating.GOOD);
+			will(returnValue(Rating.UNRATED));
 			// The sendability of the author's messages should be incremented
 			oneOf(database).getMessagesByAuthor(txn, authorId);
 			will(returnValue(Collections.singletonList(messageId)));
-- 
GitLab