mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-15 20:29:52 +01:00
Show a status screen when opening the database or applying migrations
This commit is contained in:
@@ -6,6 +6,7 @@ import org.briarproject.bramble.api.db.DataTooNewException;
|
||||
import org.briarproject.bramble.api.db.DataTooOldException;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.db.Metadata;
|
||||
import org.briarproject.bramble.api.db.MigrationListener;
|
||||
import org.briarproject.bramble.api.identity.Author;
|
||||
import org.briarproject.bramble.api.identity.AuthorId;
|
||||
import org.briarproject.bramble.api.identity.LocalAuthor;
|
||||
@@ -45,7 +46,7 @@ interface Database<T> {
|
||||
* @throws DataTooOldException if the data uses an older schema than the
|
||||
* current code and cannot be migrated
|
||||
*/
|
||||
boolean open() throws DbException;
|
||||
boolean open(@Nullable MigrationListener listener) throws DbException;
|
||||
|
||||
/**
|
||||
* Prevents new transactions from starting, waits for all current
|
||||
|
||||
@@ -10,6 +10,7 @@ import org.briarproject.bramble.api.db.ContactExistsException;
|
||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.db.Metadata;
|
||||
import org.briarproject.bramble.api.db.MigrationListener;
|
||||
import org.briarproject.bramble.api.db.NoSuchContactException;
|
||||
import org.briarproject.bramble.api.db.NoSuchGroupException;
|
||||
import org.briarproject.bramble.api.db.NoSuchLocalAuthorException;
|
||||
@@ -100,8 +101,9 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean open() throws DbException {
|
||||
boolean reopened = db.open();
|
||||
public boolean open(@Nullable MigrationListener listener)
|
||||
throws DbException {
|
||||
boolean reopened = db.open(listener);
|
||||
shutdown.addShutdownHook(() -> {
|
||||
try {
|
||||
close();
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.briarproject.bramble.db;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.db.MigrationListener;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.bramble.util.StringUtils;
|
||||
@@ -13,6 +14,7 @@ import java.sql.DriverManager;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Properties;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
@@ -42,10 +44,11 @@ class H2Database extends JdbcDatabase {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean open() throws DbException {
|
||||
public boolean open(@Nullable MigrationListener listener)
|
||||
throws DbException {
|
||||
boolean reopen = config.databaseExists();
|
||||
if (!reopen) config.getDatabaseDirectory().mkdirs();
|
||||
super.open("org.h2.Driver", reopen);
|
||||
super.open("org.h2.Driver", reopen, listener);
|
||||
return reopen;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.briarproject.bramble.db;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.db.MigrationListener;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.bramble.util.StringUtils;
|
||||
@@ -13,6 +14,7 @@ import java.sql.DriverManager;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
@@ -44,10 +46,10 @@ class HyperSqlDatabase extends JdbcDatabase {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean open() throws DbException {
|
||||
public boolean open(@Nullable MigrationListener listener) throws DbException {
|
||||
boolean reopen = config.databaseExists();
|
||||
if (!reopen) config.getDatabaseDirectory().mkdirs();
|
||||
super.open("org.hsqldb.jdbc.JDBCDriver", reopen);
|
||||
super.open("org.hsqldb.jdbc.JDBCDriver", reopen, listener);
|
||||
return reopen;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import org.briarproject.bramble.api.db.DataTooOldException;
|
||||
import org.briarproject.bramble.api.db.DbClosedException;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.db.Metadata;
|
||||
import org.briarproject.bramble.api.db.MigrationListener;
|
||||
import org.briarproject.bramble.api.identity.Author;
|
||||
import org.briarproject.bramble.api.identity.AuthorId;
|
||||
import org.briarproject.bramble.api.identity.LocalAuthor;
|
||||
@@ -301,7 +302,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
protected void open(String driverClass, boolean reopen) throws DbException {
|
||||
protected void open(String driverClass, boolean reopen,
|
||||
@Nullable MigrationListener listener) throws DbException {
|
||||
// Load the JDBC driver
|
||||
try {
|
||||
Class.forName(driverClass);
|
||||
@@ -312,7 +314,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
Connection txn = startTransaction();
|
||||
try {
|
||||
if (reopen) {
|
||||
checkSchemaVersion(txn);
|
||||
checkSchemaVersion(txn, listener);
|
||||
} else {
|
||||
createTables(txn);
|
||||
storeSchemaVersion(txn, CODE_SCHEMA_VERSION);
|
||||
@@ -335,7 +337,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
* @throws DataTooOldException if the data uses an older schema than the
|
||||
* current code and cannot be migrated
|
||||
*/
|
||||
private void checkSchemaVersion(Connection txn) throws DbException {
|
||||
private void checkSchemaVersion(Connection txn,
|
||||
@Nullable MigrationListener listener) throws DbException {
|
||||
Settings s = getSettings(txn, DB_SETTINGS_NAMESPACE);
|
||||
int dataSchemaVersion = s.getInt(SCHEMA_VERSION_KEY, -1);
|
||||
if (dataSchemaVersion == -1) throw new DbException();
|
||||
@@ -348,6 +351,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
if (start == dataSchemaVersion) {
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Migrating from schema " + start + " to " + end);
|
||||
if (listener != null) listener.onMigrationRun();
|
||||
// Apply the migration
|
||||
m.migrate(txn);
|
||||
// Store the new schema version
|
||||
|
||||
@@ -5,7 +5,9 @@ import org.briarproject.bramble.api.crypto.KeyPair;
|
||||
import org.briarproject.bramble.api.db.DataTooNewException;
|
||||
import org.briarproject.bramble.api.db.DataTooOldException;
|
||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||
import org.briarproject.bramble.api.db.DatabaseMigrationEvent;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.db.MigrationListener;
|
||||
import org.briarproject.bramble.api.db.Transaction;
|
||||
import org.briarproject.bramble.api.event.EventBus;
|
||||
import org.briarproject.bramble.api.identity.AuthorFactory;
|
||||
@@ -15,6 +17,7 @@ import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.bramble.api.lifecycle.Service;
|
||||
import org.briarproject.bramble.api.lifecycle.ServiceException;
|
||||
import org.briarproject.bramble.api.lifecycle.event.ShutdownEvent;
|
||||
import org.briarproject.bramble.api.lifecycle.event.StartupEvent;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.sync.Client;
|
||||
|
||||
@@ -31,6 +34,9 @@ import javax.inject.Inject;
|
||||
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.MIGRATING;
|
||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.RUNNING;
|
||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.STARTING;
|
||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.ALREADY_RUNNING;
|
||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.DATA_TOO_NEW_ERROR;
|
||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.DATA_TOO_OLD_ERROR;
|
||||
@@ -40,7 +46,7 @@ import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResul
|
||||
|
||||
@ThreadSafe
|
||||
@NotNullByDefault
|
||||
class LifecycleManagerImpl implements LifecycleManager {
|
||||
class LifecycleManagerImpl implements LifecycleManager, MigrationListener {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(LifecycleManagerImpl.class.getName());
|
||||
@@ -57,6 +63,7 @@ class LifecycleManagerImpl implements LifecycleManager {
|
||||
private final CountDownLatch dbLatch = new CountDownLatch(1);
|
||||
private final CountDownLatch startupLatch = new CountDownLatch(1);
|
||||
private final CountDownLatch shutdownLatch = new CountDownLatch(1);
|
||||
private volatile LifecycleState state = STARTING;
|
||||
|
||||
@Inject
|
||||
LifecycleManagerImpl(DatabaseComponent db, EventBus eventBus,
|
||||
@@ -123,7 +130,7 @@ class LifecycleManagerImpl implements LifecycleManager {
|
||||
LOG.info("Starting services");
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
boolean reopened = db.open();
|
||||
boolean reopened = db.open(this);
|
||||
long duration = System.currentTimeMillis() - start;
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
if (reopened)
|
||||
@@ -162,6 +169,8 @@ class LifecycleManagerImpl implements LifecycleManager {
|
||||
}
|
||||
}
|
||||
startupLatch.countDown();
|
||||
state = RUNNING;
|
||||
eventBus.broadcast(new StartupEvent());
|
||||
return SUCCESS;
|
||||
} catch (DataTooOldException e) {
|
||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
@@ -180,6 +189,12 @@ class LifecycleManagerImpl implements LifecycleManager {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMigrationRun() {
|
||||
state = MIGRATING;
|
||||
eventBus.broadcast(new DatabaseMigrationEvent());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopServices() {
|
||||
try {
|
||||
@@ -235,4 +250,8 @@ class LifecycleManagerImpl implements LifecycleManager {
|
||||
shutdownLatch.await();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LifecycleState getLifecycleState() {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
int shutdownHandle = 12345;
|
||||
context.checking(new Expectations() {{
|
||||
// open()
|
||||
oneOf(database).open();
|
||||
oneOf(database).open(null);
|
||||
will(returnValue(false));
|
||||
oneOf(shutdown).addShutdownHook(with(any(Runnable.class)));
|
||||
will(returnValue(shutdownHandle));
|
||||
@@ -201,7 +201,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||
shutdown);
|
||||
|
||||
assertFalse(db.open());
|
||||
assertFalse(db.open(null));
|
||||
Transaction transaction = db.startTransaction(false);
|
||||
try {
|
||||
db.addLocalAuthor(transaction, localAuthor);
|
||||
@@ -1501,7 +1501,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
MessageId messageId2 = new MessageId(TestUtils.getRandomId());
|
||||
context.checking(new Expectations() {{
|
||||
// open()
|
||||
oneOf(database).open();
|
||||
oneOf(database).open(null);
|
||||
will(returnValue(false));
|
||||
oneOf(shutdown).addShutdownHook(with(any(Runnable.class)));
|
||||
will(returnValue(shutdownHandle));
|
||||
@@ -1543,7 +1543,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||
shutdown);
|
||||
|
||||
assertFalse(db.open());
|
||||
assertFalse(db.open(null));
|
||||
Transaction transaction = db.startTransaction(false);
|
||||
try {
|
||||
db.addLocalMessage(transaction, message, metadata, true);
|
||||
|
||||
@@ -62,7 +62,7 @@ public abstract class DatabaseMigrationTest extends BrambleMockTestCase {
|
||||
public void testDoesNotRunMigrationsWhenCreatingDatabase()
|
||||
throws Exception {
|
||||
Database<Connection> db = createDatabase(singletonList(migration));
|
||||
assertFalse(db.open());
|
||||
assertFalse(db.open(null));
|
||||
assertEquals(CODE_SCHEMA_VERSION, getDataSchemaVersion(db));
|
||||
db.close();
|
||||
}
|
||||
@@ -72,14 +72,14 @@ public abstract class DatabaseMigrationTest extends BrambleMockTestCase {
|
||||
throws Exception {
|
||||
// Open the DB for the first time
|
||||
Database<Connection> db = createDatabase(asList(migration, migration1));
|
||||
assertFalse(db.open());
|
||||
assertFalse(db.open(null));
|
||||
assertEquals(CODE_SCHEMA_VERSION, getDataSchemaVersion(db));
|
||||
// Override the data schema version
|
||||
setDataSchemaVersion(db, -1);
|
||||
db.close();
|
||||
// Reopen the DB - an exception should be thrown
|
||||
db = createDatabase(asList(migration, migration1));
|
||||
db.open();
|
||||
db.open(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -87,12 +87,12 @@ public abstract class DatabaseMigrationTest extends BrambleMockTestCase {
|
||||
throws Exception {
|
||||
// Open the DB for the first time
|
||||
Database<Connection> db = createDatabase(asList(migration, migration1));
|
||||
assertFalse(db.open());
|
||||
assertFalse(db.open(null));
|
||||
assertEquals(CODE_SCHEMA_VERSION, getDataSchemaVersion(db));
|
||||
db.close();
|
||||
// Reopen the DB - migrations should not be run
|
||||
db = createDatabase(asList(migration, migration1));
|
||||
assertTrue(db.open());
|
||||
assertTrue(db.open(null));
|
||||
assertEquals(CODE_SCHEMA_VERSION, getDataSchemaVersion(db));
|
||||
db.close();
|
||||
}
|
||||
@@ -101,14 +101,14 @@ public abstract class DatabaseMigrationTest extends BrambleMockTestCase {
|
||||
public void testThrowsExceptionIfDataIsNewerThanCode() throws Exception {
|
||||
// Open the DB for the first time
|
||||
Database<Connection> db = createDatabase(asList(migration, migration1));
|
||||
assertFalse(db.open());
|
||||
assertFalse(db.open(null));
|
||||
assertEquals(CODE_SCHEMA_VERSION, getDataSchemaVersion(db));
|
||||
// Override the data schema version
|
||||
setDataSchemaVersion(db, CODE_SCHEMA_VERSION + 1);
|
||||
db.close();
|
||||
// Reopen the DB - an exception should be thrown
|
||||
db = createDatabase(asList(migration, migration1));
|
||||
db.open();
|
||||
db.open(null);
|
||||
}
|
||||
|
||||
@Test(expected = DataTooOldException.class)
|
||||
@@ -116,13 +116,13 @@ public abstract class DatabaseMigrationTest extends BrambleMockTestCase {
|
||||
throws Exception {
|
||||
// Open the DB for the first time
|
||||
Database<Connection> db = createDatabase(emptyList());
|
||||
assertFalse(db.open());
|
||||
assertFalse(db.open(null));
|
||||
assertEquals(CODE_SCHEMA_VERSION, getDataSchemaVersion(db));
|
||||
setDataSchemaVersion(db, CODE_SCHEMA_VERSION - 1);
|
||||
db.close();
|
||||
// Reopen the DB - an exception should be thrown
|
||||
db = createDatabase(emptyList());
|
||||
db.open();
|
||||
db.open(null);
|
||||
}
|
||||
|
||||
@Test(expected = DataTooOldException.class)
|
||||
@@ -141,14 +141,14 @@ public abstract class DatabaseMigrationTest extends BrambleMockTestCase {
|
||||
|
||||
// Open the DB for the first time
|
||||
Database<Connection> db = createDatabase(asList(migration, migration1));
|
||||
assertFalse(db.open());
|
||||
assertFalse(db.open(null));
|
||||
assertEquals(CODE_SCHEMA_VERSION, getDataSchemaVersion(db));
|
||||
// Override the data schema version
|
||||
setDataSchemaVersion(db, CODE_SCHEMA_VERSION - 3);
|
||||
db.close();
|
||||
// Reopen the DB - an exception should be thrown
|
||||
db = createDatabase(asList(migration, migration1));
|
||||
db.open();
|
||||
db.open(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -170,14 +170,14 @@ public abstract class DatabaseMigrationTest extends BrambleMockTestCase {
|
||||
|
||||
// Open the DB for the first time
|
||||
Database<Connection> db = createDatabase(asList(migration, migration1));
|
||||
assertFalse(db.open());
|
||||
assertFalse(db.open(null));
|
||||
assertEquals(CODE_SCHEMA_VERSION, getDataSchemaVersion(db));
|
||||
// Override the data schema version
|
||||
setDataSchemaVersion(db, CODE_SCHEMA_VERSION - 2);
|
||||
db.close();
|
||||
// Reopen the DB - the first migration should be run
|
||||
db = createDatabase(asList(migration, migration1));
|
||||
assertTrue(db.open());
|
||||
assertTrue(db.open(null));
|
||||
assertEquals(CODE_SCHEMA_VERSION, getDataSchemaVersion(db));
|
||||
db.close();
|
||||
}
|
||||
@@ -202,14 +202,14 @@ public abstract class DatabaseMigrationTest extends BrambleMockTestCase {
|
||||
|
||||
// Open the DB for the first time
|
||||
Database<Connection> db = createDatabase(asList(migration, migration1));
|
||||
assertFalse(db.open());
|
||||
assertFalse(db.open(null));
|
||||
assertEquals(CODE_SCHEMA_VERSION, getDataSchemaVersion(db));
|
||||
// Override the data schema version
|
||||
setDataSchemaVersion(db, CODE_SCHEMA_VERSION - 2);
|
||||
db.close();
|
||||
// Reopen the DB - both migrations should be run
|
||||
db = createDatabase(asList(migration, migration1));
|
||||
assertTrue(db.open());
|
||||
assertTrue(db.open(null));
|
||||
assertEquals(CODE_SCHEMA_VERSION, getDataSchemaVersion(db));
|
||||
db.close();
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ public abstract class DatabasePerformanceComparisonTest
|
||||
throws DbException {
|
||||
Database<Connection> db = createDatabase(conditionA,
|
||||
new TestDatabaseConfig(testDir, MAX_SIZE), new SystemClock());
|
||||
db.open();
|
||||
db.open(null);
|
||||
return db;
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ public abstract class DatabaseTraceTest extends DatabasePerformanceTest {
|
||||
private Database<Connection> openDatabase() throws DbException {
|
||||
Database<Connection> db = createDatabase(
|
||||
new TestDatabaseConfig(testDir, MAX_SIZE), new SystemClock());
|
||||
db.open();
|
||||
db.open(null);
|
||||
return db;
|
||||
}
|
||||
|
||||
|
||||
@@ -1700,7 +1700,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
||||
Database<Connection> db = createDatabase(
|
||||
new TestDatabaseConfig(testDir, MAX_SIZE), clock);
|
||||
if (!resume) TestUtils.deleteTestDirectory(testDir);
|
||||
db.open();
|
||||
db.open(null);
|
||||
return db;
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ public abstract class SingleDatabasePerformanceTest
|
||||
private Database<Connection> openDatabase() throws DbException {
|
||||
Database<Connection> db = createDatabase(
|
||||
new TestDatabaseConfig(testDir, MAX_SIZE), new SystemClock());
|
||||
db.open();
|
||||
db.open(null);
|
||||
return db;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@ import javax.inject.Singleton;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
|
||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.RUNNING;
|
||||
|
||||
@Module
|
||||
public class TestLifecycleModule {
|
||||
|
||||
@@ -57,6 +59,11 @@ public class TestLifecycleModule {
|
||||
@Override
|
||||
public void waitForShutdown() throws InterruptedException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public LifecycleState getLifecycleState() {
|
||||
return RUNNING;
|
||||
}
|
||||
};
|
||||
return lifecycleManager;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user