Shutdown manager (untested on Windows).

This commit is contained in:
akwizgran
2011-11-18 17:13:55 +00:00
parent 859ece6328
commit 046becd388
22 changed files with 467 additions and 96 deletions

View File

@@ -0,0 +1,16 @@
package net.sf.briar.api.lifecycle;
public interface ShutdownManager {
/**
* Registers a hook to be run when the JVM shuts down and returns a handle
* that can be used to remove the hook.
*/
int addShutdownHook(Runnable hook);
/**
* Removes the shutdown hook identified by the given handle and returns
* true if the hook was removed.
*/
boolean removeShutdownHook(int handle);
}

View File

@@ -21,6 +21,7 @@
</path> </path>
<target name='clean'> <target name='clean'>
<delete dir='build'/> <delete dir='build'/>
<delete dir='test.tmp'/>
</target> </target>
<target name='compile'> <target name='compile'>
<mkdir dir='build'/> <mkdir dir='build'/>

View File

@@ -16,6 +16,8 @@ import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.sf.briar.api.Bytes; import net.sf.briar.api.Bytes;
import net.sf.briar.api.ContactId; import net.sf.briar.api.ContactId;
@@ -38,6 +40,7 @@ import net.sf.briar.api.db.event.RatingChangedEvent;
import net.sf.briar.api.db.event.RemoteTransportsUpdatedEvent; import net.sf.briar.api.db.event.RemoteTransportsUpdatedEvent;
import net.sf.briar.api.db.event.SubscriptionsUpdatedEvent; import net.sf.briar.api.db.event.SubscriptionsUpdatedEvent;
import net.sf.briar.api.db.event.TransportAddedEvent; import net.sf.briar.api.db.event.TransportAddedEvent;
import net.sf.briar.api.lifecycle.ShutdownManager;
import net.sf.briar.api.protocol.Ack; import net.sf.briar.api.protocol.Ack;
import net.sf.briar.api.protocol.AuthorId; import net.sf.briar.api.protocol.AuthorId;
import net.sf.briar.api.protocol.Batch; import net.sf.briar.api.protocol.Batch;
@@ -73,6 +76,9 @@ import com.google.inject.Inject;
class DatabaseComponentImpl<T> implements DatabaseComponent, class DatabaseComponentImpl<T> implements DatabaseComponent,
DatabaseCleaner.Callback { DatabaseCleaner.Callback {
private static final Logger LOG =
Logger.getLogger(DatabaseComponentImpl.class.getName());
/* /*
* Locks must always be acquired in alphabetical order. See the Database * Locks must always be acquired in alphabetical order. See the Database
* interface to find out which calls require which locks. * interface to find out which calls require which locks.
@@ -97,27 +103,61 @@ DatabaseCleaner.Callback {
private final Database<T> db; private final Database<T> db;
private final DatabaseCleaner cleaner; private final DatabaseCleaner cleaner;
private final ShutdownManager shutdown;
private final List<DatabaseListener> listeners = private final List<DatabaseListener> listeners =
new ArrayList<DatabaseListener>(); // Locking: self new ArrayList<DatabaseListener>(); // Locking: self
private final Object spaceLock = new Object(); private final Object spaceLock = new Object();
private long bytesStoredSinceLastCheck = 0L; // Locking: spaceLock private long bytesStoredSinceLastCheck = 0L; // Locking: spaceLock
private long timeOfLastCheck = 0L; // Locking: spaceLock private long timeOfLastCheck = 0L; // Locking: spaceLock
private final Object openCloseLock = new Object();
private boolean open = false; // Locking: openCloseLock;
private int shutdownHandle = -1; // Locking: openCloseLock;
@Inject @Inject
DatabaseComponentImpl(Database<T> db, DatabaseCleaner cleaner) { DatabaseComponentImpl(Database<T> db, DatabaseCleaner cleaner,
ShutdownManager shutdown) {
this.db = db; this.db = db;
this.cleaner = cleaner; this.cleaner = cleaner;
this.shutdown = shutdown;
} }
public void open(boolean resume) throws DbException, IOException { public void open(boolean resume) throws DbException, IOException {
db.open(resume); synchronized(openCloseLock) {
cleaner.startCleaning(this, MAX_MS_BETWEEN_SPACE_CHECKS); if(open) throw new IllegalStateException();
open = true;
db.open(resume);
cleaner.startCleaning(this, MAX_MS_BETWEEN_SPACE_CHECKS);
shutdownHandle = shutdown.addShutdownHook(new Runnable() {
public void run() {
try {
synchronized(openCloseLock) {
shutdownHandle = -1;
close();
}
} catch(DbException e) {
if(LOG.isLoggable(Level.WARNING))
LOG.warning(e.getMessage());
} catch(IOException e) {
if(LOG.isLoggable(Level.WARNING))
LOG.warning(e.getMessage());
}
}
});
}
} }
public void close() throws DbException, IOException { public void close() throws DbException, IOException {
cleaner.stopCleaning(); synchronized(openCloseLock) {
db.close(); if(!open) return;
open = false;
if(shutdownHandle != -1)
shutdown.removeShutdownHook(shutdownHandle);
cleaner.stopCleaning();
db.close();
}
} }
public void addListener(DatabaseListener d) { public void addListener(DatabaseListener d) {

View File

@@ -8,6 +8,7 @@ import net.sf.briar.api.db.DatabaseComponent;
import net.sf.briar.api.db.DatabaseDirectory; import net.sf.briar.api.db.DatabaseDirectory;
import net.sf.briar.api.db.DatabaseMaxSize; import net.sf.briar.api.db.DatabaseMaxSize;
import net.sf.briar.api.db.DatabasePassword; import net.sf.briar.api.db.DatabasePassword;
import net.sf.briar.api.lifecycle.ShutdownManager;
import net.sf.briar.api.protocol.GroupFactory; import net.sf.briar.api.protocol.GroupFactory;
import net.sf.briar.api.transport.ConnectionContextFactory; import net.sf.briar.api.transport.ConnectionContextFactory;
import net.sf.briar.api.transport.ConnectionWindowFactory; import net.sf.briar.api.transport.ConnectionWindowFactory;
@@ -35,7 +36,7 @@ public class DatabaseModule extends AbstractModule {
@Provides @Singleton @Provides @Singleton
DatabaseComponent getDatabaseComponent(Database<Connection> db, DatabaseComponent getDatabaseComponent(Database<Connection> db,
DatabaseCleaner cleaner) { DatabaseCleaner cleaner, ShutdownManager shutdown) {
return new DatabaseComponentImpl<Connection>(db, cleaner); return new DatabaseComponentImpl<Connection>(db, cleaner, shutdown);
} }
} }

View File

@@ -0,0 +1,16 @@
package net.sf.briar.lifecycle;
import net.sf.briar.api.lifecycle.ShutdownManager;
import net.sf.briar.util.OsUtils;
import com.google.inject.AbstractModule;
public class LifecycleModule extends AbstractModule {
@Override
protected void configure() {
if(OsUtils.isWindows())
bind(ShutdownManager.class).to(WindowsShutdownManagerImpl.class);
else bind(ShutdownManager.class).to(ShutdownManagerImpl.class);
}
}

View File

@@ -0,0 +1,31 @@
package net.sf.briar.lifecycle;
import java.util.HashMap;
import java.util.Map;
import net.sf.briar.api.lifecycle.ShutdownManager;
class ShutdownManagerImpl implements ShutdownManager {
protected final Map<Integer, Thread> hooks; // Locking: this
private int nextHandle = 0; // Locking: this
ShutdownManagerImpl() {
hooks = new HashMap<Integer, Thread>();
}
public synchronized int addShutdownHook(Runnable runnable) {
int handle = nextHandle++;
Thread hook = new Thread(runnable);
hooks.put(handle, hook);
Runtime.getRuntime().addShutdownHook(hook);
return handle;
}
public synchronized boolean removeShutdownHook(int handle) {
Thread hook = hooks.remove(handle);
if(hook == null) return false;
else return Runtime.getRuntime().removeShutdownHook(hook);
}
}

View File

@@ -0,0 +1,142 @@
package net.sf.briar.lifecycle;
import java.awt.HeadlessException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import net.sf.briar.util.OsUtils;
import com.sun.jna.Native;
import com.sun.jna.platform.win32.WinDef.HWND;
import com.sun.jna.platform.win32.WinDef.LPARAM;
import com.sun.jna.platform.win32.WinDef.LRESULT;
import com.sun.jna.platform.win32.WinDef.WPARAM;
import com.sun.jna.win32.StdCallLibrary;
import com.sun.jna.win32.StdCallLibrary.StdCallCallback;
class WindowsShutdownManagerImpl extends ShutdownManagerImpl {
private static final Logger LOG =
Logger.getLogger(WindowsShutdownManagerImpl.class.getName());
private static final int WM_QUERYENDSESSION = 17;
private static final int WM_ENDSESSION = 22;
private static final int GWL_WNDPROC = -4;
private boolean initialised = false; // Locking: this
@Override
public synchronized int addShutdownHook(Runnable runnable) {
if(!initialised) initialise();
return super.addShutdownHook(new RunOnce(runnable));
}
// Locking: this
private void initialise() {
if(OsUtils.isWindows()) {
try {
HWND hwnd = new HWND();
hwnd.setPointer(Native.getComponentPointer(new JFrame()));
User32 u = (User32) Native.loadLibrary("user32", User32.class);
try {
// Load the 64-bit functions
setCallback64Bit(u, hwnd);
} catch(UnsatisfiedLinkError e) {
// Load the 32-bit functions
setCallback32Bit(u, hwnd);
}
} catch(HeadlessException e) {
if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.getMessage());
} catch(UnsatisfiedLinkError e) {
if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.getMessage());
}
} else {
if(LOG.isLoggable(Level.WARNING))
LOG.warning("Windows shutdown manager used on non-Windows OS");
}
initialised = true;
}
private void setCallback64Bit(final User32 user32, HWND hwnd) {
final WindowProc oldProc = user32.GetWindowLongPtrW(hwnd, GWL_WNDPROC);
WindowProc newProc = new WindowProc() {
public LRESULT callback(HWND hwnd, int msg, WPARAM wp, LPARAM lp) {
if(msg == WM_QUERYENDSESSION) {
// It's safe to delay returning from this message
runShutdownHooks();
} else if(msg == WM_ENDSESSION) {
// Return immediately or the JVM crashes on return
}
return user32.CallWindowProcPtrW(oldProc, hwnd, msg, wp, lp);
}
};
user32.SetWindowLongPtrW(hwnd, GWL_WNDPROC, newProc);
}
private void setCallback32Bit(final User32 user32, HWND hwnd) {
final WindowProc oldProc = user32.GetWindowLongW(hwnd, GWL_WNDPROC);
WindowProc newProc = new WindowProc() {
public LRESULT callback(HWND hwnd, int msg, WPARAM wp, LPARAM lp) {
if(msg == WM_QUERYENDSESSION) {
// It's safe to delay returning from this message
runShutdownHooks();
} else if(msg == WM_ENDSESSION) {
// Return immediately or the JVM crashes on return
}
return user32.CallWindowProcW(oldProc, hwnd, msg, wp, lp);
}
};
user32.SetWindowLongW(hwnd, GWL_WNDPROC, newProc);
}
// Package access for testing
synchronized void runShutdownHooks() {
// Start each hook in its own thread
for(Thread hook : hooks.values()) hook.start();
// Wait for all the hooks to finish
for(Thread hook : hooks.values()) {
try {
hook.join();
} catch(InterruptedException e) {
if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.getMessage());
}
}
}
private static class RunOnce implements Runnable {
private final Runnable runnable;
private final AtomicBoolean called = new AtomicBoolean(false);
private RunOnce(Runnable runnable) {
this.runnable = runnable;
}
public void run() {
if(called.getAndSet(true)) return;
runnable.run();
}
}
private static interface User32 extends StdCallLibrary {
LRESULT CallWindowProcW(WindowProc oldProc, HWND hwnd, int msg,
WPARAM wp, LPARAM lp);
LRESULT CallWindowProcPtrW(WindowProc oldProc, HWND hwnd, int msg,
WPARAM wp, LPARAM lp);
WindowProc GetWindowLongW(HWND hwnd, int index);
WindowProc GetWindowLongPtrW(HWND hwnd, int index);
LRESULT SetWindowLongW(HWND hwnd, int index, WindowProc newProc);
LRESULT SetWindowLongPtrW(HWND hwnd, int index, WindowProc newProc);
}
private static interface WindowProc extends StdCallCallback {
public LRESULT callback(HWND hwnd, int msg, WPARAM wp, LPARAM lp);
}
}

View File

@@ -19,7 +19,7 @@ import net.sf.briar.api.transport.StreamTransportConnection;
import net.sf.briar.util.ByteUtils; import net.sf.briar.util.ByteUtils;
/** A socket plugin that supports exchanging invitations over a LAN. */ /** A socket plugin that supports exchanging invitations over a LAN. */
public class LanSocketPlugin extends SimpleSocketPlugin { class LanSocketPlugin extends SimpleSocketPlugin {
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(LanSocketPlugin.class.getName()); Logger.getLogger(LanSocketPlugin.class.getName());

View File

@@ -22,7 +22,7 @@ import net.sf.briar.api.transport.TransportConstants;
import com.google.inject.Inject; import com.google.inject.Inject;
public class ConnectionDispatcherImpl implements ConnectionDispatcher { class ConnectionDispatcherImpl implements ConnectionDispatcher {
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(ConnectionDispatcherImpl.class.getName()); Logger.getLogger(ConnectionDispatcherImpl.class.getName());

View File

@@ -28,6 +28,7 @@ import net.sf.briar.api.db.event.DatabaseEvent;
import net.sf.briar.api.db.event.DatabaseListener; import net.sf.briar.api.db.event.DatabaseListener;
import net.sf.briar.api.db.event.RemoteTransportsUpdatedEvent; import net.sf.briar.api.db.event.RemoteTransportsUpdatedEvent;
import net.sf.briar.api.db.event.TransportAddedEvent; import net.sf.briar.api.db.event.TransportAddedEvent;
import net.sf.briar.api.lifecycle.ShutdownManager;
import net.sf.briar.api.protocol.Transport; import net.sf.briar.api.protocol.Transport;
import net.sf.briar.api.protocol.TransportId; import net.sf.briar.api.protocol.TransportId;
import net.sf.briar.api.protocol.TransportIndex; import net.sf.briar.api.protocol.TransportIndex;
@@ -46,6 +47,7 @@ DatabaseListener {
private final CryptoComponent crypto; private final CryptoComponent crypto;
private final DatabaseComponent db; private final DatabaseComponent db;
private final Executor executor; private final Executor executor;
private final ShutdownManager shutdown;
private final Cipher ivCipher; // Locking: this private final Cipher ivCipher; // Locking: this
private final Map<Bytes, Context> expected; // Locking: this private final Map<Bytes, Context> expected; // Locking: this
@@ -53,10 +55,11 @@ DatabaseListener {
@Inject @Inject
ConnectionRecogniserImpl(CryptoComponent crypto, DatabaseComponent db, ConnectionRecogniserImpl(CryptoComponent crypto, DatabaseComponent db,
Executor executor) { Executor executor, ShutdownManager shutdown) {
this.crypto = crypto; this.crypto = crypto;
this.db = db; this.db = db;
this.executor = executor; this.executor = executor;
this.shutdown = shutdown;
ivCipher = crypto.getIvCipher(); ivCipher = crypto.getIvCipher();
expected = new HashMap<Bytes, Context>(); expected = new HashMap<Bytes, Context>();
db.addListener(this); db.addListener(this);
@@ -64,8 +67,8 @@ DatabaseListener {
// Locking: this // Locking: this
private void initialise() throws DbException { private void initialise() throws DbException {
Runtime.getRuntime().addShutdownHook(new Thread() { assert !initialised;
@Override shutdown.addShutdownHook(new Runnable() {
public void run() { public void run() {
eraseSecrets(); eraseSecrets();
} }

View File

@@ -13,7 +13,7 @@ import net.sf.briar.api.transport.ConnectionWriter;
import net.sf.briar.api.transport.ConnectionWriterFactory; import net.sf.briar.api.transport.ConnectionWriterFactory;
import net.sf.briar.api.transport.StreamTransportConnection; import net.sf.briar.api.transport.StreamTransportConnection;
public class IncomingStreamConnection extends StreamConnection { class IncomingStreamConnection extends StreamConnection {
private final ConnectionContext ctx; private final ConnectionContext ctx;
private final byte[] encryptedIv; private final byte[] encryptedIv;

View File

@@ -15,7 +15,7 @@ import net.sf.briar.api.transport.ConnectionWriter;
import net.sf.briar.api.transport.ConnectionWriterFactory; import net.sf.briar.api.transport.ConnectionWriterFactory;
import net.sf.briar.api.transport.StreamTransportConnection; import net.sf.briar.api.transport.StreamTransportConnection;
public class OutgoingStreamConnection extends StreamConnection { class OutgoingStreamConnection extends StreamConnection {
private final TransportIndex transportIndex; private final TransportIndex transportIndex;

View File

@@ -13,7 +13,7 @@ import net.sf.briar.api.transport.StreamTransportConnection;
import com.google.inject.Inject; import com.google.inject.Inject;
public class StreamConnectionFactoryImpl implements StreamConnectionFactory { class StreamConnectionFactoryImpl implements StreamConnectionFactory {
private final ConnectionReaderFactory connReaderFactory; private final ConnectionReaderFactory connReaderFactory;
private final ConnectionWriterFactory connWriterFactory; private final ConnectionWriterFactory connWriterFactory;

View File

@@ -26,6 +26,7 @@
<test name='net.sf.briar.i18n.FontManagerTest'/> <test name='net.sf.briar.i18n.FontManagerTest'/>
<test name='net.sf.briar.i18n.I18nTest'/> <test name='net.sf.briar.i18n.I18nTest'/>
<test name='net.sf.briar.invitation.InvitationWorkerTest'/> <test name='net.sf.briar.invitation.InvitationWorkerTest'/>
<test name='net.sf.briar.lifecycle.ShutdownManagerImplTest'/>
<test name='net.sf.briar.plugins.PluginManagerImplTest'/> <test name='net.sf.briar.plugins.PluginManagerImplTest'/>
<test name='net.sf.briar.plugins.file.LinuxRemovableDriveFinderTest'/> <test name='net.sf.briar.plugins.file.LinuxRemovableDriveFinderTest'/>
<test name='net.sf.briar.plugins.file.MacRemovableDriveFinderTest'/> <test name='net.sf.briar.plugins.file.MacRemovableDriveFinderTest'/>

View File

@@ -54,6 +54,7 @@ import net.sf.briar.api.transport.ConnectionWriter;
import net.sf.briar.api.transport.ConnectionWriterFactory; import net.sf.briar.api.transport.ConnectionWriterFactory;
import net.sf.briar.crypto.CryptoModule; import net.sf.briar.crypto.CryptoModule;
import net.sf.briar.db.DatabaseModule; import net.sf.briar.db.DatabaseModule;
import net.sf.briar.lifecycle.LifecycleModule;
import net.sf.briar.protocol.ProtocolModule; import net.sf.briar.protocol.ProtocolModule;
import net.sf.briar.protocol.writers.ProtocolWritersModule; import net.sf.briar.protocol.writers.ProtocolWritersModule;
import net.sf.briar.serial.SerialModule; import net.sf.briar.serial.SerialModule;
@@ -102,10 +103,11 @@ public class ProtocolIntegrationTest extends TestCase {
} }
}; };
Injector i = Guice.createInjector(testModule, new CryptoModule(), Injector i = Guice.createInjector(testModule, new CryptoModule(),
new DatabaseModule(), new ProtocolModule(), new DatabaseModule(), new LifecycleModule(),
new ProtocolWritersModule(), new SerialModule(), new ProtocolModule(), new ProtocolWritersModule(),
new TestDatabaseModule(), new TransportBatchModule(), new SerialModule(), new TestDatabaseModule(),
new TransportModule(), new TransportStreamModule()); new TransportBatchModule(), new TransportModule(),
new TransportStreamModule());
connectionContextFactory = connectionContextFactory =
i.getInstance(ConnectionContextFactory.class); i.getInstance(ConnectionContextFactory.class);
connectionReaderFactory = i.getInstance(ConnectionReaderFactory.class); connectionReaderFactory = i.getInstance(ConnectionReaderFactory.class);

View File

@@ -7,6 +7,7 @@ import java.util.Collections;
import net.sf.briar.api.db.DatabaseComponent; import net.sf.briar.api.db.DatabaseComponent;
import net.sf.briar.api.db.DbException; import net.sf.briar.api.db.DbException;
import net.sf.briar.api.lifecycle.ShutdownManager;
import net.sf.briar.db.DatabaseCleaner.Callback; import net.sf.briar.db.DatabaseCleaner.Callback;
import org.jmock.Expectations; import org.jmock.Expectations;
@@ -25,11 +26,12 @@ public class DatabaseComponentImplTest extends DatabaseComponentTest {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class); final Database<Object> database = context.mock(Database.class);
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(database).getFreeSpace(); oneOf(database).getFreeSpace();
will(returnValue(MIN_FREE_SPACE)); will(returnValue(MIN_FREE_SPACE));
}}); }});
Callback db = createDatabaseComponentImpl(database, cleaner); Callback db = createDatabaseComponentImpl(database, cleaner, shutdown);
db.checkFreeSpaceAndClean(); db.checkFreeSpaceAndClean();
@@ -42,6 +44,7 @@ public class DatabaseComponentImplTest extends DatabaseComponentTest {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class); final Database<Object> database = context.mock(Database.class);
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(database).getFreeSpace(); oneOf(database).getFreeSpace();
will(returnValue(MIN_FREE_SPACE - 1)); will(returnValue(MIN_FREE_SPACE - 1));
@@ -54,7 +57,7 @@ public class DatabaseComponentImplTest extends DatabaseComponentTest {
oneOf(database).getFreeSpace(); oneOf(database).getFreeSpace();
will(returnValue(MIN_FREE_SPACE)); will(returnValue(MIN_FREE_SPACE));
}}); }});
Callback db = createDatabaseComponentImpl(database, cleaner); Callback db = createDatabaseComponentImpl(database, cleaner, shutdown);
db.checkFreeSpaceAndClean(); db.checkFreeSpaceAndClean();
@@ -68,6 +71,7 @@ public class DatabaseComponentImplTest extends DatabaseComponentTest {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class); final Database<Object> database = context.mock(Database.class);
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(database).getFreeSpace(); oneOf(database).getFreeSpace();
will(returnValue(MIN_FREE_SPACE - 1)); will(returnValue(MIN_FREE_SPACE - 1));
@@ -82,7 +86,7 @@ public class DatabaseComponentImplTest extends DatabaseComponentTest {
oneOf(database).getFreeSpace(); oneOf(database).getFreeSpace();
will(returnValue(MIN_FREE_SPACE)); will(returnValue(MIN_FREE_SPACE));
}}); }});
Callback db = createDatabaseComponentImpl(database, cleaner); Callback db = createDatabaseComponentImpl(database, cleaner, shutdown);
db.checkFreeSpaceAndClean(); db.checkFreeSpaceAndClean();
@@ -96,6 +100,7 @@ public class DatabaseComponentImplTest extends DatabaseComponentTest {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class); final Database<Object> database = context.mock(Database.class);
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(database).getFreeSpace(); oneOf(database).getFreeSpace();
will(returnValue(MIN_FREE_SPACE - 1)); will(returnValue(MIN_FREE_SPACE - 1));
@@ -112,7 +117,7 @@ public class DatabaseComponentImplTest extends DatabaseComponentTest {
oneOf(database).getFreeSpace(); oneOf(database).getFreeSpace();
will(returnValue(MIN_FREE_SPACE)); will(returnValue(MIN_FREE_SPACE));
}}); }});
Callback db = createDatabaseComponentImpl(database, cleaner); Callback db = createDatabaseComponentImpl(database, cleaner, shutdown);
db.checkFreeSpaceAndClean(); db.checkFreeSpaceAndClean();
@@ -121,12 +126,14 @@ public class DatabaseComponentImplTest extends DatabaseComponentTest {
@Override @Override
protected <T> DatabaseComponent createDatabaseComponent( protected <T> DatabaseComponent createDatabaseComponent(
Database<T> database, DatabaseCleaner cleaner) { Database<T> database, DatabaseCleaner cleaner,
return createDatabaseComponentImpl(database, cleaner); ShutdownManager shutdown) {
return createDatabaseComponentImpl(database, cleaner, shutdown);
} }
private <T> DatabaseComponentImpl<T> createDatabaseComponentImpl( private <T> DatabaseComponentImpl<T> createDatabaseComponentImpl(
Database<T> database, DatabaseCleaner cleaner) { Database<T> database, DatabaseCleaner cleaner,
return new DatabaseComponentImpl<T>(database, cleaner); ShutdownManager shutdown) {
return new DatabaseComponentImpl<T>(database, cleaner, shutdown);
} }
} }

