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

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

View File

@@ -231,6 +231,20 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
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)
throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
@@ -345,14 +359,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
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 {
T txn = unbox(transaction);
return db.getDeviceId(txn);

View File

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

View File

@@ -1,6 +1,7 @@
package org.briarproject.forum;
import org.briarproject.api.FormatException;
import org.briarproject.api.clients.Client;
import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.clients.PrivateGroupFactory;
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.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
class ForumSharingManagerImpl implements ForumSharingManager, AddContactHook,
RemoveContactHook, IncomingMessageHook {
class ForumSharingManagerImpl implements ForumSharingManager, Client,
AddContactHook, RemoveContactHook, IncomingMessageHook {
static final ClientId CLIENT_ID = new ClientId(StringUtils.fromHexString(
"cd11a5d04dccd9e2931d6fc3df456313"
+ "63bb3e9d9d0e9405fccdb051f41f5449"));
private static final byte[] LOCAL_GROUP_DESCRIPTOR = new byte[0];
private final DatabaseComponent db;
private final ForumManager forumManager;
private final ClientHelper clientHelper;
@@ -73,8 +72,14 @@ class ForumSharingManagerImpl implements ForumSharingManager, AddContactHook,
this.privateGroupFactory = privateGroupFactory;
this.random = random;
this.clock = clock;
localGroup = groupFactory.createGroup(CLIENT_ID,
LOCAL_GROUP_DESCRIPTOR);
localGroup = privateGroupFactory.createLocalGroup(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
@@ -82,6 +87,8 @@ class ForumSharingManagerImpl implements ForumSharingManager, AddContactHook,
try {
// Create a group to share with the contact
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
db.addGroup(txn, g);
db.setVisibleToContact(txn, c.getId(), g.getId(), true);
@@ -297,8 +304,6 @@ class ForumSharingManagerImpl implements ForumSharingManager, AddContactHook,
private List<Forum> getForumsSharedWithAllContacts(Transaction txn)
throws DbException, FormatException {
// Ensure the local group exists
db.addGroup(txn, localGroup);
// Find the latest update in the local group
LatestUpdate latest = findLatest(txn, localGroup.getId(), true);
if (latest == null) return Collections.emptyList();

View File

@@ -1,6 +1,7 @@
package org.briarproject.introduction;
import org.briarproject.api.FormatException;
import org.briarproject.api.clients.Client;
import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.clients.MessageQueueManager;
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.sync.ClientId;
import org.briarproject.api.sync.Group;
import org.briarproject.api.sync.GroupFactory;
import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.Message;
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;
class IntroductionManagerImpl extends BdfIncomingMessageHook
implements IntroductionManager, AddContactHook, RemoveContactHook {
implements IntroductionManager, Client, AddContactHook,
RemoveContactHook {
static final ClientId CLIENT_ID = new ClientId(StringUtils.fromHexString(
"23b1897c198a90ae75b976ac023d0f32"
+ "80ca67b12f2346b2c23a34f34e2434c3"));
private static final byte[] LOCAL_GROUP_DESCRIPTOR = new byte[0];
private static final Logger LOG =
Logger.getLogger(IntroductionManagerImpl.class.getName());
@@ -104,8 +103,7 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
@Inject
IntroductionManagerImpl(DatabaseComponent db,
MessageQueueManager messageQueueManager,
ClientHelper clientHelper, GroupFactory groupFactory,
PrivateGroupFactory privateGroupFactory,
ClientHelper clientHelper, PrivateGroupFactory privateGroupFactory,
MetadataEncoder metadataEncoder, MetadataParser metadataParser,
CryptoComponent cryptoComponent,
TransportPropertyManager transportPropertyManager,
@@ -117,6 +115,7 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
this.messageQueueManager = messageQueueManager;
this.privateGroupFactory = privateGroupFactory;
this.metadataEncoder = metadataEncoder;
// TODO: Inject these dependencies for easier testing
this.introducerManager =
new IntroducerManager(this, clientHelper, clock,
cryptoComponent);
@@ -124,8 +123,7 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
new IntroduceeManager(db, this, clientHelper, clock,
cryptoComponent, transportPropertyManager,
authorFactory, contactManager);
localGroup =
groupFactory.createGroup(CLIENT_ID, LOCAL_GROUP_DESCRIPTOR);
localGroup = privateGroupFactory.createLocalGroup(CLIENT_ID);
}
@Override
@@ -133,11 +131,21 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
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
public void addingContact(Transaction txn, Contact c) throws DbException {
try {
// create an introduction group for sending introduction messages
// Create an introduction group for sending introduction messages
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.setVisibleToContact(txn, c.getId(), g.getId(), true);
// Attach the contact ID to the group
@@ -169,7 +177,6 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
introducerManager.abort(txn, d);
}
}
}
} catch (FormatException 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,
BdfDictionary message) throws DbException {
// add local group for engine states to make sure it exists
db.addGroup(txn, localGroup);
// Get message data and type
GroupId groupId = m.getGroupId();
message.put(GROUP_ID, groupId);
@@ -265,15 +269,13 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
public void makeIntroduction(Contact c1, Contact c2, String msg)
throws DbException, FormatException {
Transaction txn = db.startTransaction(false);
try {
// add local group for session states to make sure it exists
db.addGroup(txn, getLocalGroup());
introducerManager.makeIntroduction(txn, c1, c2, msg);
txn.setComplete();
} finally {
db.endTransaction(txn);
}
Transaction txn = db.startTransaction(false);
try {
introducerManager.makeIntroduction(txn, c1, c2, msg);
txn.setComplete();
} finally {
db.endTransaction(txn);
}
}
@Override
@@ -468,7 +470,7 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
}
if (LOG.isLoggable(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));
}
throw new FormatException();
@@ -505,5 +507,4 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
db.deleteMessage(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.data.MetadataEncoder;
import org.briarproject.api.introduction.IntroductionManager;
import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.system.Clock;
import javax.inject.Inject;
@@ -41,15 +42,17 @@ public class IntroductionModule {
@Provides
@Singleton
IntroductionManager getIntroductionManager(
LifecycleManager lifecycleManager,
ContactManager contactManager,
MessageQueueManager messageQueueManager,
IntroductionManagerImpl introductionManager) {
lifecycleManager.registerClient(introductionManager);
contactManager.registerAddContactHook(introductionManager);
contactManager.registerRemoveContactHook(introductionManager);
messageQueueManager
.registerIncomingMessageHook(introductionManager.getClientId(),
introductionManager);
messageQueueManager.registerIncomingMessageHook(
introductionManager.getClientId(),
introductionManager);
return introductionManager;
}

View File

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

View File

@@ -1,6 +1,7 @@
package org.briarproject.messaging;
import org.briarproject.api.FormatException;
import org.briarproject.api.clients.Client;
import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.clients.PrivateGroupFactory;
import org.briarproject.api.contact.Contact;
@@ -28,7 +29,7 @@ import java.util.Map;
import javax.inject.Inject;
class MessagingManagerImpl implements MessagingManager, AddContactHook,
class MessagingManagerImpl implements MessagingManager, Client, AddContactHook,
RemoveContactHook {
static final ClientId CLIENT_ID = new ClientId(StringUtils.fromHexString(
@@ -47,11 +48,19 @@ class MessagingManagerImpl implements MessagingManager, AddContactHook,
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
public void addingContact(Transaction txn, Contact c) throws DbException {
try {
// Create a group to share with the contact
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
db.addGroup(txn, g);
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.contact.ContactManager;
import org.briarproject.api.data.MetadataEncoder;
import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.messaging.MessagingManager;
import org.briarproject.api.messaging.PrivateMessageFactory;
import org.briarproject.api.sync.ValidationManager;
@@ -43,8 +44,10 @@ public class MessagingModule {
@Provides
@Singleton
MessagingManager getMessagingManager(ContactManager contactManager,
MessagingManager getMessagingManager(LifecycleManager lifecycleManager,
ContactManager contactManager,
MessagingManagerImpl messagingManager) {
lifecycleManager.registerClient(messagingManager);
contactManager.registerAddContactHook(messagingManager);
contactManager.registerRemoveContactHook(messagingManager);
return messagingManager;

View File

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

View File

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

View File

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

View File

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

View File

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