diff --git a/components/net/sf/briar/db/DatabaseComponentImpl.java b/components/net/sf/briar/db/DatabaseComponentImpl.java
index 7ccc8277ec20591319668275f6b9fb9489d26aaa..ccb162b80cd9ed90e0ca4be8cc6e7ac026e3938f 100644
--- a/components/net/sf/briar/db/DatabaseComponentImpl.java
+++ b/components/net/sf/briar/db/DatabaseComponentImpl.java
@@ -21,13 +21,13 @@ import net.sf.briar.api.protocol.MessageId;
  * Abstract superclass containing code shared by ReadWriteLockDatabaseComponent
  * and SynchronizedDatabaseComponent.
  */
-abstract class DatabaseComponentImpl<Txn> implements DatabaseComponent,
+abstract class DatabaseComponentImpl<T> implements DatabaseComponent,
 DatabaseCleaner.Callback {
 
 	private static final Logger LOG =
 		Logger.getLogger(DatabaseComponentImpl.class.getName());
 
-	protected final Database<Txn> db;
+	protected final Database<T> db;
 	protected final DatabaseCleaner cleaner;
 
 	private final List<DatabaseListener> listeners =
@@ -38,7 +38,7 @@ DatabaseCleaner.Callback {
 	private long timeOfLastCheck = 0L; // Locking: spaceLock
 	private volatile boolean writesAllowed = true;
 
-	DatabaseComponentImpl(Database<Txn> db, DatabaseCleaner cleaner) {
+	DatabaseComponentImpl(Database<T> db, DatabaseCleaner cleaner) {
 		this.db = db;
 		this.cleaner = cleaner;
 	}
@@ -71,7 +71,7 @@ DatabaseCleaner.Callback {
 	 * <p>
 	 * Locking: messages write.
 	 */
-	private int calculateSendability(Txn txn, Message m) throws DbException {
+	private int calculateSendability(T txn, Message m) throws DbException {
 		int sendability = 0;
 		// One point for a good rating
 		if(db.getRating(txn, m.getAuthor()) == Rating.GOOD) sendability++;
@@ -121,7 +121,7 @@ DatabaseCleaner.Callback {
 	 * Locking: contacts read.
 	 */
 	protected boolean containsContact(ContactId c) throws DbException {
-		Txn txn = db.startTransaction();
+		T txn = db.startTransaction();
 		try {
 			boolean contains = db.containsContact(txn, c);
 			db.commitTransaction(txn);
@@ -137,7 +137,7 @@ DatabaseCleaner.Callback {
 	 * <p>
 	 * Locking: contacts read, messages write, messageStatuses write.
 	 */
-	protected void removeMessage(Txn txn, MessageId id) throws DbException {
+	protected void removeMessage(T txn, MessageId id) throws DbException {
 		Integer sendability = db.getSendability(txn, id);
 		assert sendability != null;
 		// If the message is sendable, deleting it may affect its ancestors'
@@ -176,7 +176,7 @@ DatabaseCleaner.Callback {
 	 * <p>
 	 * Locking: contacts read, messages write, messageStatuses write.
 	 */
-	protected boolean storeGroupMessage(Txn txn, Message m, ContactId sender)
+	protected boolean storeGroupMessage(T txn, Message m, ContactId sender)
 	throws DbException {
 		if(m.getGroup() == null) throw new IllegalArgumentException();
 		boolean stored = db.addGroupMessage(txn, m);
@@ -204,7 +204,7 @@ DatabaseCleaner.Callback {
 	 * Attempts to store the given messages, received from the given contact,
 	 * and returns true if any were stored.
 	 */
-	protected boolean storeMessages(Txn txn, ContactId c,
+	protected boolean storeMessages(T txn, ContactId c,
 			Collection<Message> messages) throws DbException {
 		boolean anyStored = false;
 		for(Message m : messages) {
@@ -226,7 +226,7 @@ DatabaseCleaner.Callback {
 	 * <p>
 	 * Locking: contacts read, messages write, messageStatuses write.
 	 */
-	protected boolean storePrivateMessage(Txn txn, Message m, ContactId c,
+	protected boolean storePrivateMessage(T txn, Message m, ContactId c,
 			boolean incoming) throws DbException {
 		if(m.getGroup() != null) throw new IllegalArgumentException();
 		if(m.getAuthor() != null) throw new IllegalArgumentException();
@@ -250,7 +250,7 @@ DatabaseCleaner.Callback {
 	 * @param increment True if the message's sendability has changed from 0 to
 	 * greater than 0, or false if it has changed from greater than 0 to 0.
 	 */
-	private int updateAncestorSendability(Txn txn, MessageId m,
+	private int updateAncestorSendability(T txn, MessageId m,
 			boolean increment) throws DbException {
 		int affected = 0;
 		boolean changed = true;
@@ -285,7 +285,7 @@ DatabaseCleaner.Callback {
 	 * @param increment True if the user's rating for the author has changed
 	 * from not good to good, or false if it has changed from good to not good.
 	 */
-	protected void updateAuthorSendability(Txn txn, AuthorId a,
+	protected void updateAuthorSendability(T txn, AuthorId a,
 			boolean increment) throws DbException {
 		int direct = 0, indirect = 0;
 		for(MessageId id : db.getMessagesByAuthor(txn, a)) {
diff --git a/components/net/sf/briar/db/ReadWriteLockDatabaseComponent.java b/components/net/sf/briar/db/ReadWriteLockDatabaseComponent.java
index f7c50d561935120326e6bca2044fdf38ee9d03ea..2800184cda340de0487c3f5ab6d9e402275f446e 100644
--- a/components/net/sf/briar/db/ReadWriteLockDatabaseComponent.java
+++ b/components/net/sf/briar/db/ReadWriteLockDatabaseComponent.java
@@ -38,9 +38,11 @@ import com.google.inject.Inject;
 
 /**
  * An implementation of DatabaseComponent using reentrant read-write locks.
- * This implementation can allow writers to starve.
+ * Depending on the JVM's read-write lock implementation, this implementation
+ * may allow writers to starve. LockFairnessTest can be used to test whether
+ * this implementation is safe on a given JVM.
  */
-class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
+class ReadWriteLockDatabaseComponent<T> extends DatabaseComponentImpl<T> {
 
 	private static final Logger LOG =
 		Logger.getLogger(ReadWriteLockDatabaseComponent.class.getName());
@@ -66,7 +68,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
 		new ReentrantReadWriteLock(true);
 
 	@Inject
-	ReadWriteLockDatabaseComponent(Database<Txn> db, DatabaseCleaner cleaner) {
+	ReadWriteLockDatabaseComponent(Database<T> db, DatabaseCleaner cleaner) {
 		super(db, cleaner);
 	}
 
@@ -77,7 +79,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
 			try {
 				messageStatusLock.writeLock().lock();
 				try {
-					Txn txn = db.startTransaction();
+					T txn = db.startTransaction();
 					try {
 						for(MessageId m : db.getOldMessages(txn, size)) {
 							removeMessage(txn, m);
@@ -111,7 +113,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
 		try {
 			transportLock.writeLock().lock();
 			try {
-				Txn txn = db.startTransaction();
+				T txn = db.startTransaction();
 				try {
 					c = db.addContact(txn, transports, secret);
 					db.commitTransaction(txn);
@@ -143,7 +145,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
 				try {
 					subscriptionLock.readLock().lock();
 					try {
-						Txn txn = db.startTransaction();
+						T txn = db.startTransaction();
 						try {
 							// Don't store the message if the user has
 							// unsubscribed from the group or the message
@@ -184,7 +186,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
 			try {
 				messageStatusLock.writeLock().lock();
 				try {
-					Txn txn = db.startTransaction();
+					T txn = db.startTransaction();
 					try {
 						added = storePrivateMessage(txn, m, c, false);
 						db.commitTransaction(txn);
@@ -215,7 +217,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
 			try {
 				messageStatusLock.writeLock().lock();
 				try {
-					Txn txn = db.startTransaction();
+					T txn = db.startTransaction();
 					try {
 						lost = db.getLostBatches(txn, c);
 						db.commitTransaction(txn);
@@ -240,7 +242,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
 				try {
 					messageStatusLock.writeLock().lock();
 					try {
-						Txn txn = db.startTransaction();
+						T txn = db.startTransaction();
 						try {
 							if(LOG.isLoggable(Level.FINE))
 								LOG.fine("Removing lost batch");
@@ -269,7 +271,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
 			if(!containsContact(c)) throw new NoSuchContactException();
 			messageStatusLock.writeLock().lock();
 			try {
-				Txn txn = db.startTransaction();
+				T txn = db.startTransaction();
 				try {
 					Collection<BatchId> acks = db.getBatchesToAck(txn, c);
 					Collection<BatchId> sent = new ArrayList<BatchId>();
@@ -307,7 +309,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
 				try {
 					subscriptionLock.readLock().lock();
 					try {
-						Txn txn = db.startTransaction();
+						T txn = db.startTransaction();
 						try {
 							sent = new ArrayList<MessageId>();
 							int capacity = b.getCapacity();
@@ -338,7 +340,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
 				BatchId id = b.finish();
 				messageStatusLock.writeLock().lock();
 				try {
-					Txn txn = db.startTransaction();
+					T txn = db.startTransaction();
 					try {
 						db.addOutstandingBatch(txn, c, id, sent);
 						db.commitTransaction(txn);
@@ -369,7 +371,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
 				try{
 					subscriptionLock.readLock().lock();
 					try {
-						Txn txn = db.startTransaction();
+						T txn = db.startTransaction();
 						try {
 							sent = new ArrayList<MessageId>();
 							considered = new ArrayList<MessageId>();
@@ -406,7 +408,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
 				BatchId id = b.finish();
 				messageStatusLock.writeLock().lock();
 				try {
-					Txn txn = db.startTransaction();
+					T txn = db.startTransaction();
 					try {
 						db.addOutstandingBatch(txn, c, id, sent);
 						db.commitTransaction(txn);
@@ -435,7 +437,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
 			try {
 				messageStatusLock.readLock().lock();
 				try {
-					Txn txn = db.startTransaction();
+					T txn = db.startTransaction();
 					try {
 						Collection<MessageId> sendable =
 							db.getSendableMessages(txn, c, Integer.MAX_VALUE);
@@ -474,7 +476,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
 			if(!containsContact(c)) throw new NoSuchContactException();
 			subscriptionLock.readLock().lock();
 			try {
-				Txn txn = db.startTransaction();
+				T txn = db.startTransaction();
 				try {
 					Map<Group, Long> subs = db.getVisibleSubscriptions(txn, c);
 					s.writeSubscriptions(subs, System.currentTimeMillis());
@@ -503,7 +505,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
 			if(!containsContact(c)) throw new NoSuchContactException();
 			transportLock.readLock().lock();
 			try {
-				Txn txn = db.startTransaction();
+				T txn = db.startTransaction();
 				try {
 					Map<String, Map<String, String>> transports =
 						db.getTransports(txn);
@@ -533,7 +535,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
 			if(!containsContact(c)) throw new NoSuchContactException();
 			windowLock.readLock().lock();
 			try {
-				Txn txn = db.startTransaction();
+				T txn = db.startTransaction();
 				try {
 					ConnectionWindow w =
 						db.getConnectionWindow(txn, c, transportId);
@@ -554,7 +556,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
 	public Collection<ContactId> getContacts() throws DbException {
 		contactLock.readLock().lock();
 		try {
-			Txn txn = db.startTransaction();
+			T txn = db.startTransaction();
 			try {
 				Collection<ContactId> contacts = db.getContacts(txn);
 				db.commitTransaction(txn);
@@ -571,7 +573,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
 	public Rating getRating(AuthorId a) throws DbException {
 		ratingLock.readLock().lock();
 		try {
-			Txn txn = db.startTransaction();
+			T txn = db.startTransaction();
 			try {
 				Rating r = db.getRating(txn, a);
 				db.commitTransaction(txn);
@@ -589,7 +591,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
 		contactLock.readLock().lock();
 		try {
 			if(!containsContact(c)) throw new NoSuchContactException();
-			Txn txn = db.startTransaction();
+			T txn = db.startTransaction();
 			try {
 				byte[] secret = db.getSharedSecret(txn, c);
 				db.commitTransaction(txn);
@@ -606,7 +608,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
 	public Collection<Group> getSubscriptions() throws DbException {
 		subscriptionLock.readLock().lock();
 		try {
-			Txn txn = db.startTransaction();
+			T txn = db.startTransaction();
 			try {
 				Collection<Group> subs = db.getSubscriptions(txn);
 				db.commitTransaction(txn);
@@ -624,7 +626,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
 	throws DbException {
 		transportLock.readLock().lock();
 		try {
-			Txn txn = db.startTransaction();
+			T txn = db.startTransaction();
 			try {
 				Map<String, String> config = db.getTransportConfig(txn, name);
 				db.commitTransaction(txn);
@@ -641,7 +643,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
 	public Map<String, Map<String, String>> getTransports() throws DbException {
 		transportLock.readLock().lock();
 		try {
-			Txn txn = db.startTransaction();
+			T txn = db.startTransaction();
 			try {
 				Map<String, Map<String, String>> transports =
 					db.getTransports(txn);
@@ -663,7 +665,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
 			if(!containsContact(c)) throw new NoSuchContactException();
 			transportLock.readLock().lock();
 			try {
-				Txn txn = db.startTransaction();
+				T txn = db.startTransaction();
 				try {
 					Map<String, Map<String, String>> transports =
 						db.getTransports(txn, c);
@@ -686,7 +688,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
 		try {
 			subscriptionLock.readLock().lock();
 			try {
-				Txn txn = db.startTransaction();
+				T txn = db.startTransaction();
 				try {
 					Collection<ContactId> visible = db.getVisibility(txn, g);
 					db.commitTransaction(txn);
@@ -713,7 +715,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
 				try {
 					subscriptionLock.readLock().lock();
 					try {
-						Txn txn = db.startTransaction();
+						T txn = db.startTransaction();
 						try {
 							boolean has = db.hasSendableMessages(txn, c);
 							db.commitTransaction(txn);
@@ -747,7 +749,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
 				try {
 					Collection<BatchId> acks = a.getBatchIds();
 					for(BatchId ack : acks) {
-						Txn txn = db.startTransaction();
+						T txn = db.startTransaction();
 						try {
 							db.removeAckedBatch(txn, c, ack);
 							db.commitTransaction(txn);
@@ -781,7 +783,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
 				try {
 					subscriptionLock.readLock().lock();
 					try {
-						Txn txn = db.startTransaction();
+						T txn = db.startTransaction();
 						try {
 							anyAdded = storeMessages(txn, c, b.getMessages());
 							db.addBatchToAck(txn, c, b.getId());
@@ -819,7 +821,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
 					try {
 						Collection<MessageId> offered = o.getMessageIds();
 						BitSet request = new BitSet(offered.size());
-						Txn txn = db.startTransaction();
+						T txn = db.startTransaction();
 						try {
 							Iterator<MessageId> it = offered.iterator();
 							for(int i = 0; it.hasNext(); i++) {
@@ -857,7 +859,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
 			if(!containsContact(c)) throw new NoSuchContactException();
 			subscriptionLock.writeLock().lock();
 			try {
-				Txn txn = db.startTransaction();
+				T txn = db.startTransaction();
 				try {
 					Map<Group, Long> subs = s.getSubscriptions();
 					db.setSubscriptions(txn, c, subs, s.getTimestamp());
@@ -884,7 +886,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
 			if(!containsContact(c)) throw new NoSuchContactException();
 			transportLock.writeLock().lock();
 			try {
-				Txn txn = db.startTransaction();
+				T txn = db.startTransaction();
 				try {
 					Map<String, Map<String, String>> transports =
 						t.getTransports();
@@ -917,7 +919,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
 					try {
 						transportLock.writeLock().lock();
 						try {
-							Txn txn = db.startTransaction();
+							T txn = db.startTransaction();
 							try {
 								db.removeContact(txn, c);
 								db.commitTransaction(txn);
@@ -951,7 +953,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
 			if(!containsContact(c)) throw new NoSuchContactException();
 			windowLock.writeLock().lock();
 			try {
-				Txn txn = db.startTransaction();
+				T txn = db.startTransaction();
 				try {
 					db.setConnectionWindow(txn, c, transportId, w);
 					db.commitTransaction(txn);
@@ -971,7 +973,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
 		try {
 			ratingLock.writeLock().lock();
 			try {
-				Txn txn = db.startTransaction();
+				T txn = db.startTransaction();
 				try {
 					Rating old = db.setRating(txn, a, r);
 					// Update the sendability of the author's messages
@@ -997,7 +999,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
 		boolean changed = false;
 		transportLock.writeLock().lock();
 		try {
-			Txn txn = db.startTransaction();
+			T txn = db.startTransaction();
 			try {
 				Map<String, String> old = db.getTransportConfig(txn, name);
 				if(!config.equals(old)) {
@@ -1021,7 +1023,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
 		boolean changed = false;
 		transportLock.writeLock().lock();
 		try {
-			Txn txn = db.startTransaction();
+			T txn = db.startTransaction();
 			try {
 				Map<String, String> old = db.getTransports(txn).get(name);
 				if(!properties.equals(old)) {
@@ -1046,7 +1048,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
 		try {
 			subscriptionLock.writeLock().lock();
 			try {
-				Txn txn = db.startTransaction();
+				T txn = db.startTransaction();
 				try {
 					// Remove any ex-contacts from the set
 					Collection<ContactId> present =
@@ -1073,7 +1075,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
 		boolean added = false;
 		subscriptionLock.writeLock().lock();
 		try {
-			Txn txn = db.startTransaction();
+			T txn = db.startTransaction();
 			try {
 				if(db.containsSubscription(txn, g.getId())) {
 					db.addSubscription(txn, g);
@@ -1102,7 +1104,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
 				try {
 					subscriptionLock.writeLock().lock();
 					try {
-						Txn txn = db.startTransaction();
+						T txn = db.startTransaction();
 						try {
 							if(db.containsSubscription(txn, g)) {
 								db.removeSubscription(txn, g);