View File

@@ -22,6 +22,7 @@ import net.sf.briar.api.db.event.DatabaseListener;
import net.sf.briar.api.db.event.MessagesAddedEvent; import net.sf.briar.api.db.event.MessagesAddedEvent;
import net.sf.briar.api.db.event.RatingChangedEvent; import net.sf.briar.api.db.event.RatingChangedEvent;
import net.sf.briar.api.db.event.TransportAddedEvent; import net.sf.briar.api.db.event.TransportAddedEvent;
import net.sf.briar.api.lifecycle.ShutdownManager;
import net.sf.briar.api.protocol.Ack; import net.sf.briar.api.protocol.Ack;
import net.sf.briar.api.protocol.AuthorId; import net.sf.briar.api.protocol.AuthorId;
import net.sf.briar.api.protocol.Batch; import net.sf.briar.api.protocol.Batch;
@@ -103,14 +104,17 @@ public abstract class DatabaseComponentTest extends TestCase {
} }
protected abstract <T> DatabaseComponent createDatabaseComponent( protected abstract <T> DatabaseComponent createDatabaseComponent(
Database<T> database, DatabaseCleaner cleaner); Database<T> database, DatabaseCleaner cleaner,
ShutdownManager shutdown);
@Test @Test
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public void testSimpleCalls() throws Exception { public void testSimpleCalls() throws Exception {
final int shutdownHandle = 12345;
Mockery context = new Mockery(); Mockery context = new Mockery();
final Database<Object> database = context.mock(Database.class); final Database<Object> database = context.mock(Database.class);
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
final ConnectionWindow connectionWindow = final ConnectionWindow connectionWindow =
context.mock(ConnectionWindow.class); context.mock(ConnectionWindow.class);
final Group group = context.mock(Group.class); final Group group = context.mock(Group.class);
@@ -124,6 +128,8 @@ public abstract class DatabaseComponentTest extends TestCase {
oneOf(cleaner).startCleaning( oneOf(cleaner).startCleaning(
with(any(DatabaseCleaner.Callback.class)), with(any(DatabaseCleaner.Callback.class)),
with(any(long.class))); with(any(long.class)));
oneOf(shutdown).addShutdownHook(with(any(Runnable.class)));
will(returnValue(shutdownHandle));
// getRating(authorId) // getRating(authorId)
oneOf(database).getRating(txn, authorId); oneOf(database).getRating(txn, authorId);
will(returnValue(Rating.UNRATED)); will(returnValue(Rating.UNRATED));
@@ -189,10 +195,12 @@ public abstract class DatabaseComponentTest extends TestCase {
oneOf(database).removeContact(txn, contactId); oneOf(database).removeContact(txn, contactId);
oneOf(listener).eventOccurred(with(any(ContactRemovedEvent.class))); oneOf(listener).eventOccurred(with(any(ContactRemovedEvent.class)));
// close() // close()
oneOf(shutdown).removeShutdownHook(shutdownHandle);
oneOf(cleaner).stopCleaning(); oneOf(cleaner).stopCleaning();
oneOf(database).close(); oneOf(database).close();
}}); }});
DatabaseComponent db = createDatabaseComponent(database, cleaner); DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown);
db.open(false); db.open(false);
db.addListener(listener); db.addListener(listener);
@@ -224,6 +232,7 @@ public abstract class DatabaseComponentTest extends TestCase {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class); final Database<Object> database = context.mock(Database.class);
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// setRating(authorId, Rating.GOOD) // setRating(authorId, Rating.GOOD)
allowing(database).startTransaction(); allowing(database).startTransaction();
@@ -241,7 +250,8 @@ public abstract class DatabaseComponentTest extends TestCase {
will(returnValue(null)); will(returnValue(null));
oneOf(database).commitTransaction(txn); oneOf(database).commitTransaction(txn);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, cleaner); DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown);
db.setRating(authorId, Rating.GOOD); db.setRating(authorId, Rating.GOOD);
@@ -254,6 +264,7 @@ public abstract class DatabaseComponentTest extends TestCase {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class); final Database<Object> database = context.mock(Database.class);
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// setRating(authorId, Rating.GOOD) // setRating(authorId, Rating.GOOD)
oneOf(database).startTransaction(); oneOf(database).startTransaction();
@@ -275,7 +286,8 @@ public abstract class DatabaseComponentTest extends TestCase {
oneOf(database).setSendability(txn, parentId, 2); oneOf(database).setSendability(txn, parentId, 2);
oneOf(database).commitTransaction(txn); oneOf(database).commitTransaction(txn);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, cleaner); DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown);
db.setRating(authorId, Rating.GOOD); db.setRating(authorId, Rating.GOOD);
@@ -289,6 +301,7 @@ public abstract class DatabaseComponentTest extends TestCase {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class); final Database<Object> database = context.mock(Database.class);
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// setRating(authorId, Rating.GOOD) // setRating(authorId, Rating.GOOD)
oneOf(database).startTransaction(); oneOf(database).startTransaction();
@@ -313,7 +326,8 @@ public abstract class DatabaseComponentTest extends TestCase {
will(returnValue(null)); will(returnValue(null));
oneOf(database).commitTransaction(txn); oneOf(database).commitTransaction(txn);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, cleaner); DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown);
db.setRating(authorId, Rating.GOOD); db.setRating(authorId, Rating.GOOD);
@@ -327,6 +341,7 @@ public abstract class DatabaseComponentTest extends TestCase {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class); final Database<Object> database = context.mock(Database.class);
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// addLocalGroupMessage(message) // addLocalGroupMessage(message)
oneOf(database).startTransaction(); oneOf(database).startTransaction();
@@ -335,7 +350,8 @@ public abstract class DatabaseComponentTest extends TestCase {
will(returnValue(false)); will(returnValue(false));
oneOf(database).commitTransaction(txn); oneOf(database).commitTransaction(txn);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, cleaner); DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown);
db.addLocalGroupMessage(message); db.addLocalGroupMessage(message);
@@ -348,6 +364,7 @@ public abstract class DatabaseComponentTest extends TestCase {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class); final Database<Object> database = context.mock(Database.class);
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// addLocalGroupMessage(message) // addLocalGroupMessage(message)
oneOf(database).startTransaction(); oneOf(database).startTransaction();
@@ -358,7 +375,8 @@ public abstract class DatabaseComponentTest extends TestCase {
will(returnValue(false)); will(returnValue(false));
oneOf(database).commitTransaction(txn); oneOf(database).commitTransaction(txn);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, cleaner); DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown);
db.addLocalGroupMessage(message); db.addLocalGroupMessage(message);
@@ -371,6 +389,7 @@ public abstract class DatabaseComponentTest extends TestCase {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class); final Database<Object> database = context.mock(Database.class);
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// addLocalGroupMessage(message) // addLocalGroupMessage(message)
oneOf(database).startTransaction(); oneOf(database).startTransaction();
@@ -390,7 +409,8 @@ public abstract class DatabaseComponentTest extends TestCase {
oneOf(database).setSendability(txn, messageId, 0); oneOf(database).setSendability(txn, messageId, 0);
oneOf(database).commitTransaction(txn); oneOf(database).commitTransaction(txn);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, cleaner); DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown);
db.addLocalGroupMessage(message); db.addLocalGroupMessage(message);
@@ -404,6 +424,7 @@ public abstract class DatabaseComponentTest extends TestCase {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class); final Database<Object> database = context.mock(Database.class);
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// addLocalGroupMessage(message) // addLocalGroupMessage(message)
oneOf(database).startTransaction(); oneOf(database).startTransaction();
@@ -426,7 +447,8 @@ public abstract class DatabaseComponentTest extends TestCase {
will(returnValue(null)); will(returnValue(null));
oneOf(database).commitTransaction(txn); oneOf(database).commitTransaction(txn);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, cleaner); DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown);
db.addLocalGroupMessage(message); db.addLocalGroupMessage(message);
@@ -439,6 +461,7 @@ public abstract class DatabaseComponentTest extends TestCase {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class); final Database<Object> database = context.mock(Database.class);
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
allowing(database).startTransaction(); allowing(database).startTransaction();
will(returnValue(txn)); will(returnValue(txn));
@@ -449,7 +472,8 @@ public abstract class DatabaseComponentTest extends TestCase {
oneOf(database).addPrivateMessage(txn, privateMessage, contactId); oneOf(database).addPrivateMessage(txn, privateMessage, contactId);
will(returnValue(false)); will(returnValue(false));
}}); }});
DatabaseComponent db = createDatabaseComponent(database, cleaner); DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown);
db.addLocalPrivateMessage(privateMessage, contactId); db.addLocalPrivateMessage(privateMessage, contactId);
@@ -462,6 +486,7 @@ public abstract class DatabaseComponentTest extends TestCase {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class); final Database<Object> database = context.mock(Database.class);
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
allowing(database).startTransaction(); allowing(database).startTransaction();
will(returnValue(txn)); will(returnValue(txn));
@@ -473,7 +498,8 @@ public abstract class DatabaseComponentTest extends TestCase {
will(returnValue(true)); will(returnValue(true));
oneOf(database).setStatus(txn, contactId, messageId, Status.NEW); oneOf(database).setStatus(txn, contactId, messageId, Status.NEW);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, cleaner); DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown);
db.addLocalPrivateMessage(privateMessage, contactId); db.addLocalPrivateMessage(privateMessage, contactId);
@@ -487,6 +513,7 @@ public abstract class DatabaseComponentTest extends TestCase {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class); final Database<Object> database = context.mock(Database.class);
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
final AckWriter ackWriter = context.mock(AckWriter.class); final AckWriter ackWriter = context.mock(AckWriter.class);
final BatchWriter batchWriter = context.mock(BatchWriter.class); final BatchWriter batchWriter = context.mock(BatchWriter.class);
final OfferWriter offerWriter = context.mock(OfferWriter.class); final OfferWriter offerWriter = context.mock(OfferWriter.class);
@@ -510,7 +537,8 @@ public abstract class DatabaseComponentTest extends TestCase {
will(returnValue(false)); will(returnValue(false));
exactly(19).of(database).commitTransaction(txn); exactly(19).of(database).commitTransaction(txn);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, cleaner); DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown);
try { try {
db.addLocalPrivateMessage(privateMessage, contactId); db.addLocalPrivateMessage(privateMessage, contactId);
@@ -621,6 +649,7 @@ public abstract class DatabaseComponentTest extends TestCase {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class); final Database<Object> database = context.mock(Database.class);
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
final AckWriter ackWriter = context.mock(AckWriter.class); final AckWriter ackWriter = context.mock(AckWriter.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
allowing(database).startTransaction(); allowing(database).startTransaction();
@@ -641,7 +670,8 @@ public abstract class DatabaseComponentTest extends TestCase {
oneOf(database).removeBatchesToAck(txn, contactId, oneOf(database).removeBatchesToAck(txn, contactId,
Collections.singletonList(batchId)); Collections.singletonList(batchId));
}}); }});
DatabaseComponent db = createDatabaseComponent(database, cleaner); DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown);
db.generateAck(contactId, ackWriter); db.generateAck(contactId, ackWriter);
@@ -659,6 +689,7 @@ public abstract class DatabaseComponentTest extends TestCase {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class); final Database<Object> database = context.mock(Database.class);
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
final BatchWriter batchWriter = context.mock(BatchWriter.class); final BatchWriter batchWriter = context.mock(BatchWriter.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
allowing(database).startTransaction(); allowing(database).startTransaction();
@@ -688,7 +719,8 @@ public abstract class DatabaseComponentTest extends TestCase {
oneOf(database).addOutstandingBatch(txn, contactId, batchId, oneOf(database).addOutstandingBatch(txn, contactId, batchId,
sendable); sendable);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, cleaner); DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown);
db.generateBatch(contactId, batchWriter); db.generateBatch(contactId, batchWriter);
@@ -708,6 +740,7 @@ public abstract class DatabaseComponentTest extends TestCase {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class); final Database<Object> database = context.mock(Database.class);
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
final BatchWriter batchWriter = context.mock(BatchWriter.class); final BatchWriter batchWriter = context.mock(BatchWriter.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
allowing(database).startTransaction(); allowing(database).startTransaction();
@@ -734,7 +767,8 @@ public abstract class DatabaseComponentTest extends TestCase {
oneOf(database).addOutstandingBatch(txn, contactId, batchId, oneOf(database).addOutstandingBatch(txn, contactId, batchId,
Collections.singletonList(messageId1)); Collections.singletonList(messageId1));
}}); }});
DatabaseComponent db = createDatabaseComponent(database, cleaner); DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown);
db.generateBatch(contactId, batchWriter, requested); db.generateBatch(contactId, batchWriter, requested);
@@ -751,6 +785,7 @@ public abstract class DatabaseComponentTest extends TestCase {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class); final Database<Object> database = context.mock(Database.class);
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
final OfferWriter offerWriter = context.mock(OfferWriter.class); final OfferWriter offerWriter = context.mock(OfferWriter.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
allowing(database).startTransaction(); allowing(database).startTransaction();
@@ -768,7 +803,8 @@ public abstract class DatabaseComponentTest extends TestCase {
will(returnValue(false)); will(returnValue(false));
oneOf(offerWriter).finish(); oneOf(offerWriter).finish();
}}); }});
DatabaseComponent db = createDatabaseComponent(database, cleaner); DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown);
assertEquals(Collections.singletonList(messageId), assertEquals(Collections.singletonList(messageId),
db.generateOffer(contactId, offerWriter)); db.generateOffer(contactId, offerWriter));
@@ -783,6 +819,7 @@ public abstract class DatabaseComponentTest extends TestCase {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class); final Database<Object> database = context.mock(Database.class);
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
final SubscriptionUpdateWriter subscriptionUpdateWriter = final SubscriptionUpdateWriter subscriptionUpdateWriter =
context.mock(SubscriptionUpdateWriter.class); context.mock(SubscriptionUpdateWriter.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
@@ -797,7 +834,8 @@ public abstract class DatabaseComponentTest extends TestCase {
oneOf(database).getSubscriptionsSent(txn, contactId); oneOf(database).getSubscriptionsSent(txn, contactId);
will(returnValue(now)); will(returnValue(now));
}}); }});
DatabaseComponent db = createDatabaseComponent(database, cleaner); DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown);
db.generateSubscriptionUpdate(contactId, subscriptionUpdateWriter); db.generateSubscriptionUpdate(contactId, subscriptionUpdateWriter);
@@ -814,6 +852,7 @@ public abstract class DatabaseComponentTest extends TestCase {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class); final Database<Object> database = context.mock(Database.class);
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
final SubscriptionUpdateWriter subscriptionUpdateWriter = final SubscriptionUpdateWriter subscriptionUpdateWriter =
context.mock(SubscriptionUpdateWriter.class); context.mock(SubscriptionUpdateWriter.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
@@ -837,7 +876,8 @@ public abstract class DatabaseComponentTest extends TestCase {
with(Collections.singletonMap(group, 0L)), with(Collections.singletonMap(group, 0L)),
with(any(long.class))); with(any(long.class)));
}}); }});
DatabaseComponent db = createDatabaseComponent(database, cleaner); DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown);
db.generateSubscriptionUpdate(contactId, subscriptionUpdateWriter); db.generateSubscriptionUpdate(contactId, subscriptionUpdateWriter);
@@ -851,6 +891,7 @@ public abstract class DatabaseComponentTest extends TestCase {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class); final Database<Object> database = context.mock(Database.class);
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
final TransportUpdateWriter transportUpdateWriter = final TransportUpdateWriter transportUpdateWriter =
context.mock(TransportUpdateWriter.class); context.mock(TransportUpdateWriter.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
@@ -865,7 +906,8 @@ public abstract class DatabaseComponentTest extends TestCase {
oneOf(database).getTransportsSent(txn, contactId); oneOf(database).getTransportsSent(txn, contactId);
will(returnValue(now)); will(returnValue(now));
}}); }});
DatabaseComponent db = createDatabaseComponent(database, cleaner); DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown);
db.generateTransportUpdate(contactId, transportUpdateWriter); db.generateTransportUpdate(contactId, transportUpdateWriter);
@@ -882,6 +924,7 @@ public abstract class DatabaseComponentTest extends TestCase {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class); final Database<Object> database = context.mock(Database.class);
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
final TransportUpdateWriter transportUpdateWriter = final TransportUpdateWriter transportUpdateWriter =
context.mock(TransportUpdateWriter.class); context.mock(TransportUpdateWriter.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
@@ -904,7 +947,8 @@ public abstract class DatabaseComponentTest extends TestCase {
oneOf(transportUpdateWriter).writeTransports(with(transports), oneOf(transportUpdateWriter).writeTransports(with(transports),
with(any(long.class))); with(any(long.class)));
}}); }});
DatabaseComponent db = createDatabaseComponent(database, cleaner); DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown);
db.generateTransportUpdate(contactId, transportUpdateWriter); db.generateTransportUpdate(contactId, transportUpdateWriter);
@@ -918,6 +962,7 @@ public abstract class DatabaseComponentTest extends TestCase {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class); final Database<Object> database = context.mock(Database.class);
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
final Ack ack = context.mock(Ack.class); final Ack ack = context.mock(Ack.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
allowing(database).startTransaction(); allowing(database).startTransaction();
@@ -934,7 +979,8 @@ public abstract class DatabaseComponentTest extends TestCase {
will(returnValue(Collections.singletonList(batchId1))); will(returnValue(Collections.singletonList(batchId1)));
oneOf(database).removeLostBatch(txn, contactId, batchId1); oneOf(database).removeLostBatch(txn, contactId, batchId1);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, cleaner); DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown);
db.receiveAck(contactId, ack); db.receiveAck(contactId, ack);
@@ -947,6 +993,7 @@ public abstract class DatabaseComponentTest extends TestCase {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class); final Database<Object> database = context.mock(Database.class);
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
final Batch batch = context.mock(Batch.class); final Batch batch = context.mock(Batch.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
allowing(database).startTransaction(); allowing(database).startTransaction();
@@ -965,7 +1012,8 @@ public abstract class DatabaseComponentTest extends TestCase {
will(returnValue(batchId)); will(returnValue(batchId));
oneOf(database).addBatchToAck(txn, contactId, batchId); oneOf(database).addBatchToAck(txn, contactId, batchId);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, cleaner); DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown);
db.receiveBatch(contactId, batch); db.receiveBatch(contactId, batch);
@@ -978,6 +1026,7 @@ public abstract class DatabaseComponentTest extends TestCase {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class); final Database<Object> database = context.mock(Database.class);
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
final Batch batch = context.mock(Batch.class); final Batch batch = context.mock(Batch.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
allowing(database).startTransaction(); allowing(database).startTransaction();
@@ -995,7 +1044,8 @@ public abstract class DatabaseComponentTest extends TestCase {
will(returnValue(batchId)); will(returnValue(batchId));
oneOf(database).addBatchToAck(txn, contactId, batchId); oneOf(database).addBatchToAck(txn, contactId, batchId);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, cleaner); DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown);
db.receiveBatch(contactId, batch); db.receiveBatch(contactId, batch);
@@ -1009,6 +1059,7 @@ public abstract class DatabaseComponentTest extends TestCase {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class); final Database<Object> database = context.mock(Database.class);
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
final Batch batch = context.mock(Batch.class); final Batch batch = context.mock(Batch.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
allowing(database).startTransaction(); allowing(database).startTransaction();
@@ -1027,7 +1078,8 @@ public abstract class DatabaseComponentTest extends TestCase {
will(returnValue(batchId)); will(returnValue(batchId));
oneOf(database).addBatchToAck(txn, contactId, batchId); oneOf(database).addBatchToAck(txn, contactId, batchId);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, cleaner); DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown);
db.receiveBatch(contactId, batch); db.receiveBatch(contactId, batch);
@@ -1041,6 +1093,7 @@ public abstract class DatabaseComponentTest extends TestCase {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class); final Database<Object> database = context.mock(Database.class);
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
final Batch batch = context.mock(Batch.class); final Batch batch = context.mock(Batch.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
allowing(database).startTransaction(); allowing(database).startTransaction();
@@ -1063,7 +1116,8 @@ public abstract class DatabaseComponentTest extends TestCase {
will(returnValue(batchId)); will(returnValue(batchId));
oneOf(database).addBatchToAck(txn, contactId, batchId); oneOf(database).addBatchToAck(txn, contactId, batchId);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, cleaner); DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown);
db.receiveBatch(contactId, batch); db.receiveBatch(contactId, batch);
@@ -1076,6 +1130,7 @@ public abstract class DatabaseComponentTest extends TestCase {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class); final Database<Object> database = context.mock(Database.class);
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
final Batch batch = context.mock(Batch.class); final Batch batch = context.mock(Batch.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
allowing(database).startTransaction(); allowing(database).startTransaction();
@@ -1107,7 +1162,8 @@ public abstract class DatabaseComponentTest extends TestCase {
will(returnValue(batchId)); will(returnValue(batchId));
oneOf(database).addBatchToAck(txn, contactId, batchId); oneOf(database).addBatchToAck(txn, contactId, batchId);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, cleaner); DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown);
db.receiveBatch(contactId, batch); db.receiveBatch(contactId, batch);
@@ -1120,6 +1176,7 @@ public abstract class DatabaseComponentTest extends TestCase {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class); final Database<Object> database = context.mock(Database.class);
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
final Batch batch = context.mock(Batch.class); final Batch batch = context.mock(Batch.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
allowing(database).startTransaction(); allowing(database).startTransaction();
@@ -1153,7 +1210,8 @@ public abstract class DatabaseComponentTest extends TestCase {
will(returnValue(batchId)); will(returnValue(batchId));
oneOf(database).addBatchToAck(txn, contactId, batchId); oneOf(database).addBatchToAck(txn, contactId, batchId);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, cleaner); DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown);
db.receiveBatch(contactId, batch); db.receiveBatch(contactId, batch);
@@ -1175,6 +1233,7 @@ public abstract class DatabaseComponentTest extends TestCase {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class); final Database<Object> database = context.mock(Database.class);
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
final Offer offer = context.mock(Offer.class); final Offer offer = context.mock(Offer.class);
final RequestWriter requestWriter = context.mock(RequestWriter.class); final RequestWriter requestWriter = context.mock(RequestWriter.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
@@ -1194,7 +1253,8 @@ public abstract class DatabaseComponentTest extends TestCase {
will(returnValue(false)); // Not visible - request message # 2 will(returnValue(false)); // Not visible - request message # 2
oneOf(requestWriter).writeRequest(expectedRequest, 3); oneOf(requestWriter).writeRequest(expectedRequest, 3);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, cleaner); DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown);
db.receiveOffer(contactId, offer, requestWriter); db.receiveOffer(contactId, offer, requestWriter);
@@ -1208,6 +1268,7 @@ public abstract class DatabaseComponentTest extends TestCase {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class); final Database<Object> database = context.mock(Database.class);
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
final SubscriptionUpdate subscriptionUpdate = final SubscriptionUpdate subscriptionUpdate =
context.mock(SubscriptionUpdate.class); context.mock(SubscriptionUpdate.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
@@ -1224,7 +1285,8 @@ public abstract class DatabaseComponentTest extends TestCase {
oneOf(database).setSubscriptions(txn, contactId, oneOf(database).setSubscriptions(txn, contactId,
Collections.singletonMap(group, 0L), timestamp); Collections.singletonMap(group, 0L), timestamp);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, cleaner); DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown);
db.receiveSubscriptionUpdate(contactId, subscriptionUpdate); db.receiveSubscriptionUpdate(contactId, subscriptionUpdate);
@@ -1238,6 +1300,7 @@ public abstract class DatabaseComponentTest extends TestCase {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class); final Database<Object> database = context.mock(Database.class);
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
final TransportUpdate transportUpdate = final TransportUpdate transportUpdate =
context.mock(TransportUpdate.class); context.mock(TransportUpdate.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
@@ -1254,7 +1317,8 @@ public abstract class DatabaseComponentTest extends TestCase {
oneOf(database).setTransports(txn, contactId, transports, oneOf(database).setTransports(txn, contactId, transports,
timestamp); timestamp);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, cleaner); DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown);
db.receiveTransportUpdate(contactId, transportUpdate); db.receiveTransportUpdate(contactId, transportUpdate);
@@ -1267,6 +1331,7 @@ public abstract class DatabaseComponentTest extends TestCase {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class); final Database<Object> database = context.mock(Database.class);
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
final DatabaseListener listener = context.mock(DatabaseListener.class); final DatabaseListener listener = context.mock(DatabaseListener.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// addLocalGroupMessage(message) // addLocalGroupMessage(message)
@@ -1288,7 +1353,8 @@ public abstract class DatabaseComponentTest extends TestCase {
// The message was added, so the listener should be called // The message was added, so the listener should be called
oneOf(listener).eventOccurred(with(any(MessagesAddedEvent.class))); oneOf(listener).eventOccurred(with(any(MessagesAddedEvent.class)));
}}); }});
DatabaseComponent db = createDatabaseComponent(database, cleaner); DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown);
db.addListener(listener); db.addListener(listener);
db.addLocalGroupMessage(message); db.addLocalGroupMessage(message);
@@ -1302,6 +1368,7 @@ public abstract class DatabaseComponentTest extends TestCase {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class); final Database<Object> database = context.mock(Database.class);
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
final DatabaseListener listener = context.mock(DatabaseListener.class); final DatabaseListener listener = context.mock(DatabaseListener.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
allowing(database).startTransaction(); allowing(database).startTransaction();
@@ -1316,7 +1383,8 @@ public abstract class DatabaseComponentTest extends TestCase {
// The message was added, so the listener should be called // The message was added, so the listener should be called
oneOf(listener).eventOccurred(with(any(MessagesAddedEvent.class))); oneOf(listener).eventOccurred(with(any(MessagesAddedEvent.class)));
}}); }});
DatabaseComponent db = createDatabaseComponent(database, cleaner); DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown);
db.addListener(listener); db.addListener(listener);
db.addLocalPrivateMessage(privateMessage, contactId); db.addLocalPrivateMessage(privateMessage, contactId);
@@ -1331,6 +1399,7 @@ public abstract class DatabaseComponentTest extends TestCase {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class); final Database<Object> database = context.mock(Database.class);
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
final DatabaseListener listener = context.mock(DatabaseListener.class); final DatabaseListener listener = context.mock(DatabaseListener.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// addLocalGroupMessage(message) // addLocalGroupMessage(message)
@@ -1343,7 +1412,8 @@ public abstract class DatabaseComponentTest extends TestCase {
oneOf(database).commitTransaction(txn); oneOf(database).commitTransaction(txn);
// The message was not added, so the listener should not be called // The message was not added, so the listener should not be called
}}); }});
DatabaseComponent db = createDatabaseComponent(database, cleaner); DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown);
db.addListener(listener); db.addListener(listener);
db.addLocalGroupMessage(message); db.addLocalGroupMessage(message);
@@ -1358,6 +1428,7 @@ public abstract class DatabaseComponentTest extends TestCase {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class); final Database<Object> database = context.mock(Database.class);
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
final DatabaseListener listener = context.mock(DatabaseListener.class); final DatabaseListener listener = context.mock(DatabaseListener.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
allowing(database).startTransaction(); allowing(database).startTransaction();
@@ -1370,7 +1441,8 @@ public abstract class DatabaseComponentTest extends TestCase {
will(returnValue(false)); will(returnValue(false));
// The message was not added, so the listener should not be called // The message was not added, so the listener should not be called
}}); }});
DatabaseComponent db = createDatabaseComponent(database, cleaner); DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown);
db.addListener(listener); db.addListener(listener);
db.addLocalPrivateMessage(privateMessage, contactId); db.addLocalPrivateMessage(privateMessage, contactId);
@@ -1387,6 +1459,7 @@ public abstract class DatabaseComponentTest extends TestCase {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class); final Database<Object> database = context.mock(Database.class);
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
final DatabaseListener listener = context.mock(DatabaseListener.class); final DatabaseListener listener = context.mock(DatabaseListener.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(database).startTransaction(); oneOf(database).startTransaction();
@@ -1400,7 +1473,8 @@ public abstract class DatabaseComponentTest extends TestCase {
oneOf(listener).eventOccurred(with(any( oneOf(listener).eventOccurred(with(any(
TransportAddedEvent.class))); TransportAddedEvent.class)));
}}); }});
DatabaseComponent db = createDatabaseComponent(database, cleaner); DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown);
db.addListener(listener); db.addListener(listener);
db.setLocalProperties(transportId, properties); db.setLocalProperties(transportId, properties);
@@ -1417,6 +1491,7 @@ public abstract class DatabaseComponentTest extends TestCase {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class); final Database<Object> database = context.mock(Database.class);
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
final DatabaseListener listener = context.mock(DatabaseListener.class); final DatabaseListener listener = context.mock(DatabaseListener.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(database).startTransaction(); oneOf(database).startTransaction();
@@ -1425,7 +1500,8 @@ public abstract class DatabaseComponentTest extends TestCase {
will(returnValue(properties)); will(returnValue(properties));
oneOf(database).commitTransaction(txn); oneOf(database).commitTransaction(txn);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, cleaner); DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown);
db.addListener(listener); db.addListener(listener);
db.setLocalProperties(transportId, properties); db.setLocalProperties(transportId, properties);
@@ -1439,6 +1515,7 @@ public abstract class DatabaseComponentTest extends TestCase {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class); final Database<Object> database = context.mock(Database.class);
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
allowing(database).startTransaction(); allowing(database).startTransaction();
will(returnValue(txn)); will(returnValue(txn));
@@ -1448,7 +1525,8 @@ public abstract class DatabaseComponentTest extends TestCase {
// setSeen(contactId, Collections.singletonList(messageId)) // setSeen(contactId, Collections.singletonList(messageId))
oneOf(database).setStatusSeenIfVisible(txn, contactId, messageId); oneOf(database).setStatusSeenIfVisible(txn, contactId, messageId);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, cleaner); DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown);
db.setSeen(contactId, Collections.singletonList(messageId)); db.setSeen(contactId, Collections.singletonList(messageId));

