Store schema version in database. Dev task #50.

If the schema of the database is incompatible with the schema expected
by the code, the database throws a DbSchemaException. LifecycleManager
indicates the error to BriarService, which uses HomeScreenActivity to
show a notification and quit the app.
This commit is contained in:
akwizgran
2014-01-09 21:00:40 +00:00
parent c779d7b95a
commit 623e7330ed
20 changed files with 237 additions and 117 deletions

View File

@@ -34,6 +34,7 @@ import org.briarproject.api.TransportId;
import org.briarproject.api.TransportProperties;
import org.briarproject.api.db.DbClosedException;
import org.briarproject.api.db.DbException;
import org.briarproject.api.db.DbSchemaException;
import org.briarproject.api.db.MessageHeader;
import org.briarproject.api.messaging.Group;
import org.briarproject.api.messaging.GroupId;
@@ -56,6 +57,14 @@ import org.briarproject.api.transport.TemporarySecret;
*/
abstract class JdbcDatabase implements Database<Connection> {
private static final int SCHEMA_VERSION = 1;
private static final String CREATE_SETTINGS =
"CREATE TABLE settings"
+ " (key VARCHAR NOT NULL,"
+ " value VARCHAR NOT NULL,"
+ " PRIMARY KEY (key))";
// Locking: identity
// Dependents: contact, message, retention, subscription, transport, window
private static final String CREATE_LOCAL_AUTHORS =
@@ -356,7 +365,13 @@ abstract class JdbcDatabase implements Database<Connection> {
// Open the database and create the tables if necessary
Connection txn = startTransaction();
try {
if(!reopen) createTables(txn);
if(reopen) {
if(!checkSchemaVersion(txn))
throw new DbSchemaException();
} else {
createTables(txn);
setSchemaVersion(txn);
}
commitTransaction(txn);
} catch(DbException e) {
abortTransaction(txn);
@@ -364,10 +379,53 @@ abstract class JdbcDatabase implements Database<Connection> {
}
}
private boolean checkSchemaVersion(Connection txn) throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
String value;
try {
String sql = "SELECT value FROM settings WHERE key = ?";
ps = txn.prepareStatement(sql);
ps.setString(1, "schemaVersion");
rs = ps.executeQuery();
if(!rs.next()) throw new DbStateException();
value = rs.getString(1);
if(rs.next()) throw new DbStateException();
rs.close();
ps.close();
} catch(SQLException e) {
tryToClose(rs);
tryToClose(ps);
throw new DbException(e);
}
try {
return Integer.valueOf(value) == SCHEMA_VERSION;
} catch(NumberFormatException e) {
throw new DbException(e);
}
}
private void tryToClose(ResultSet rs) {
if(rs != null) try {
rs.close();
} catch(SQLException e) {
if(LOG.isLoggable(WARNING))LOG.log(WARNING, e.toString(), e);
}
}
private void tryToClose(Statement s) {
if(s != null) try {
s.close();
} catch(SQLException e) {
if(LOG.isLoggable(WARNING))LOG.log(WARNING, e.toString(), e);
}
}
private void createTables(Connection txn) throws DbException {
Statement s = null;
try {
s = txn.createStatement();
s.executeUpdate(insertTypeNames(CREATE_SETTINGS));
s.executeUpdate(insertTypeNames(CREATE_LOCAL_AUTHORS));
s.executeUpdate(insertTypeNames(CREATE_CONTACTS));
s.executeUpdate(insertTypeNames(CREATE_GROUPS));
@@ -405,19 +463,19 @@ abstract class JdbcDatabase implements Database<Connection> {
return s;
}
private void tryToClose(Statement s) {
if(s != null) try {
s.close();
private void setSchemaVersion(Connection txn) throws DbException {
PreparedStatement ps = null;
try {
String sql = "INSERT INTO settings (key, value) VALUES (?, ?)";
ps = txn.prepareStatement(sql);
ps.setString(1, "schemaVersion");
ps.setString(2, String.valueOf(SCHEMA_VERSION));
int affected = ps.executeUpdate();
if(affected != 1) throw new DbStateException();
ps.close();
} catch(SQLException e) {
if(LOG.isLoggable(WARNING))LOG.log(WARNING, e.toString(), e);
}
}
private void tryToClose(ResultSet rs) {
if(rs != null) try {
rs.close();
} catch(SQLException e) {
if(LOG.isLoggable(WARNING))LOG.log(WARNING, e.toString(), e);
tryToClose(ps);
throw new DbException(e);
}
}

View File

@@ -48,7 +48,7 @@ class LifecycleManagerImpl implements LifecycleManager {
executors.add(e);
}
public void startServices() {
public boolean startServices() {
try {
if(LOG.isLoggable(INFO)) LOG.info("Starting");
boolean reopened = db.open();
@@ -64,12 +64,16 @@ class LifecycleManagerImpl implements LifecycleManager {
if(started) LOG.info("Service started: " + name);
else LOG.info("Service failed to start: " + name);
}
if(!started) return false;
}
startupLatch.countDown();
return true;
} catch(DbException e) {
if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
return false;
} catch(IOException e) {
if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
return false;
}
}