Merge branch '279-create-client-state' into 'master'

Create local state for clients at startup. #279

Most of the clients we've written so far use private groups shared with individual contacts and/or a local group that's not shared with anyone. To make it easier to ensure that the necessary groups exist when we need them, this patch allows clients to register startup hooks for creating their local state.

Fixes #279.

See merge request !131
This commit is contained in:
akwizgran
2016-04-04 15:07:37 +00:00
20 changed files with 209 additions and 105 deletions

View File

@@ -93,10 +93,8 @@ public class AndroidModule {
AndroidNotificationManager provideAndroidNotificationManager( AndroidNotificationManager provideAndroidNotificationManager(
LifecycleManager lifecycleManager, EventBus eventBus, LifecycleManager lifecycleManager, EventBus eventBus,
AndroidNotificationManagerImpl notificationManager) { AndroidNotificationManagerImpl notificationManager) {
lifecycleManager.register(notificationManager); lifecycleManager.registerService(notificationManager);
eventBus.addListener(notificationManager); eventBus.addListener(notificationManager);
return notificationManager; return notificationManager;
} }
} }

View File

@@ -0,0 +1,12 @@
package org.briarproject.api.clients;
import org.briarproject.api.db.DbException;
import org.briarproject.api.db.Transaction;
public interface Client {
/**
* Called at startup to create any local state needed by the client.
*/
void createLocalState(Transaction txn) throws DbException;
}

View File

@@ -6,6 +6,9 @@ import org.briarproject.api.sync.Group;
public interface PrivateGroupFactory { public interface PrivateGroupFactory {
/** Creates a group that is not shared with any contacts. */
Group createLocalGroup(ClientId clientId);
/** Creates a group for the given client to share with the given contact. */ /** Creates a group for the given client to share with the given contact. */
Group createPrivateGroup(ClientId clientId, Contact contact); Group createPrivateGroup(ClientId clientId, Contact contact);
} }

View File

