DatabaseComponent throws an exception instead of returning silently if a contact is removed during an operation involving that contact. More unit tests.

This commit is contained in:
akwizgran
2011-07-05 18:15:44 +01:00
parent 13b3d4cc03
commit 5d768a5718
14 changed files with 282 additions and 134 deletions

View File

@@ -32,6 +32,16 @@ import net.sf.briar.api.protocol.MessageId;
*/
interface Database<T> {
/**
* A batch sent to a contact is considered lost when this many bundles have
* been received from the contact since the batch was sent.
* <p>
* FIXME: Come up with a better retransmission scheme. This scheme doesn't
* cope well with transports that have high latency but send bundles
* frequently.
*/
static final int RETRANSMIT_THRESHOLD = 3;
/**
* Opens the database.
* @param resume True to reopen an existing database, false to create a

View File

@@ -18,7 +18,6 @@ import java.util.logging.Level;
import java.util.logging.Logger;
import net.sf.briar.api.db.ContactId;
import net.sf.briar.api.db.DatabaseComponent;
import net.sf.briar.api.db.DbException;
import net.sf.briar.api.db.Rating;
import net.sf.briar.api.db.Status;
@@ -527,7 +526,7 @@ abstract class JdbcDatabase implements Database<Connection> {
rs.close();
ps.close();
Set<BatchId> lost;
if(received == DatabaseComponent.RETRANSMIT_THRESHOLD) {
if(received == RETRANSMIT_THRESHOLD) {
// Expire batches related to the oldest received bundle
assert oldestBundle != null;
lost = findLostBatches(txn, c, oldestBundle);

View File

@@ -7,8 +7,9 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.sf.briar.api.db.DbException;
import net.sf.briar.api.db.ContactId;
import net.sf.briar.api.db.DbException;
import net.sf.briar.api.db.NoSuchContactException;
import net.sf.briar.api.db.Rating;
import net.sf.briar.api.protocol.AuthorId;
import net.sf.briar.api.protocol.Batch;
@@ -120,7 +121,10 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
// unsubscribed from the group
if(db.containsSubscription(txn, m.getGroup())) {
boolean added = storeMessage(txn, m, null);
assert added;
if(!added) {
if(LOG.isLoggable(Level.FINE))
LOG.fine("Duplicate local message");
}
} else {
if(LOG.isLoggable(Level.FINE))
LOG.fine("Not subscribed");
@@ -177,7 +181,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
// Ack all batches received from c
contactLock.readLock().lock();
try {
if(!containsContact(c)) return;
if(!containsContact(c)) throw new NoSuchContactException();
messageStatusLock.writeLock().lock();
try {
Txn txn = db.startTransaction();
@@ -203,7 +207,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
// Add a list of subscriptions
contactLock.readLock().lock();
try {
if(!containsContact(c)) return;
if(!containsContact(c)) throw new NoSuchContactException();
subscriptionLock.readLock().lock();
try {
Txn txn = db.startTransaction();
@@ -246,7 +250,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
private Batch fillBatch(ContactId c, long capacity) throws DbException {
contactLock.readLock().lock();
try {
if(!containsContact(c)) return null;
if(!containsContact(c)) throw new NoSuchContactException();
messageLock.readLock().lock();
try {
Set<MessageId> sent;
@@ -344,7 +348,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
// Mark all messages in acked batches as seen
contactLock.readLock().lock();
try {
if(!containsContact(c)) return;
if(!containsContact(c)) throw new NoSuchContactException();
messageLock.readLock().lock();
try {
messageStatusLock.writeLock().lock();
@@ -375,7 +379,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
// Update the contact's subscriptions
contactLock.readLock().lock();
try {
if(!containsContact(c)) return;
if(!containsContact(c)) throw new NoSuchContactException();
messageStatusLock.writeLock().lock();
try {
Txn txn = db.startTransaction();
@@ -406,7 +410,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
waitForPermissionToWrite();
contactLock.readLock().lock();
try {
if(!containsContact(c)) return;
if(!containsContact(c)) throw new NoSuchContactException();
messageLock.writeLock().lock();
try {
messageStatusLock.writeLock().lock();
@@ -451,7 +455,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
Set<BatchId> lost;
contactLock.readLock().lock();
try {
if(!containsContact(c)) return;
if(!containsContact(c)) throw new NoSuchContactException();
messageLock.readLock().lock();
try {
messageStatusLock.writeLock().lock();
@@ -476,7 +480,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
for(BatchId batch : lost) {
contactLock.readLock().lock();
try {
if(!containsContact(c)) return;
if(!containsContact(c)) throw new NoSuchContactException();
messageLock.readLock().lock();
try {
messageStatusLock.writeLock().lock();

View File

@@ -8,6 +8,7 @@ import java.util.logging.Logger;
import net.sf.briar.api.db.DbException;
import net.sf.briar.api.db.ContactId;
import net.sf.briar.api.db.NoSuchContactException;
import net.sf.briar.api.db.Rating;
import net.sf.briar.api.protocol.AuthorId;
import net.sf.briar.api.protocol.Batch;
@@ -89,7 +90,10 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
// unsubscribed from the group
if(db.containsSubscription(txn, m.getGroup())) {
boolean added = storeMessage(txn, m, null);
assert added;
if(!added) {
if(LOG.isLoggable(Level.FINE))
LOG.fine("Duplicate local message");
}
} else {
if(LOG.isLoggable(Level.FINE))
LOG.fine("Not subscribed");
@@ -128,7 +132,7 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
if(LOG.isLoggable(Level.FINE)) LOG.fine("Generating bundle for " + c);
// Ack all batches received from c
synchronized(contactLock) {
if(!containsContact(c)) return;
if(!containsContact(c)) throw new NoSuchContactException();
synchronized(messageStatusLock) {
Txn txn = db.startTransaction();
try {
@@ -148,7 +152,7 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
}
// Add a list of subscriptions
synchronized(contactLock) {
if(!containsContact(c)) return;
if(!containsContact(c)) throw new NoSuchContactException();
synchronized(subscriptionLock) {
Txn txn = db.startTransaction();
try {
@@ -185,7 +189,7 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
private Batch fillBatch(ContactId c, long capacity) throws DbException {
synchronized(contactLock) {
if(!containsContact(c)) return null;
if(!containsContact(c)) throw new NoSuchContactException();
synchronized(messageLock) {
synchronized(messageStatusLock) {
Txn txn = db.startTransaction();
@@ -254,7 +258,7 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
+ b.getSize() + " bytes");
// Mark all messages in acked batches as seen
synchronized(contactLock) {
if(!containsContact(c)) return;
if(!containsContact(c)) throw new NoSuchContactException();
synchronized(messageLock) {
synchronized(messageStatusLock) {
int acks = 0;
@@ -276,7 +280,7 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
}
// Update the contact's subscriptions
synchronized(contactLock) {
if(!containsContact(c)) return;
if(!containsContact(c)) throw new NoSuchContactException();
synchronized(messageStatusLock) {
Txn txn = db.startTransaction();
try {
@@ -301,7 +305,7 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
batches++;
waitForPermissionToWrite();
synchronized(contactLock) {
if(!containsContact(c)) return;
if(!containsContact(c)) throw new NoSuchContactException();
synchronized(messageLock) {
synchronized(messageStatusLock) {
synchronized(subscriptionLock) {
@@ -334,7 +338,7 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
// Find any lost batches that need to be retransmitted
Set<BatchId> lost;
synchronized(contactLock) {
if(!containsContact(c)) return;
if(!containsContact(c)) throw new NoSuchContactException();
synchronized(messageLock) {
synchronized(messageStatusLock) {
Txn txn = db.startTransaction();
@@ -350,7 +354,7 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
}
for(BatchId batch : lost) {
synchronized(contactLock) {
if(!containsContact(c)) return;
if(!containsContact(c)) throw new NoSuchContactException();
synchronized(messageLock) {
synchronized(messageStatusLock) {
Txn txn = db.startTransaction();