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

@@ -15,6 +15,8 @@
<string name="passwords_do_not_match">Passwords do not match</string>
<string name="enter_password">Enter your password:</string>
<string name="try_again">Wrong password, try again:</string>
<string name="startup_failed_notification_title">Briar could not start up</string>
<string name="startup_failed_notification_text">You may need to reinstall Briar.</string>
<string name="expiry_warning">This software has expired.\nPlease install a newer version.</string>
<string name="contact_list_button">Contacts</string>
<string name="forums_button">Forums</string>

View File

@@ -1,6 +1,7 @@
package org.briarproject.android;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP;
import static java.util.logging.Level.INFO;
@@ -13,6 +14,7 @@ import org.briarproject.R;
import org.briarproject.api.android.AndroidExecutor;
import org.briarproject.api.db.DatabaseConfig;
import org.briarproject.api.lifecycle.LifecycleManager;
import roboguice.service.RoboService;
import android.app.PendingIntent;
import android.content.ComponentName;
@@ -30,11 +32,11 @@ public class BriarService extends RoboService {
private final Binder binder = new BriarBinder();
@Inject private DatabaseConfig databaseConfig;
private boolean started = false;
// Fields that are accessed from background threads must be volatile
@Inject private volatile LifecycleManager lifecycleManager;
@Inject private volatile AndroidExecutor androidExecutor;
private volatile boolean started = false;
@Override
public void onCreate() {
@@ -51,21 +53,31 @@ public class BriarService extends RoboService {
b.setContentTitle(getText(R.string.notification_title));
b.setContentText(getText(R.string.notification_text));
b.setWhen(0); // Don't show the time
b.setOngoing(true);
// Touch the notification to show the home screen
Intent i = new Intent(this, HomeScreenActivity.class);
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pi = PendingIntent.getActivity(this, 0, i, 0);
b.setContentIntent(pi);
b.setOngoing(true);
i.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP |
FLAG_ACTIVITY_SINGLE_TOP);
b.setContentIntent(PendingIntent.getActivity(this, 0, i, 0));
startForeground(1, b.build());
// Start the services in a background thread
new Thread() {
@Override
public void run() {
lifecycleManager.startServices();
if(lifecycleManager.startServices()) {
started = true;
} else {
if(LOG.isLoggable(INFO)) LOG.info("Startup failed");
Intent i = new Intent(BriarService.this,
HomeScreenActivity.class);
i.setFlags(FLAG_ACTIVITY_NEW_TASK |
FLAG_ACTIVITY_CLEAR_TOP);
i.putExtra("briar.STARTUP_FAILED", true);
startActivity(i);
stopSelf();
}
}
}.start();
started = true;
}
@Override
@@ -84,11 +96,11 @@ public class BriarService extends RoboService {
super.onDestroy();
if(LOG.isLoggable(INFO)) LOG.info("Destroyed");
// Stop the services in a background thread
if(started) new Thread() {
new Thread() {
@Override
public void run() {
androidExecutor.shutdown();
lifecycleManager.stopServices();
if(started) lifecycleManager.stopServices();
}
}.start();
}

View File

@@ -1,5 +1,6 @@
package org.briarproject.android;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.text.InputType.TYPE_CLASS_TEXT;
import static android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD;
import static android.view.Gravity.CENTER;
@@ -41,10 +42,15 @@ import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.util.StringUtils;
import roboguice.activity.RoboActivity;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.IBinder;
import android.support.v4.app.NotificationCompat;
import android.text.Editable;
import android.view.KeyEvent;
import android.view.View;
@@ -90,9 +96,17 @@ public class HomeScreenActivity extends RoboActivity {
@Override
public void onCreate(Bundle state) {
super.onCreate(state);
if(LOG.isLoggable(INFO)) LOG.info("Created");
Intent i = getIntent();
long handle = i.getLongExtra("org.briarproject.LOCAL_AUTHOR_HANDLE", -1);
if(handle != -1) {
boolean failed = i.getBooleanExtra("briar.STARTUP_FAILED", false);
long handle = i.getLongExtra("briar.LOCAL_AUTHOR_HANDLE", -1);
if(failed) {
// LifecycleManager failed to start all necessary services
showStartupFailureNotification();
finish();
if(LOG.isLoggable(INFO)) LOG.info("Exiting");
System.exit(0);
} else if(handle != -1) {
// The activity was launched from the setup wizard
if(System.currentTimeMillis() < EXPIRY_DATE) {
showSpinner();
@@ -118,13 +132,30 @@ public class HomeScreenActivity extends RoboActivity {
}
}
private void showStartupFailureNotification() {
NotificationCompat.Builder b = new NotificationCompat.Builder(this);
b.setSmallIcon(android.R.drawable.stat_notify_error);
b.setContentTitle(getText(R.string.startup_failed_notification_title));
b.setContentText(getText(R.string.startup_failed_notification_text));
// Touch the notification to relaunch the app
Intent i = new Intent(this, HomeScreenActivity.class);
i.setFlags(FLAG_ACTIVITY_NEW_TASK);
b.setContentIntent(PendingIntent.getActivity(this, 0, i, 0));
Notification n = b.build();
Object o = getSystemService(Context.NOTIFICATION_SERVICE);
NotificationManager nm = (NotificationManager) o;
nm.notify(0, n);
}
private void showSpinner() {
LinearLayout layout = new LinearLayout(this);
layout.setLayoutParams(MATCH_MATCH);
layout.setGravity(CENTER);
ProgressBar progress = new ProgressBar(this);
progress.setIndeterminate(true);
layout.addView(progress);
setContentView(layout);
}

View File

@@ -247,7 +247,7 @@ public class SetupActivity extends RoboActivity implements OnClickListener {
public void run() {
Intent i = new Intent(SetupActivity.this,
HomeScreenActivity.class);
i.putExtra("org.briarproject.LOCAL_AUTHOR_HANDLE", handle);
i.putExtra("briar.LOCAL_AUTHOR_HANDLE", handle);
i.setFlags(FLAG_ACTIVITY_NEW_TASK);
startActivity(i);
finish();

View File

@@ -212,10 +212,10 @@ ConnectionListener {
GroupId inbox = item.getInboxGroupId();
AuthorId localAuthorId = item.getContact().getLocalAuthorId();
Intent i = new Intent(this, ConversationActivity.class);
i.putExtra("org.briarproject.CONTACT_ID", contactId.getInt());
i.putExtra("org.briarproject.CONTACT_NAME", contactName);
i.putExtra("org.briarproject.GROUP_ID", inbox.getBytes());
i.putExtra("org.briarproject.LOCAL_AUTHOR_ID", localAuthorId.getBytes());
i.putExtra("briar.CONTACT_ID", contactId.getInt());
i.putExtra("briar.CONTACT_NAME", contactName);
i.putExtra("briar.GROUP_ID", inbox.getBytes());
i.putExtra("briar.LOCAL_AUTHOR_ID", localAuthorId.getBytes());
startActivity(i);
}

View File

@@ -68,16 +68,16 @@ implements EventListener, OnClickListener, OnItemClickListener {
super.onCreate(state);
Intent i = getIntent();
int id = i.getIntExtra("org.briarproject.CONTACT_ID", -1);
int id = i.getIntExtra("briar.CONTACT_ID", -1);
if(id == -1) throw new IllegalStateException();
contactId = new ContactId(id);
contactName = i.getStringExtra("org.briarproject.CONTACT_NAME");
contactName = i.getStringExtra("briar.CONTACT_NAME");
if(contactName == null) throw new IllegalStateException();
setTitle(contactName);
byte[] b = i.getByteArrayExtra("org.briarproject.GROUP_ID");
byte[] b = i.getByteArrayExtra("briar.GROUP_ID");
if(b == null) throw new IllegalStateException();
groupId = new GroupId(b);
b = i.getByteArrayExtra("org.briarproject.LOCAL_AUTHOR_ID");
b = i.getByteArrayExtra("briar.LOCAL_AUTHOR_ID");
if(b == null) throw new IllegalStateException();
localAuthorId = new AuthorId(b);
@@ -221,9 +221,9 @@ implements EventListener, OnClickListener, OnItemClickListener {
public void onClick(View view) {
Intent i = new Intent(this, WritePrivateMessageActivity.class);
i.putExtra("org.briarproject.CONTACT_NAME", contactName);
i.putExtra("org.briarproject.GROUP_ID", groupId.getBytes());
i.putExtra("org.briarproject.LOCAL_AUTHOR_ID", localAuthorId.getBytes());
i.putExtra("briar.CONTACT_NAME", contactName);
i.putExtra("briar.GROUP_ID", groupId.getBytes());
i.putExtra("briar.LOCAL_AUTHOR_ID", localAuthorId.getBytes());
startActivity(i);
}
@@ -235,14 +235,14 @@ implements EventListener, OnClickListener, OnItemClickListener {
private void displayMessage(int position) {
MessageHeader header = adapter.getItem(position).getHeader();
Intent i = new Intent(this, ReadPrivateMessageActivity.class);
i.putExtra("org.briarproject.CONTACT_ID", contactId.getInt());
i.putExtra("org.briarproject.CONTACT_NAME", contactName);
i.putExtra("org.briarproject.GROUP_ID", groupId.getBytes());
i.putExtra("org.briarproject.LOCAL_AUTHOR_ID", localAuthorId.getBytes());
i.putExtra("org.briarproject.AUTHOR_NAME", header.getAuthor().getName());
i.putExtra("org.briarproject.MESSAGE_ID", header.getId().getBytes());
i.putExtra("org.briarproject.CONTENT_TYPE", header.getContentType());
i.putExtra("org.briarproject.TIMESTAMP", header.getTimestamp());
i.putExtra("briar.CONTACT_ID", contactId.getInt());
i.putExtra("briar.CONTACT_NAME", contactName);
i.putExtra("briar.GROUP_ID", groupId.getBytes());
i.putExtra("briar.LOCAL_AUTHOR_ID", localAuthorId.getBytes());
i.putExtra("briar.AUTHOR_NAME", header.getAuthor().getName());
i.putExtra("briar.MESSAGE_ID", header.getId().getBytes());
i.putExtra("briar.CONTENT_TYPE", header.getContentType());
i.putExtra("briar.TIMESTAMP", header.getTimestamp());
startActivityForResult(i, position);
}
}

View File

@@ -72,30 +72,30 @@ implements OnClickListener {
super.onCreate(state);
Intent i = getIntent();
contactName = i.getStringExtra("org.briarproject.CONTACT_NAME");
contactName = i.getStringExtra("briar.CONTACT_NAME");
if(contactName == null) throw new IllegalStateException();
setTitle(contactName);
byte[] b = i.getByteArrayExtra("org.briarproject.LOCAL_AUTHOR_ID");
byte[] b = i.getByteArrayExtra("briar.LOCAL_AUTHOR_ID");
if(b == null) throw new IllegalStateException();
localAuthorId = new AuthorId(b);
String authorName = i.getStringExtra("org.briarproject.AUTHOR_NAME");
String authorName = i.getStringExtra("briar.AUTHOR_NAME");
if(authorName == null) throw new IllegalStateException();
b = i.getByteArrayExtra("org.briarproject.MESSAGE_ID");
b = i.getByteArrayExtra("briar.MESSAGE_ID");
if(b == null) throw new IllegalStateException();
messageId = new MessageId(b);
b = i.getByteArrayExtra("org.briarproject.GROUP_ID");
b = i.getByteArrayExtra("briar.GROUP_ID");
if(b == null) throw new IllegalStateException();
groupId = new GroupId(b);
String contentType = i.getStringExtra("org.briarproject.CONTENT_TYPE");
String contentType = i.getStringExtra("briar.CONTENT_TYPE");
if(contentType == null) throw new IllegalStateException();
timestamp = i.getLongExtra("org.briarproject.TIMESTAMP", -1);
timestamp = i.getLongExtra("briar.TIMESTAMP", -1);
if(timestamp == -1) throw new IllegalStateException();
if(state == null) {
read = false;
setReadInDatabase(true);
} else {
read = state.getBoolean("org.briarproject.READ");
read = state.getBoolean("briar.READ");
}
LinearLayout layout = new LinearLayout(this);
@@ -257,7 +257,7 @@ implements OnClickListener {
@Override
public void onSaveInstanceState(Bundle state) {
super.onSaveInstanceState(state);
state.putBoolean("org.briarproject.READ", read);
state.putBoolean("briar.READ", read);
}
public void onClick(View view) {
@@ -271,12 +271,12 @@ implements OnClickListener {
finish();
} else if(view == replyButton) {
Intent i = new Intent(this, WritePrivateMessageActivity.class);
i.putExtra("org.briarproject.CONTACT_NAME", contactName);
i.putExtra("org.briarproject.GROUP_ID", groupId.getBytes());
i.putExtra("org.briarproject.LOCAL_AUTHOR_ID",
i.putExtra("briar.CONTACT_NAME", contactName);
i.putExtra("briar.GROUP_ID", groupId.getBytes());
i.putExtra("briar.LOCAL_AUTHOR_ID",
localAuthorId.getBytes());
i.putExtra("org.briarproject.PARENT_ID", messageId.getBytes());
i.putExtra("org.briarproject.TIMESTAMP", timestamp);
i.putExtra("briar.PARENT_ID", messageId.getBytes());
i.putExtra("briar.TIMESTAMP", timestamp);
startActivity(i);
setResult(RESULT_REPLY);
finish();

View File

@@ -78,17 +78,17 @@ implements OnClickListener {
super.onCreate(state);
Intent i = getIntent();
contactName = i.getStringExtra("org.briarproject.CONTACT_NAME");
contactName = i.getStringExtra("briar.CONTACT_NAME");
if(contactName == null) throw new IllegalStateException();
byte[] b = i.getByteArrayExtra("org.briarproject.GROUP_ID");
byte[] b = i.getByteArrayExtra("briar.GROUP_ID");
if(b == null) throw new IllegalStateException();
groupId = new GroupId(b);
b = i.getByteArrayExtra("org.briarproject.LOCAL_AUTHOR_ID");
b = i.getByteArrayExtra("briar.LOCAL_AUTHOR_ID");
if(b == null) throw new IllegalStateException();
localAuthorId = new AuthorId(b);
b = i.getByteArrayExtra("org.briarproject.PARENT_ID");
b = i.getByteArrayExtra("briar.PARENT_ID");
if(b != null) parentId = new MessageId(b);
timestamp = i.getLongExtra("org.briarproject.TIMESTAMP", -1);
timestamp = i.getLongExtra("briar.TIMESTAMP", -1);
LinearLayout layout = new LinearLayout(this);
layout.setLayoutParams(MATCH_WRAP);

View File

@@ -69,17 +69,17 @@ SelectContactsDialog.Listener {
super.onCreate(state);
Intent i = getIntent();
byte[] b = i.getByteArrayExtra("org.briarproject.GROUP_ID");
byte[] b = i.getByteArrayExtra("briar.GROUP_ID");
if(b == null) throw new IllegalStateException();
GroupId id = new GroupId(b);
String name = i.getStringExtra("org.briarproject.GROUP_NAME");
String name = i.getStringExtra("briar.GROUP_NAME");
if(name == null) throw new IllegalStateException();
setTitle(name);
b = i.getByteArrayExtra("org.briarproject.GROUP_SALT");
b = i.getByteArrayExtra("briar.GROUP_SALT");
if(b == null) throw new IllegalStateException();
group = new Group(id, name, b);
subscribed = i.getBooleanExtra("org.briarproject.SUBSCRIBED", false);
boolean all = i.getBooleanExtra("org.briarproject.VISIBLE_TO_ALL", false);
subscribed = i.getBooleanExtra("briar.SUBSCRIBED", false);
boolean all = i.getBooleanExtra("briar.VISIBLE_TO_ALL", false);
LinearLayout layout = new LinearLayout(this);
layout.setLayoutParams(MATCH_MATCH);

View File

@@ -67,10 +67,10 @@ OnClickListener, OnItemClickListener {
super.onCreate(state);
Intent i = getIntent();
byte[] b = i.getByteArrayExtra("org.briarproject.GROUP_ID");
byte[] b = i.getByteArrayExtra("briar.GROUP_ID");
if(b == null) throw new IllegalStateException();
groupId = new GroupId(b);
groupName = i.getStringExtra("org.briarproject.GROUP_NAME");
groupName = i.getStringExtra("briar.GROUP_NAME");
if(groupName == null) throw new IllegalStateException();
setTitle(groupName);
@@ -210,7 +210,7 @@ OnClickListener, OnItemClickListener {
public void onClick(View view) {
Intent i = new Intent(this, WriteGroupPostActivity.class);
i.putExtra("org.briarproject.GROUP_ID", groupId.getBytes());
i.putExtra("briar.GROUP_ID", groupId.getBytes());
startActivity(i);
}
@@ -222,16 +222,16 @@ OnClickListener, OnItemClickListener {
private void displayMessage(int position) {
MessageHeader item = adapter.getItem(position);
Intent i = new Intent(this, ReadGroupPostActivity.class);
i.putExtra("org.briarproject.GROUP_ID", groupId.getBytes());
i.putExtra("org.briarproject.GROUP_NAME", groupName);
i.putExtra("org.briarproject.MESSAGE_ID", item.getId().getBytes());
i.putExtra("briar.GROUP_ID", groupId.getBytes());
i.putExtra("briar.GROUP_NAME", groupName);
i.putExtra("briar.MESSAGE_ID", item.getId().getBytes());
Author author = item.getAuthor();
if(author != null) {
i.putExtra("org.briarproject.AUTHOR_ID", author.getId().getBytes());
i.putExtra("org.briarproject.AUTHOR_NAME", author.getName());
i.putExtra("briar.AUTHOR_ID", author.getId().getBytes());
i.putExtra("briar.AUTHOR_NAME", author.getName());
}
i.putExtra("org.briarproject.CONTENT_TYPE", item.getContentType());
i.putExtra("org.briarproject.TIMESTAMP", item.getTimestamp());
i.putExtra("briar.CONTENT_TYPE", item.getContentType());
i.putExtra("briar.TIMESTAMP", item.getTimestamp());
startActivityForResult(i, position);
}
}

View File

@@ -347,8 +347,8 @@ implements EventListener, OnClickListener, OnItemClickListener {
} else {
Intent i = new Intent(this, GroupActivity.class);
Group g = item.getGroup();
i.putExtra("org.briarproject.GROUP_ID", g.getId().getBytes());
i.putExtra("org.briarproject.GROUP_NAME", g.getName());
i.putExtra("briar.GROUP_ID", g.getId().getBytes());
i.putExtra("briar.GROUP_NAME", g.getName());
startActivity(i);
}
}

View File

@@ -132,11 +132,11 @@ implements EventListener, OnItemClickListener {
GroupStatus s = item.getGroupStatus();
Group g = s.getGroup();
Intent i = new Intent(this, ConfigureGroupActivity.class);
i.putExtra("org.briarproject.GROUP_ID", g.getId().getBytes());
i.putExtra("org.briarproject.GROUP_NAME", g.getName());
i.putExtra("org.briarproject.GROUP_SALT", g.getSalt());
i.putExtra("org.briarproject.SUBSCRIBED", s.isSubscribed());
i.putExtra("org.briarproject.VISIBLE_TO_ALL", s.isVisibleToAll());
i.putExtra("briar.GROUP_ID", g.getId().getBytes());
i.putExtra("briar.GROUP_NAME", g.getName());
i.putExtra("briar.GROUP_SALT", g.getSalt());
i.putExtra("briar.SUBSCRIBED", s.isSubscribed());
i.putExtra("briar.VISIBLE_TO_ALL", s.isVisibleToAll());
startActivity(i);
}

View File

@@ -69,26 +69,26 @@ implements OnClickListener {
super.onCreate(state);
Intent i = getIntent();
byte[] b = i.getByteArrayExtra("org.briarproject.GROUP_ID");
byte[] b = i.getByteArrayExtra("briar.GROUP_ID");
if(b == null) throw new IllegalStateException();
groupId = new GroupId(b);
String groupName = i.getStringExtra("org.briarproject.GROUP_NAME");
String groupName = i.getStringExtra("briar.GROUP_NAME");
if(groupName == null) throw new IllegalStateException();
setTitle(groupName);
b = i.getByteArrayExtra("org.briarproject.MESSAGE_ID");
b = i.getByteArrayExtra("briar.MESSAGE_ID");
if(b == null) throw new IllegalStateException();
messageId = new MessageId(b);
String contentType = i.getStringExtra("org.briarproject.CONTENT_TYPE");
String contentType = i.getStringExtra("briar.CONTENT_TYPE");
if(contentType == null) throw new IllegalStateException();
timestamp = i.getLongExtra("org.briarproject.TIMESTAMP", -1);
timestamp = i.getLongExtra("briar.TIMESTAMP", -1);
if(timestamp == -1) throw new IllegalStateException();
String authorName = i.getStringExtra("org.briarproject.AUTHOR_NAME");
String authorName = i.getStringExtra("briar.AUTHOR_NAME");
if(state == null) {
read = false;
setReadInDatabase(true);
} else {
read = state.getBoolean("org.briarproject.READ");
read = state.getBoolean("briar.READ");
}
LinearLayout layout = new LinearLayout(this);
@@ -255,7 +255,7 @@ implements OnClickListener {
@Override
public void onSaveInstanceState(Bundle state) {
super.onSaveInstanceState(state);
state.putBoolean("org.briarproject.READ", read);
state.putBoolean("briar.READ", read);
}
public void onClick(View view) {
@@ -269,9 +269,9 @@ implements OnClickListener {
finish();
} else if(view == replyButton) {
Intent i = new Intent(this, WriteGroupPostActivity.class);
i.putExtra("org.briarproject.GROUP_ID", groupId.getBytes());
i.putExtra("org.briarproject.PARENT_ID", messageId.getBytes());
i.putExtra("org.briarproject.TIMESTAMP", timestamp);
i.putExtra("briar.GROUP_ID", groupId.getBytes());
i.putExtra("briar.PARENT_ID", messageId.getBytes());
i.putExtra("briar.TIMESTAMP", timestamp);
startActivity(i);
setResult(RESULT_REPLY);
finish();

View File

@@ -89,16 +89,16 @@ implements OnItemSelectedListener, OnClickListener {
super.onCreate(state);
Intent i = getIntent();
byte[] b = i.getByteArrayExtra("org.briarproject.GROUP_ID");
byte[] b = i.getByteArrayExtra("briar.GROUP_ID");
if(b == null) throw new IllegalStateException();
groupId = new GroupId(b);
b = i.getByteArrayExtra("org.briarproject.PARENT_ID");
b = i.getByteArrayExtra("briar.PARENT_ID");
if(b != null) parentId = new MessageId(b);
timestamp = i.getLongExtra("org.briarproject.TIMESTAMP", -1);
timestamp = i.getLongExtra("briar.TIMESTAMP", -1);
if(state != null) {
b = state.getByteArray("org.briarproject.LOCAL_AUTHOR_ID");
b = state.getByteArray("briar.LOCAL_AUTHOR_ID");
if(b != null) localAuthorId = new AuthorId(b);
}
@@ -214,7 +214,7 @@ implements OnItemSelectedListener, OnClickListener {
super.onSaveInstanceState(state);
if(localAuthorId != null) {
byte[] b = localAuthorId.getBytes();
state.putByteArray("org.briarproject.LOCAL_AUTHOR_ID", b);
state.putByteArray("briar.LOCAL_AUTHOR_ID", b);
}
}

View File

@@ -78,17 +78,17 @@ implements InvitationListener {
setView(new NetworkSetupView(this));
} else {
// Restore the activity's state
byte[] b = state.getByteArray("org.briarproject.LOCAL_AUTHOR_ID");
byte[] b = state.getByteArray("briar.LOCAL_AUTHOR_ID");
if(b != null) localAuthorId = new AuthorId(b);
taskHandle = state.getLong("org.briarproject.TASK_HANDLE", -1);
taskHandle = state.getLong("briar.TASK_HANDLE", -1);
task = referenceManager.getReference(taskHandle,
InvitationTask.class);
if(task == null) {
// No background task - we must be in an initial or final state
localInvitationCode = state.getInt("org.briarproject.LOCAL_CODE");
remoteInvitationCode = state.getInt("org.briarproject.REMOTE_CODE");
connectionFailed = state.getBoolean("org.briarproject.FAILED");
contactName = state.getString("org.briarproject.CONTACT_NAME");
localInvitationCode = state.getInt("briar.LOCAL_CODE");
remoteInvitationCode = state.getInt("briar.REMOTE_CODE");
connectionFailed = state.getBoolean("briar.FAILED");
contactName = state.getString("briar.CONTACT_NAME");
if(contactName != null) {
localCompared = remoteCompared = true;
localMatched = remoteMatched = true;
@@ -184,13 +184,13 @@ implements InvitationListener {
super.onSaveInstanceState(state);
if(localAuthorId != null) {
byte[] b = localAuthorId.getBytes();
state.putByteArray("org.briarproject.LOCAL_AUTHOR_ID", b);
state.putByteArray("briar.LOCAL_AUTHOR_ID", b);
}
state.putInt("org.briarproject.LOCAL_CODE", localInvitationCode);
state.putInt("org.briarproject.REMOTE_CODE", remoteInvitationCode);
state.putBoolean("org.briarproject.FAILED", connectionFailed);
state.putString("org.briarproject.CONTACT_NAME", contactName);
if(task != null) state.putLong("org.briarproject.TASK_HANDLE", taskHandle);
state.putInt("briar.LOCAL_CODE", localInvitationCode);
state.putInt("briar.REMOTE_CODE", remoteInvitationCode);
state.putBoolean("briar.FAILED", connectionFailed);
state.putString("briar.CONTACT_NAME", contactName);
if(task != null) state.putLong("briar.TASK_HANDLE", taskHandle);
}
@Override

View File

@@ -0,0 +1,10 @@
package org.briarproject.api.db;
/**
* Thrown when the schema of the database differs from the schema expected by
* the code.
*/
public class DbSchemaException extends DbException {
private static final long serialVersionUID = -4444069279533651710L;
}

View File

@@ -13,8 +13,11 @@ public interface LifecycleManager {
*/
public void registerForShutdown(ExecutorService e);
/** Starts any registered {@link Service}s. */
public void startServices();
/**
* Starts any registered {@link Service}s and returns true if all services
* started successfully.
*/
public boolean startServices();
/**
* Stops any registered {@link Service}s and shuts down any registered

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;
}
}

View File

@@ -17,7 +17,7 @@ public class TestLifecycleModule extends AbstractModule {
public void registerForShutdown(ExecutorService e) {}
public void startServices() {}
public boolean startServices() { return true; }
public void stopServices() {}