View File

@@ -44,6 +44,7 @@ import net.sf.briar.api.transport.ConnectionContextFactory;
import net.sf.briar.api.transport.ConnectionWindow; import net.sf.briar.api.transport.ConnectionWindow;
import net.sf.briar.api.transport.ConnectionWindowFactory; import net.sf.briar.api.transport.ConnectionWindowFactory;
import net.sf.briar.crypto.CryptoModule; import net.sf.briar.crypto.CryptoModule;
import net.sf.briar.lifecycle.LifecycleModule;
import net.sf.briar.protocol.ProtocolModule; import net.sf.briar.protocol.ProtocolModule;
import net.sf.briar.protocol.writers.ProtocolWritersModule; import net.sf.briar.protocol.writers.ProtocolWritersModule;
import net.sf.briar.serial.SerialModule; import net.sf.briar.serial.SerialModule;
@@ -105,10 +106,11 @@ public class H2DatabaseTest extends TestCase {
} }
}; };
Injector i = Guice.createInjector(testModule, new CryptoModule(), Injector i = Guice.createInjector(testModule, new CryptoModule(),
new DatabaseModule(), new ProtocolModule(), new DatabaseModule(), new LifecycleModule(),
new ProtocolWritersModule(), new SerialModule(), new ProtocolModule(), new ProtocolWritersModule(),
new TransportBatchModule(), new TransportModule(), new SerialModule(), new TransportBatchModule(),
new TransportStreamModule(), new TestDatabaseModule(testDir)); new TransportModule(), new TransportStreamModule(),
new TestDatabaseModule(testDir));
connectionContextFactory = connectionContextFactory =
i.getInstance(ConnectionContextFactory.class); i.getInstance(ConnectionContextFactory.class);
connectionWindowFactory = i.getInstance(ConnectionWindowFactory.class); connectionWindowFactory = i.getInstance(ConnectionWindowFactory.class);