@@ -44,6 +44,7 @@ public interface DatabaseComponent {
* <p/> * <p/>
* This method acquires locks, so it must not be called while holding a * This method acquires locks, so it must not be called while holding a
* lock. * lock.
*
* @param readOnly true if the transaction will only be used for reading. * @param readOnly true if the transaction will only be used for reading.
*/ */
Transaction startTransaction(boolean readOnly) throws DbException; Transaction startTransaction(boolean readOnly) throws DbException;
@@ -90,13 +91,27 @@ public interface DatabaseComponent {
void addTransportKeys(Transaction txn, ContactId c, TransportKeys k) void addTransportKeys(Transaction txn, ContactId c, TransportKeys k)
throws DbException; throws DbException;
/**
* Returns true if the database contains the given contact for the given
* local pseudonym.
*/
boolean containsContact(Transaction txn, AuthorId remote, AuthorId local)
throws DbException;
/**
* Returns true if the database contains the given group.
*/
boolean containsGroup(Transaction txn, GroupId g) throws DbException;
/** /**
* Deletes the message with the given ID. The message ID and any other * Deletes the message with the given ID. The message ID and any other
* associated data are not deleted. * associated data are not deleted.
*/ */
void deleteMessage(Transaction txn, MessageId m) throws DbException; void deleteMessage(Transaction txn, MessageId m) throws DbException;
/** Deletes any metadata associated with the given message. */ /**
* Deletes any metadata associated with the given message.
*/
void deleteMessageMetadata(Transaction txn, MessageId m) throws DbException; void deleteMessageMetadata(Transaction txn, MessageId m) throws DbException;
/** /**
@@ -162,13 +177,6 @@ public interface DatabaseComponent {
Collection<ContactId> getContacts(Transaction txn, AuthorId a) Collection<ContactId> getContacts(Transaction txn, AuthorId a)
throws DbException; throws DbException;
/**
* Returns true if the database contains the given contact for the given
* local pseudonym.
*/
boolean containsContact(Transaction txn, AuthorId remote, AuthorId local)
throws DbException;
/** /**
* Returns the unique ID for this device. * Returns the unique ID for this device.
* <p/> * <p/>
@@ -359,7 +367,7 @@ public interface DatabaseComponent {
* Marks the given contact as active or inactive. * Marks the given contact as active or inactive.
*/ */
void setContactActive(Transaction txn, ContactId c, boolean active) void setContactActive(Transaction txn, ContactId c, boolean active)
throws DbException; throws DbException;
/** /**
* Marks the given message as shared or unshared. * Marks the given message as shared or unshared.

View File

@@ -1,32 +1,49 @@
package org.briarproject.api.lifecycle; package org.briarproject.api.lifecycle;
import org.briarproject.api.clients.Client;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
/** /**
* Manages the lifecycle of the app, starting and stopping {@link Service * Manages the lifecycle of the app, starting {@link
* Services}, shutting down {@link java.util.concurrent.ExecutorService * org.briarproject.api.clients.Client Clients}, starting and stopping {@link
* Service Services}, shutting down {@link java.util.concurrent.ExecutorService
* ExecutorServices}, and opening and closing the {@link * ExecutorServices}, and opening and closing the {@link
* org.briarproject.api.db.DatabaseComponent DatabaseComponent}. * org.briarproject.api.db.DatabaseComponent DatabaseComponent}.
*/ */
public interface LifecycleManager { public interface LifecycleManager {
/** The result of calling {@link LifecycleManager#startServices()}. */ /**
enum StartResult { ALREADY_RUNNING, DB_ERROR, SERVICE_ERROR, SUCCESS } * The result of calling {@link LifecycleManager#startServices()}.
*/
enum StartResult {
ALREADY_RUNNING, DB_ERROR, SERVICE_ERROR, SUCCESS
}
/** Registers a {@link Service} to be started and stopped. */ /**
public void register(Service s); * Registers a {@link Service} to be started and stopped.
*/
void registerService(Service s);
/**
* Registers a {@link org.briarproject.api.clients.Client Client} to be
* started.
*/
void registerClient(Client c);
/** /**
* Registers an {@link java.util.concurrent.ExecutorService ExecutorService} * Registers an {@link java.util.concurrent.ExecutorService ExecutorService}
* to be shut down. * to be shut down.
*/ */
public void registerForShutdown(ExecutorService e); void registerForShutdown(ExecutorService e);
/** /**
* Starts any registered {@link Service Services} and opens the {@link * Opens the {@link org.briarproject.api.db.DatabaseComponent
* org.briarproject.api.db.DatabaseComponent DatabaseComponent}. * DatabaseComponent} and starts any registered {@link
* org.briarproject.api.clients.Client Clients} and {@link Service
* Services}.
*/ */
public StartResult startServices(); StartResult startServices();
/** /**
* Stops any registered {@link Service Services}, shuts down any * Stops any registered {@link Service Services}, shuts down any
@@ -34,20 +51,21 @@ public interface LifecycleManager {
* and closes the {@link org.briarproject.api.db.DatabaseComponent * and closes the {@link org.briarproject.api.db.DatabaseComponent
* DatabaseComponent}. * DatabaseComponent}.
*/ */
public void stopServices(); void stopServices();
/** /**
* Waits for the {@link org.briarproject.api.db.DatabaseComponent * Waits for the {@link org.briarproject.api.db.DatabaseComponent
* DatabaseComponent} to be opened before returning. * DatabaseComponent} to be opened before returning.
*/ */
public void waitForDatabase() throws InterruptedException; void waitForDatabase() throws InterruptedException;
/** /**
* Waits for the {@link org.briarproject.api.db.DatabaseComponent * Waits for the {@link org.briarproject.api.db.DatabaseComponent
* DatabaseComponent} to be opened and all registered {@link Service * DatabaseComponent} to be opened and all registered {@link
* org.briarproject.api.clients.Client Clients} and {@link Service
* Services} to start before returning. * Services} to start before returning.
*/ */
public void waitForStartup() throws InterruptedException; void waitForStartup() throws InterruptedException;
/** /**
* Waits for all registered {@link Service Services} to stop, all * Waits for all registered {@link Service Services} to stop, all
@@ -55,5 +73,5 @@ public interface LifecycleManager {
* to shut down, and the {@link org.briarproject.api.db.DatabaseComponent * to shut down, and the {@link org.briarproject.api.db.DatabaseComponent
* DatabaseComponent} to be closed before returning. * DatabaseComponent} to be closed before returning.
*/ */
public void waitForShutdown() throws InterruptedException; void waitForShutdown() throws InterruptedException;
} }

View File

@@ -16,6 +16,8 @@ import javax.inject.Inject;
class PrivateGroupFactoryImpl implements PrivateGroupFactory { class PrivateGroupFactoryImpl implements PrivateGroupFactory {
private static final byte[] LOCAL_GROUP_DESCRIPTOR = new byte[0];
private final GroupFactory groupFactory; private final GroupFactory groupFactory;
private final ClientHelper clientHelper; private final ClientHelper clientHelper;
@@ -26,6 +28,11 @@ class PrivateGroupFactoryImpl implements PrivateGroupFactory {
this.clientHelper = clientHelper; this.clientHelper = clientHelper;
} }
@Override
public Group createLocalGroup(ClientId clientId) {
return groupFactory.createGroup(clientId, LOCAL_GROUP_DESCRIPTOR);
}
@Override @Override
public Group createPrivateGroup(ClientId clientId, Contact contact) { public Group createPrivateGroup(ClientId clientId, Contact contact) {
AuthorId local = contact.getLocalAuthorId(); AuthorId local = contact.getLocalAuthorId();

View File

@@ -231,6 +231,20 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
db.addTransportKeys(txn, c, k); db.addTransportKeys(txn, c, k);
} }
public boolean containsContact(Transaction transaction, AuthorId remote,
AuthorId local) throws DbException {
T txn = unbox(transaction);
if (!db.containsLocalAuthor(txn, local))
throw new NoSuchLocalAuthorException();
return db.containsContact(txn, remote, local);
}
public boolean containsGroup(Transaction transaction, GroupId g)
throws DbException {
T txn = unbox(transaction);
return db.containsGroup(txn, g);
}
public void deleteMessage(Transaction transaction, MessageId m) public void deleteMessage(Transaction transaction, MessageId m)
throws DbException { throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException(); if (transaction.isReadOnly()) throw new IllegalArgumentException();
@@ -345,14 +359,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
return db.getContacts(txn, a); return db.getContacts(txn, a);
} }
public boolean containsContact(Transaction transaction, AuthorId remote,
AuthorId local) throws DbException {
T txn = unbox(transaction);
if (!db.containsLocalAuthor(txn, local))
throw new NoSuchLocalAuthorException();
return db.containsContact(txn, remote, local);
}
public DeviceId getDeviceId(Transaction transaction) throws DbException { public DeviceId getDeviceId(Transaction transaction) throws DbException {
T txn = unbox(transaction); T txn = unbox(transaction);
return db.getDeviceId(txn); return db.getDeviceId(txn);

View File

@@ -3,14 +3,13 @@ package org.briarproject.forum;
import org.briarproject.api.clients.ClientHelper; import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.contact.ContactManager; import org.briarproject.api.contact.ContactManager;
import org.briarproject.api.crypto.CryptoComponent; import org.briarproject.api.crypto.CryptoComponent;
import org.briarproject.api.data.BdfReaderFactory;
import org.briarproject.api.data.MetadataEncoder; import org.briarproject.api.data.MetadataEncoder;
import org.briarproject.api.data.MetadataParser;
import org.briarproject.api.db.DatabaseComponent; import org.briarproject.api.db.DatabaseComponent;
import org.briarproject.api.forum.ForumManager; import org.briarproject.api.forum.ForumManager;
import org.briarproject.api.forum.ForumPostFactory; import org.briarproject.api.forum.ForumPostFactory;
import org.briarproject.api.forum.ForumSharingManager; import org.briarproject.api.forum.ForumSharingManager;
import org.briarproject.api.identity.AuthorFactory; import org.briarproject.api.identity.AuthorFactory;
import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.sync.ValidationManager; import org.briarproject.api.sync.ValidationManager;
import org.briarproject.api.system.Clock; import org.briarproject.api.system.Clock;
@@ -73,9 +72,11 @@ public class ForumModule {
@Provides @Provides
@Singleton @Singleton
ForumSharingManager provideForumSharingManager( ForumSharingManager provideForumSharingManager(
LifecycleManager lifecycleManager,
ContactManager contactManager, ContactManager contactManager,
ValidationManager validationManager, ValidationManager validationManager,
ForumSharingManagerImpl forumSharingManager) { ForumSharingManagerImpl forumSharingManager) {
lifecycleManager.registerClient(forumSharingManager);
contactManager.registerAddContactHook(forumSharingManager); contactManager.registerAddContactHook(forumSharingManager);
contactManager.registerRemoveContactHook(forumSharingManager); contactManager.registerRemoveContactHook(forumSharingManager);
validationManager.registerIncomingMessageHook( validationManager.registerIncomingMessageHook(

View File

@@ -1,6 +1,7 @@
package org.briarproject.forum; package org.briarproject.forum;
import org.briarproject.api.FormatException; import org.briarproject.api.FormatException;
import org.briarproject.api.clients.Client;
import org.briarproject.api.clients.ClientHelper; import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.clients.PrivateGroupFactory; import org.briarproject.api.clients.PrivateGroupFactory;
import org.briarproject.api.contact.Contact; import org.briarproject.api.contact.Contact;
@@ -43,15 +44,13 @@ import static org.briarproject.api.forum.ForumConstants.FORUM_SALT_LENGTH;
import static org.briarproject.api.forum.ForumConstants.MAX_FORUM_NAME_LENGTH; import static org.briarproject.api.forum.ForumConstants.MAX_FORUM_NAME_LENGTH;
import static org.briarproject.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH; import static org.briarproject.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
class ForumSharingManagerImpl implements ForumSharingManager, AddContactHook, class ForumSharingManagerImpl implements ForumSharingManager, Client,
RemoveContactHook, IncomingMessageHook { AddContactHook, RemoveContactHook, IncomingMessageHook {
static final ClientId CLIENT_ID = new ClientId(StringUtils.fromHexString( static final ClientId CLIENT_ID = new ClientId(StringUtils.fromHexString(
"cd11a5d04dccd9e2931d6fc3df456313" "cd11a5d04dccd9e2931d6fc3df456313"
+ "63bb3e9d9d0e9405fccdb051f41f5449")); + "63bb3e9d9d0e9405fccdb051f41f5449"));
private static final byte[] LOCAL_GROUP_DESCRIPTOR = new byte[0];
private final DatabaseComponent db; private final DatabaseComponent db;
private final ForumManager forumManager; private final ForumManager forumManager;
private final ClientHelper clientHelper; private final ClientHelper clientHelper;
@@ -73,8 +72,14 @@ class ForumSharingManagerImpl implements ForumSharingManager, AddContactHook,
this.privateGroupFactory = privateGroupFactory; this.privateGroupFactory = privateGroupFactory;
this.random = random; this.random = random;
this.clock = clock; this.clock = clock;
localGroup = groupFactory.createGroup(CLIENT_ID, localGroup = privateGroupFactory.createLocalGroup(CLIENT_ID);
LOCAL_GROUP_DESCRIPTOR); }
@Override
public void createLocalState(Transaction txn) throws DbException {
db.addGroup(txn, localGroup);
// Ensure we've set things up for any pre-existing contacts
for (Contact c : db.getContacts(txn)) addingContact(txn, c);
} }
@Override @Override
@@ -82,6 +87,8 @@ class ForumSharingManagerImpl implements ForumSharingManager, AddContactHook,
try { try {
// Create a group to share with the contact // Create a group to share with the contact
Group g = getContactGroup(c); Group g = getContactGroup(c);
// Return if we've already set things up for this contact
if (db.containsGroup(txn, g.getId())) return;
// Store the group and share it with the contact // Store the group and share it with the contact
db.addGroup(txn, g); db.addGroup(txn, g);
db.setVisibleToContact(txn, c.getId(), g.getId(), true); db.setVisibleToContact(txn, c.getId(), g.getId(), true);
@@ -297,8 +304,6 @@ class ForumSharingManagerImpl implements ForumSharingManager, AddContactHook,
private List<Forum> getForumsSharedWithAllContacts(Transaction txn) private List<Forum> getForumsSharedWithAllContacts(Transaction txn)
throws DbException, FormatException { throws DbException, FormatException {
// Ensure the local group exists
db.addGroup(txn, localGroup);
// Find the latest update in the local group // Find the latest update in the local group
LatestUpdate latest = findLatest(txn, localGroup.getId(), true); LatestUpdate latest = findLatest(txn, localGroup.getId(), true);
if (latest == null) return Collections.emptyList(); if (latest == null) return Collections.emptyList();

View File

@@ -1,6 +1,7 @@
package org.briarproject.introduction; package org.briarproject.introduction;
import org.briarproject.api.FormatException; import org.briarproject.api.FormatException;
import org.briarproject.api.clients.Client;
import org.briarproject.api.clients.ClientHelper; import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.clients.MessageQueueManager; import org.briarproject.api.clients.MessageQueueManager;
import org.briarproject.api.clients.PrivateGroupFactory; import org.briarproject.api.clients.PrivateGroupFactory;
@@ -31,7 +32,6 @@ import org.briarproject.api.introduction.SessionId;
import org.briarproject.api.properties.TransportPropertyManager; import org.briarproject.api.properties.TransportPropertyManager;
import org.briarproject.api.sync.ClientId; import org.briarproject.api.sync.ClientId;
import org.briarproject.api.sync.Group; import org.briarproject.api.sync.Group;
import org.briarproject.api.sync.GroupFactory;
import org.briarproject.api.sync.GroupId; import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.Message; import org.briarproject.api.sync.Message;
import org.briarproject.api.sync.MessageId; import org.briarproject.api.sync.MessageId;
@@ -82,14 +82,13 @@ import static org.briarproject.api.introduction.IntroductionConstants.TYPE_REQUE
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_RESPONSE; import static org.briarproject.api.introduction.IntroductionConstants.TYPE_RESPONSE;
class IntroductionManagerImpl extends BdfIncomingMessageHook class IntroductionManagerImpl extends BdfIncomingMessageHook
implements IntroductionManager, AddContactHook, RemoveContactHook { implements IntroductionManager, Client, AddContactHook,
RemoveContactHook {
static final ClientId CLIENT_ID = new ClientId(StringUtils.fromHexString( static final ClientId CLIENT_ID = new ClientId(StringUtils.fromHexString(
"23b1897c198a90ae75b976ac023d0f32" "23b1897c198a90ae75b976ac023d0f32"
+ "80ca67b12f2346b2c23a34f34e2434c3")); + "80ca67b12f2346b2c23a34f34e2434c3"));
private static final byte[] LOCAL_GROUP_DESCRIPTOR = new byte[0];
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(IntroductionManagerImpl.class.getName()); Logger.getLogger(IntroductionManagerImpl.class.getName());
@@ -104,8 +103,7 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
@Inject @Inject
IntroductionManagerImpl(DatabaseComponent db, IntroductionManagerImpl(DatabaseComponent db,
MessageQueueManager messageQueueManager, MessageQueueManager messageQueueManager,
ClientHelper clientHelper, GroupFactory groupFactory, ClientHelper clientHelper, PrivateGroupFactory privateGroupFactory,
PrivateGroupFactory privateGroupFactory,
MetadataEncoder metadataEncoder, MetadataParser metadataParser, MetadataEncoder metadataEncoder, MetadataParser metadataParser,
CryptoComponent cryptoComponent, CryptoComponent cryptoComponent,
TransportPropertyManager transportPropertyManager, TransportPropertyManager transportPropertyManager,
@@ -117,6 +115,7 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
this.messageQueueManager = messageQueueManager; this.messageQueueManager = messageQueueManager;
this.privateGroupFactory = privateGroupFactory; this.privateGroupFactory = privateGroupFactory;
this.metadataEncoder = metadataEncoder; this.metadataEncoder = metadataEncoder;
// TODO: Inject these dependencies for easier testing
this.introducerManager = this.introducerManager =
new IntroducerManager(this, clientHelper, clock, new IntroducerManager(this, clientHelper, clock,
cryptoComponent); cryptoComponent);
@@ -124,8 +123,7 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
new IntroduceeManager(db, this, clientHelper, clock, new IntroduceeManager(db, this, clientHelper, clock,
cryptoComponent, transportPropertyManager, cryptoComponent, transportPropertyManager,
authorFactory, contactManager); authorFactory, contactManager);
localGroup = localGroup = privateGroupFactory.createLocalGroup(CLIENT_ID);
groupFactory.createGroup(CLIENT_ID, LOCAL_GROUP_DESCRIPTOR);
} }
@Override @Override
@@ -133,11 +131,21 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
return CLIENT_ID; return CLIENT_ID;
} }
@Override
public void createLocalState(Transaction txn) throws DbException {
db.addGroup(txn, localGroup);
// Ensure we've set things up for any pre-existing contacts
for (Contact c : db.getContacts(txn)) addingContact(txn, c);
}
@Override @Override
public void addingContact(Transaction txn, Contact c) throws DbException { public void addingContact(Transaction txn, Contact c) throws DbException {
try { try {
// create an introduction group for sending introduction messages // Create an introduction group for sending introduction messages
Group g = getIntroductionGroup(c); Group g = getIntroductionGroup(c);
// Return if we've already set things up for this contact
if (db.containsGroup(txn, g.getId())) return;
// Store the group and share it with the contact
db.addGroup(txn, g); db.addGroup(txn, g);
db.setVisibleToContact(txn, c.getId(), g.getId(), true); db.setVisibleToContact(txn, c.getId(), g.getId(), true);
// Attach the contact ID to the group // Attach the contact ID to the group
@@ -169,7 +177,6 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
introducerManager.abort(txn, d); introducerManager.abort(txn, d);
} }
} }
} }
} catch (FormatException e) { } catch (FormatException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
@@ -189,9 +196,6 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
protected void incomingMessage(Transaction txn, Message m, BdfList body, protected void incomingMessage(Transaction txn, Message m, BdfList body,
BdfDictionary message) throws DbException { BdfDictionary message) throws DbException {
// add local group for engine states to make sure it exists
db.addGroup(txn, localGroup);
// Get message data and type // Get message data and type
GroupId groupId = m.getGroupId(); GroupId groupId = m.getGroupId();
message.put(GROUP_ID, groupId); message.put(GROUP_ID, groupId);
@@ -265,15 +269,13 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
public void makeIntroduction(Contact c1, Contact c2, String msg) public void makeIntroduction(Contact c1, Contact c2, String msg)
throws DbException, FormatException { throws DbException, FormatException {
Transaction txn = db.startTransaction(false); Transaction txn = db.startTransaction(false);
try { try {
// add local group for session states to make sure it exists introducerManager.makeIntroduction(txn, c1, c2, msg);
db.addGroup(txn, getLocalGroup()); txn.setComplete();
introducerManager.makeIntroduction(txn, c1, c2, msg); } finally {
txn.setComplete(); db.endTransaction(txn);
} finally { }
db.endTransaction(txn);
}
} }
@Override @Override
@@ -468,7 +470,7 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
} }
if (LOG.isLoggable(WARNING)) { if (LOG.isLoggable(WARNING)) {
LOG.warning( LOG.warning(
"No session state found for this message with session ID " + "No session state found for message with session ID " +
Arrays.hashCode(sessionId)); Arrays.hashCode(sessionId));
} }
throw new FormatException(); throw new FormatException();
@@ -505,5 +507,4 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
db.deleteMessage(txn, messageId); db.deleteMessage(txn, messageId);
db.deleteMessageMetadata(txn, messageId); db.deleteMessageMetadata(txn, messageId);
} }
} }

View File

@@ -5,6 +5,7 @@ import org.briarproject.api.clients.MessageQueueManager;
import org.briarproject.api.contact.ContactManager; import org.briarproject.api.contact.ContactManager;
import org.briarproject.api.data.MetadataEncoder; import org.briarproject.api.data.MetadataEncoder;
import org.briarproject.api.introduction.IntroductionManager; import org.briarproject.api.introduction.IntroductionManager;
import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.system.Clock; import org.briarproject.api.system.Clock;
import javax.inject.Inject; import javax.inject.Inject;
@@ -41,15 +42,17 @@ public class IntroductionModule {
@Provides @Provides
@Singleton @Singleton
IntroductionManager getIntroductionManager( IntroductionManager getIntroductionManager(
LifecycleManager lifecycleManager,
ContactManager contactManager, ContactManager contactManager,
MessageQueueManager messageQueueManager, MessageQueueManager messageQueueManager,
IntroductionManagerImpl introductionManager) { IntroductionManagerImpl introductionManager) {
lifecycleManager.registerClient(introductionManager);
contactManager.registerAddContactHook(introductionManager); contactManager.registerAddContactHook(introductionManager);
contactManager.registerRemoveContactHook(introductionManager); contactManager.registerRemoveContactHook(introductionManager);
messageQueueManager messageQueueManager.registerIncomingMessageHook(
.registerIncomingMessageHook(introductionManager.getClientId(), introductionManager.getClientId(),
introductionManager); introductionManager);
return introductionManager; return introductionManager;
} }

View File

@@ -1,14 +1,16 @@
package org.briarproject.lifecycle; package org.briarproject.lifecycle;
import org.briarproject.api.clients.Client;
import org.briarproject.api.db.DatabaseComponent; import org.briarproject.api.db.DatabaseComponent;
import org.briarproject.api.db.DbException; import org.briarproject.api.db.DbException;
import org.briarproject.api.db.Transaction;
import org.briarproject.api.event.EventBus; import org.briarproject.api.event.EventBus;
import org.briarproject.api.event.ShutdownEvent; import org.briarproject.api.event.ShutdownEvent;
import org.briarproject.api.lifecycle.LifecycleManager; import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.lifecycle.Service; import org.briarproject.api.lifecycle.Service;
import java.io.IOException; import java.io.IOException;
import java.util.Collection; import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
@@ -31,8 +33,9 @@ class LifecycleManagerImpl implements LifecycleManager {
private final DatabaseComponent db; private final DatabaseComponent db;
private final EventBus eventBus; private final EventBus eventBus;
private final Collection<Service> services; private final List<Service> services;
private final Collection<ExecutorService> executors; private final List<Client> clients;
private final List<ExecutorService> executors;
private final Semaphore startStopSemaphore = new Semaphore(1); private final Semaphore startStopSemaphore = new Semaphore(1);
private final CountDownLatch dbLatch = new CountDownLatch(1); private final CountDownLatch dbLatch = new CountDownLatch(1);
private final CountDownLatch startupLatch = new CountDownLatch(1); private final CountDownLatch startupLatch = new CountDownLatch(1);
@@ -43,15 +46,22 @@ class LifecycleManagerImpl implements LifecycleManager {
this.db = db; this.db = db;
this.eventBus = eventBus; this.eventBus = eventBus;
services = new CopyOnWriteArrayList<Service>(); services = new CopyOnWriteArrayList<Service>();
clients = new CopyOnWriteArrayList<Client>();
executors = new CopyOnWriteArrayList<ExecutorService>(); executors = new CopyOnWriteArrayList<ExecutorService>();
} }
public void register(Service s) { public void registerService(Service s) {
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
LOG.info("Registering service " + s.getClass().getName()); LOG.info("Registering service " + s.getClass().getName());
services.add(s); services.add(s);
} }
public void registerClient(Client c) {
if (LOG.isLoggable(INFO))
LOG.info("Registering client " + c.getClass().getName());
clients.add(c);
}
public void registerForShutdown(ExecutorService e) { public void registerForShutdown(ExecutorService e) {
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
LOG.info("Registering executor " + e.getClass().getName()); LOG.info("Registering executor " + e.getClass().getName());
@@ -74,15 +84,28 @@ class LifecycleManagerImpl implements LifecycleManager {
else LOG.info("Creating database took " + duration + " ms"); else LOG.info("Creating database took " + duration + " ms");
} }
dbLatch.countDown(); dbLatch.countDown();
Transaction txn = db.startTransaction(false);
try {
for (Client c : clients) {
start = System.currentTimeMillis();
c.createLocalState(txn);
duration = System.currentTimeMillis() - start;
if (LOG.isLoggable(INFO)) {
LOG.info("Starting " + c.getClass().getName()
+ " took " + duration + " ms");
}
}
txn.setComplete();
} finally {
db.endTransaction(txn);
}
for (Service s : services) { for (Service s : services) {
start = System.currentTimeMillis(); start = System.currentTimeMillis();
boolean started = s.start(); boolean started = s.start();
duration = System.currentTimeMillis() - start; duration = System.currentTimeMillis() - start;
if (!started) { if (!started) {
if (LOG.isLoggable(WARNING)) { if (LOG.isLoggable(WARNING))
String name = s.getClass().getName(); LOG.warning(s.getClass().getName() + " did not start");
LOG.warning(name + " did not start");
}
return SERVICE_ERROR; return SERVICE_ERROR;
} }
if (LOG.isLoggable(INFO)) { if (LOG.isLoggable(INFO)) {

View File

@@ -1,6 +1,7 @@
package org.briarproject.messaging; package org.briarproject.messaging;
import org.briarproject.api.FormatException; import org.briarproject.api.FormatException;
import org.briarproject.api.clients.Client;
import org.briarproject.api.clients.ClientHelper; import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.clients.PrivateGroupFactory; import org.briarproject.api.clients.PrivateGroupFactory;
import org.briarproject.api.contact.Contact; import org.briarproject.api.contact.Contact;
@@ -28,7 +29,7 @@ import java.util.Map;
import javax.inject.Inject; import javax.inject.Inject;
class MessagingManagerImpl implements MessagingManager, AddContactHook, class MessagingManagerImpl implements MessagingManager, Client, AddContactHook,
RemoveContactHook { RemoveContactHook {
static final ClientId CLIENT_ID = new ClientId(StringUtils.fromHexString( static final ClientId CLIENT_ID = new ClientId(StringUtils.fromHexString(
@@ -47,11 +48,19 @@ class MessagingManagerImpl implements MessagingManager, AddContactHook,
this.privateGroupFactory = privateGroupFactory; this.privateGroupFactory = privateGroupFactory;
} }
@Override
public void createLocalState(Transaction txn) throws DbException {
// Ensure we've set things up for any pre-existing contacts
for (Contact c : db.getContacts(txn)) addingContact(txn, c);
}
@Override @Override
public void addingContact(Transaction txn, Contact c) throws DbException { public void addingContact(Transaction txn, Contact c) throws DbException {
try { try {
// Create a group to share with the contact // Create a group to share with the contact
Group g = getContactGroup(c); Group g = getContactGroup(c);
// Return if we've already set things up for this contact
if (db.containsGroup(txn, g.getId())) return;
// Store the group and share it with the contact // Store the group and share it with the contact
db.addGroup(txn, g); db.addGroup(txn, g);
db.setVisibleToContact(txn, c.getId(), g.getId(), true); db.setVisibleToContact(txn, c.getId(), g.getId(), true);

View File

@@ -3,6 +3,7 @@ package org.briarproject.messaging;
import org.briarproject.api.clients.ClientHelper; import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.contact.ContactManager; import org.briarproject.api.contact.ContactManager;
import org.briarproject.api.data.MetadataEncoder; import org.briarproject.api.data.MetadataEncoder;
import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.messaging.MessagingManager; import org.briarproject.api.messaging.MessagingManager;
import org.briarproject.api.messaging.PrivateMessageFactory; import org.briarproject.api.messaging.PrivateMessageFactory;
import org.briarproject.api.sync.ValidationManager; import org.briarproject.api.sync.ValidationManager;
@@ -43,8 +44,10 @@ public class MessagingModule {
@Provides @Provides
@Singleton @Singleton
MessagingManager getMessagingManager(ContactManager contactManager, MessagingManager getMessagingManager(LifecycleManager lifecycleManager,
ContactManager contactManager,
MessagingManagerImpl messagingManager) { MessagingManagerImpl messagingManager) {
lifecycleManager.registerClient(messagingManager);
contactManager.registerAddContactHook(messagingManager); contactManager.registerAddContactHook(messagingManager);
contactManager.registerRemoveContactHook(messagingManager); contactManager.registerRemoveContactHook(messagingManager);
return messagingManager; return messagingManager;

View File

@@ -64,7 +64,7 @@ public class PluginsModule {
@Singleton @Singleton
PluginManager getPluginManager(LifecycleManager lifecycleManager, PluginManager getPluginManager(LifecycleManager lifecycleManager,
PluginManagerImpl pluginManager) { PluginManagerImpl pluginManager) {
lifecycleManager.register(pluginManager); lifecycleManager.registerService(pluginManager);
return pluginManager; return pluginManager;
} }
} }

View File

@@ -3,6 +3,7 @@ package org.briarproject.properties;
import org.briarproject.api.clients.ClientHelper; import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.contact.ContactManager; import org.briarproject.api.contact.ContactManager;
import org.briarproject.api.data.MetadataEncoder; import org.briarproject.api.data.MetadataEncoder;
import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.properties.TransportPropertyManager; import org.briarproject.api.properties.TransportPropertyManager;
import org.briarproject.api.sync.ValidationManager; import org.briarproject.api.sync.ValidationManager;
import org.briarproject.api.system.Clock; import org.briarproject.api.system.Clock;
@@ -36,8 +37,10 @@ public class PropertiesModule {
@Provides @Singleton @Provides @Singleton
TransportPropertyManager getTransportPropertyManager( TransportPropertyManager getTransportPropertyManager(
LifecycleManager lifecycleManager,
ContactManager contactManager, ContactManager contactManager,
TransportPropertyManagerImpl transportPropertyManager) { TransportPropertyManagerImpl transportPropertyManager) {
lifecycleManager.registerClient(transportPropertyManager);
contactManager.registerAddContactHook(transportPropertyManager); contactManager.registerAddContactHook(transportPropertyManager);
contactManager.registerRemoveContactHook(transportPropertyManager); contactManager.registerRemoveContactHook(transportPropertyManager);
return transportPropertyManager; return transportPropertyManager;

View File

@@ -3,6 +3,7 @@ package org.briarproject.properties;
import org.briarproject.api.DeviceId; import org.briarproject.api.DeviceId;
import org.briarproject.api.FormatException; import org.briarproject.api.FormatException;
import org.briarproject.api.TransportId; import org.briarproject.api.TransportId;
import org.briarproject.api.clients.Client;
import org.briarproject.api.clients.ClientHelper; import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.clients.PrivateGroupFactory; import org.briarproject.api.clients.PrivateGroupFactory;
import org.briarproject.api.contact.Contact; import org.briarproject.api.contact.Contact;
@@ -13,13 +14,11 @@ import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.data.BdfList; import org.briarproject.api.data.BdfList;
import org.briarproject.api.db.DatabaseComponent; import org.briarproject.api.db.DatabaseComponent;
import org.briarproject.api.db.DbException; import org.briarproject.api.db.DbException;
import org.briarproject.api.db.NoSuchGroupException;
import org.briarproject.api.db.Transaction; import org.briarproject.api.db.Transaction;
import org.briarproject.api.properties.TransportProperties; import org.briarproject.api.properties.TransportProperties;
import org.briarproject.api.properties.TransportPropertyManager; import org.briarproject.api.properties.TransportPropertyManager;
import org.briarproject.api.sync.ClientId; import org.briarproject.api.sync.ClientId;
import org.briarproject.api.sync.Group; import org.briarproject.api.sync.Group;
import org.briarproject.api.sync.GroupFactory;
import org.briarproject.api.sync.GroupId; import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.Message; import org.briarproject.api.sync.Message;
import org.briarproject.api.sync.MessageId; import org.briarproject.api.sync.MessageId;
@@ -34,14 +33,12 @@ import java.util.Map.Entry;
import javax.inject.Inject; import javax.inject.Inject;
class TransportPropertyManagerImpl implements TransportPropertyManager, class TransportPropertyManagerImpl implements TransportPropertyManager,
AddContactHook, RemoveContactHook { Client, AddContactHook, RemoveContactHook {
static final ClientId CLIENT_ID = new ClientId(StringUtils.fromHexString( static final ClientId CLIENT_ID = new ClientId(StringUtils.fromHexString(
"673ea091673561e28f70122f6a8ea8f4" "673ea091673561e28f70122f6a8ea8f4"
+ "97c3624b86fa07f785bb15f09fb87b4b")); + "97c3624b86fa07f785bb15f09fb87b4b"));
private static final byte[] LOCAL_GROUP_DESCRIPTOR = new byte[0];
private final DatabaseComponent db; private final DatabaseComponent db;
private final ClientHelper clientHelper; private final ClientHelper clientHelper;
private final PrivateGroupFactory privateGroupFactory; private final PrivateGroupFactory privateGroupFactory;
@@ -50,20 +47,28 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
@Inject @Inject
TransportPropertyManagerImpl(DatabaseComponent db, TransportPropertyManagerImpl(DatabaseComponent db,
ClientHelper clientHelper, GroupFactory groupFactory, ClientHelper clientHelper, PrivateGroupFactory privateGroupFactory,
PrivateGroupFactory privateGroupFactory, Clock clock) { Clock clock) {
this.db = db; this.db = db;
this.clientHelper = clientHelper; this.clientHelper = clientHelper;
this.privateGroupFactory = privateGroupFactory; this.privateGroupFactory = privateGroupFactory;
this.clock = clock; this.clock = clock;
localGroup = groupFactory.createGroup(CLIENT_ID, localGroup = privateGroupFactory.createLocalGroup(CLIENT_ID);
LOCAL_GROUP_DESCRIPTOR); }
@Override
public void createLocalState(Transaction txn) throws DbException {
db.addGroup(txn, localGroup);
// Ensure we've set things up for any pre-existing contacts
for (Contact c : db.getContacts(txn)) addingContact(txn, c);
} }
@Override @Override
public void addingContact(Transaction txn, Contact c) throws DbException { public void addingContact(Transaction txn, Contact c) throws DbException {
// Create a group to share with the contact // Create a group to share with the contact
Group g = getContactGroup(c); Group g = getContactGroup(c);
// Return if we've already set things up for this contact
if (db.containsGroup(txn, g.getId())) return;
// Store the group and share it with the contact // Store the group and share it with the contact
db.addGroup(txn, g); db.addGroup(txn, g);
db.setVisibleToContact(txn, c.getId(), g.getId(), true); db.setVisibleToContact(txn, c.getId(), g.getId(), true);
@@ -126,9 +131,6 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
db.endTransaction(txn); db.endTransaction(txn);
} }
return p; return p;
} catch (NoSuchGroupException e) {
// Local group doesn't exist - there are no local properties
return null;
} catch (FormatException e) { } catch (FormatException e) {
throw new DbException(e); throw new DbException(e);
} }
@@ -169,8 +171,6 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
try { try {
Transaction txn = db.startTransaction(false); Transaction txn = db.startTransaction(false);
try { try {
// Create the local group if necessary
db.addGroup(txn, localGroup);
// Merge the new properties with any existing properties // Merge the new properties with any existing properties
TransportProperties merged; TransportProperties merged;
boolean changed; boolean changed;
@@ -230,9 +230,6 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
local.put(e.getKey(), parseProperties(message)); local.put(e.getKey(), parseProperties(message));
} }
return local; return local;
} catch (NoSuchGroupException e) {
// Local group doesn't exist - there are no local properties
return Collections.emptyMap();
} catch (FormatException e) { } catch (FormatException e) {
throw new DbException(e); throw new DbException(e);
} }

View File

@@ -62,7 +62,7 @@ public class SyncModule {
@Singleton @Singleton
ValidationManager getValidationManager(LifecycleManager lifecycleManager, ValidationManager getValidationManager(LifecycleManager lifecycleManager,
EventBus eventBus, ValidationManagerImpl validationManager) { EventBus eventBus, ValidationManagerImpl validationManager) {
lifecycleManager.register(validationManager); lifecycleManager.registerService(validationManager);
eventBus.addListener(validationManager); eventBus.addListener(validationManager);
return validationManager; return validationManager;
} }

View File

@@ -37,7 +37,7 @@ public class TransportModule {
@Singleton @Singleton
KeyManager getKeyManager(LifecycleManager lifecycleManager, KeyManager getKeyManager(LifecycleManager lifecycleManager,
EventBus eventBus, KeyManagerImpl keyManager) { EventBus eventBus, KeyManagerImpl keyManager) {
lifecycleManager.register(keyManager); lifecycleManager.registerService(keyManager);
eventBus.addListener(keyManager); eventBus.addListener(keyManager);
return keyManager; return keyManager;
} }

View File

@@ -1,5 +1,6 @@
package org.briarproject; package org.briarproject;
import org.briarproject.api.clients.Client;
import org.briarproject.api.lifecycle.IoExecutor; import org.briarproject.api.lifecycle.IoExecutor;
import org.briarproject.api.lifecycle.LifecycleManager; import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.lifecycle.Service; import org.briarproject.api.lifecycle.Service;
@@ -20,8 +21,14 @@ public class TestLifecycleModule {
@Provides @Provides
LifecycleManager provideLifecycleManager() { LifecycleManager provideLifecycleManager() {
return new LifecycleManager() { return new LifecycleManager() {
@Override @Override
public void register(Service s) { public void registerService(Service s) {
}
@Override
public void registerClient(Client c) {
} }
@@ -60,6 +67,7 @@ public class TestLifecycleModule {
@Provides @Provides
ShutdownManager provideShutdownManager() { ShutdownManager provideShutdownManager() {
return new ShutdownManager() { return new ShutdownManager() {
@Override @Override
public int addShutdownHook(Runnable hook) { public int addShutdownHook(Runnable hook) {
return 0; return 0;
@@ -75,8 +83,7 @@ public class TestLifecycleModule {
@Provides @Provides
@IoExecutor @IoExecutor
@Singleton @Singleton
Executor provideExecutor() { Executor provideIoExecutor() {
return Executors.newCachedThreadPool(); return Executors.newCachedThreadPool();
} }
} }