View File

@@ -0,0 +1,29 @@
package net.sf.briar.lifecycle;
import java.util.HashSet;
import java.util.Set;
import junit.framework.TestCase;
import net.sf.briar.api.lifecycle.ShutdownManager;
import org.junit.Test;
public class ShutdownManagerImplTest extends TestCase {
@Test
public void testAddAndRemove() {
ShutdownManager s = new ShutdownManagerImpl();
Set<Integer> handles = new HashSet<Integer>();
for(int i = 0; i < 100; i++) {
int handle = s.addShutdownHook(new Runnable() {
public void run() {}
});
// The handles should all be distinct
assertTrue(handles.add(handle));
}
// The hooks should be removable
for(int handle : handles) assertTrue(s.removeShutdownHook(handle));
// The hooks should no longer be removable
for(int handle : handles) assertFalse(s.removeShutdownHook(handle));
}
}

View File

@@ -17,6 +17,7 @@ import net.sf.briar.api.crypto.CryptoComponent;
import net.sf.briar.api.crypto.ErasableKey; import net.sf.briar.api.crypto.ErasableKey;
import net.sf.briar.api.db.DatabaseComponent; import net.sf.briar.api.db.DatabaseComponent;
import net.sf.briar.api.db.DbException; import net.sf.briar.api.db.DbException;
import net.sf.briar.api.lifecycle.ShutdownManager;
import net.sf.briar.api.protocol.Transport; import net.sf.briar.api.protocol.Transport;
import net.sf.briar.api.protocol.TransportId; import net.sf.briar.api.protocol.TransportId;
import net.sf.briar.api.protocol.TransportIndex; import net.sf.briar.api.protocol.TransportIndex;
@@ -65,9 +66,11 @@ public class ConnectionRecogniserImplTest extends TestCase {
public void testUnexpectedIv() throws Exception { public void testUnexpectedIv() throws Exception {
Mockery context = new Mockery(); Mockery context = new Mockery();
final DatabaseComponent db = context.mock(DatabaseComponent.class); final DatabaseComponent db = context.mock(DatabaseComponent.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(db).addListener(with(any(ConnectionRecogniserImpl.class))); oneOf(db).addListener(with(any(ConnectionRecogniserImpl.class)));
// Initialise // Initialise
oneOf(shutdown).addShutdownHook(with(any(Runnable.class)));
oneOf(db).getLocalTransports(); oneOf(db).getLocalTransports();
will(returnValue(transports)); will(returnValue(transports));
oneOf(db).getContacts(); oneOf(db).getContacts();
@@ -77,8 +80,9 @@ public class ConnectionRecogniserImplTest extends TestCase {
oneOf(db).getConnectionWindow(contactId, remoteIndex); oneOf(db).getConnectionWindow(contactId, remoteIndex);
will(returnValue(connectionWindow)); will(returnValue(connectionWindow));
}}); }});
Executor e = new ImmediateExecutor(); Executor executor = new ImmediateExecutor();
ConnectionRecogniser c = new ConnectionRecogniserImpl(crypto, db, e); ConnectionRecogniser c = new ConnectionRecogniserImpl(crypto, db,
executor, shutdown);
c.acceptConnection(transportId, new byte[IV_LENGTH], new Callback() { c.acceptConnection(transportId, new byte[IV_LENGTH], new Callback() {
public void connectionAccepted(ConnectionContext ctx) { public void connectionAccepted(ConnectionContext ctx) {
@@ -112,9 +116,11 @@ public class ConnectionRecogniserImplTest extends TestCase {
Mockery context = new Mockery(); Mockery context = new Mockery();
final DatabaseComponent db = context.mock(DatabaseComponent.class); final DatabaseComponent db = context.mock(DatabaseComponent.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(db).addListener(with(any(ConnectionRecogniserImpl.class))); oneOf(db).addListener(with(any(ConnectionRecogniserImpl.class)));
// Initialise // Initialise
oneOf(shutdown).addShutdownHook(with(any(Runnable.class)));
oneOf(db).getLocalTransports(); oneOf(db).getLocalTransports();
will(returnValue(transports)); will(returnValue(transports));
oneOf(db).getContacts(); oneOf(db).getContacts();
@@ -127,8 +133,9 @@ public class ConnectionRecogniserImplTest extends TestCase {
oneOf(db).setConnectionWindow(contactId, remoteIndex, oneOf(db).setConnectionWindow(contactId, remoteIndex,
connectionWindow); connectionWindow);
}}); }});
Executor e = new ImmediateExecutor(); Executor executor = new ImmediateExecutor();
ConnectionRecogniser c = new ConnectionRecogniserImpl(crypto, db, e); ConnectionRecogniser c = new ConnectionRecogniserImpl(crypto, db,
executor, shutdown);
// The IV should not be expected by the wrong transport // The IV should not be expected by the wrong transport
TransportId wrong = new TransportId(TestUtils.getRandomId()); TransportId wrong = new TransportId(TestUtils.getRandomId());
c.acceptConnection(wrong, encryptedIv, new Callback() { c.acceptConnection(wrong, encryptedIv, new Callback() {

View File

@@ -18,6 +18,7 @@ import net.sf.briar.api.transport.ConnectionWriter;
import net.sf.briar.api.transport.ConnectionWriterFactory; import net.sf.briar.api.transport.ConnectionWriterFactory;
import net.sf.briar.crypto.CryptoModule; import net.sf.briar.crypto.CryptoModule;
import net.sf.briar.db.DatabaseModule; import net.sf.briar.db.DatabaseModule;
import net.sf.briar.lifecycle.LifecycleModule;
import net.sf.briar.protocol.ProtocolModule; import net.sf.briar.protocol.ProtocolModule;
import net.sf.briar.protocol.writers.ProtocolWritersModule; import net.sf.briar.protocol.writers.ProtocolWritersModule;
import net.sf.briar.serial.SerialModule; import net.sf.briar.serial.SerialModule;
@@ -50,10 +51,11 @@ public class ConnectionWriterTest extends TestCase {
} }
}; };
Injector i = Guice.createInjector(testModule, new CryptoModule(), Injector i = Guice.createInjector(testModule, new CryptoModule(),
new DatabaseModule(), new ProtocolModule(), new DatabaseModule(), new LifecycleModule(),
new ProtocolWritersModule(), new SerialModule(), new ProtocolModule(), new ProtocolWritersModule(),
new TestDatabaseModule(), new TransportBatchModule(), new SerialModule(), new TestDatabaseModule(),
new TransportModule(), new TransportStreamModule()); new TransportBatchModule(), new TransportModule(),
new TransportStreamModule());
connectionContextFactory = connectionContextFactory =
i.getInstance(ConnectionContextFactory.class); i.getInstance(ConnectionContextFactory.class);
connectionWriterFactory = i.getInstance(ConnectionWriterFactory.class); connectionWriterFactory = i.getInstance(ConnectionWriterFactory.class);

View File

@@ -40,6 +40,7 @@ import net.sf.briar.api.transport.ConnectionRecogniser.Callback;
import net.sf.briar.api.transport.ConnectionWriterFactory; import net.sf.briar.api.transport.ConnectionWriterFactory;
import net.sf.briar.crypto.CryptoModule; import net.sf.briar.crypto.CryptoModule;
import net.sf.briar.db.DatabaseModule; import net.sf.briar.db.DatabaseModule;
import net.sf.briar.lifecycle.LifecycleModule;
import net.sf.briar.protocol.ProtocolModule; import net.sf.briar.protocol.ProtocolModule;
import net.sf.briar.protocol.writers.ProtocolWritersModule; import net.sf.briar.protocol.writers.ProtocolWritersModule;
import net.sf.briar.serial.SerialModule; import net.sf.briar.serial.SerialModule;
@@ -81,32 +82,24 @@ public class BatchConnectionReadWriteTest extends TestCase {
@Before @Before
public void setUp() { public void setUp() {
testDir.mkdirs(); testDir.mkdirs();
// Create Alice's injector alice = createInjector(aliceDir);
Module aliceTestModule = new AbstractModule() { bob = createInjector(bobDir);
}
private Injector createInjector(File dir) {
Module testModule = new AbstractModule() {
@Override @Override
public void configure() { public void configure() {
bind(Executor.class).toInstance( bind(Executor.class).toInstance(
new ScheduledThreadPoolExecutor(5)); new ScheduledThreadPoolExecutor(5));
} }
}; };
alice = Guice.createInjector(aliceTestModule, new CryptoModule(), return Guice.createInjector(testModule, new CryptoModule(),
new DatabaseModule(), new ProtocolModule(), new DatabaseModule(), new LifecycleModule(),
new ProtocolWritersModule(), new SerialModule(), new ProtocolModule(), new ProtocolWritersModule(),
new TestDatabaseModule(aliceDir), new TransportBatchModule(), new SerialModule(), new TestDatabaseModule(dir),
new TransportModule(), new TransportStreamModule()); new TransportBatchModule(), new TransportModule(),
// Create Bob's injector new TransportStreamModule());
Module bobTestModule = new AbstractModule() {
@Override
public void configure() {
bind(Executor.class).toInstance(
new ScheduledThreadPoolExecutor(5));
}
};
bob = Guice.createInjector(bobTestModule, new CryptoModule(),
new DatabaseModule(), new ProtocolModule(),
new ProtocolWritersModule(), new SerialModule(),
new TestDatabaseModule(bobDir), new TransportBatchModule(),
new TransportModule(), new TransportStreamModule());
} }
@Test @Test