Merge branch '427-local-author-caching' into 'master'

Cache the local author and load before the db latch is released

Closes #427, #588 

See merge request !354
This commit is contained in:
akwizgran
2016-11-01 17:21:14 +00:00
45 changed files with 294 additions and 665 deletions

View File

@@ -26,8 +26,6 @@ import org.briarproject.api.identity.Author;
import org.briarproject.api.identity.Author.Status;
import org.briarproject.api.identity.AuthorId;
import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.identity.IdentityManager.AddIdentityHook;
import org.briarproject.api.identity.IdentityManager.RemoveIdentityHook;
import org.briarproject.api.identity.LocalAuthor;
import org.briarproject.api.sync.ClientId;
import org.briarproject.api.sync.Group;
@@ -75,8 +73,7 @@ import static org.briarproject.api.contact.ContactManager.RemoveContactHook;
import static org.briarproject.blogs.BlogPostValidator.authorToBdfDictionary;
class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
AddContactHook, RemoveContactHook, Client,
AddIdentityHook, RemoveIdentityHook {
AddContactHook, RemoveContactHook, Client {
private static final Logger LOG =
Logger.getLogger(BlogManagerImpl.class.getName());
@@ -151,25 +148,6 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
}
}
@Override
public void addingIdentity(Transaction txn, LocalAuthor a)
throws DbException {
// add a personal blog for the new identity
LOG.info("New Personal Blog Added.");
Blog b = blogFactory.createBlog(a);
db.addGroup(txn, b.getGroup());
}
@Override
public void removingIdentity(Transaction txn, LocalAuthor a)
throws DbException {
// remove the personal blog of that identity
Blog b = blogFactory.createBlog(a);
db.removeGroup(txn, b.getGroup());
}
@Override
protected boolean incomingMessage(Transaction txn, Message m, BdfList list,
BdfDictionary meta) throws DbException, FormatException {

View File

@@ -7,7 +7,6 @@ import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.contact.ContactManager;
import org.briarproject.api.data.MetadataEncoder;
import org.briarproject.api.identity.AuthorFactory;
import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.sync.GroupFactory;
import org.briarproject.api.sync.MessageFactory;
@@ -36,14 +35,11 @@ public class BlogsModule {
@Singleton
BlogManager provideBlogManager(BlogManagerImpl blogManager,
LifecycleManager lifecycleManager, ContactManager contactManager,
IdentityManager identityManager,
ValidationManager validationManager) {
lifecycleManager.registerClient(blogManager);
contactManager.registerAddContactHook(blogManager);
contactManager.registerRemoveContactHook(blogManager);
identityManager.registerAddIdentityHook(blogManager);
identityManager.registerRemoveIdentityHook(blogManager);
validationManager.registerIncomingMessageHook(CLIENT_ID, blogManager);
return blogManager;
}

View File

@@ -9,8 +9,6 @@ import org.briarproject.api.db.DbException;
import org.briarproject.api.db.Transaction;
import org.briarproject.api.identity.Author;
import org.briarproject.api.identity.AuthorId;
import org.briarproject.api.identity.IdentityManager.RemoveIdentityHook;
import org.briarproject.api.identity.LocalAuthor;
import org.briarproject.api.transport.KeyManager;
import java.util.ArrayList;
@@ -21,7 +19,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
import javax.inject.Inject;
class ContactManagerImpl implements ContactManager, RemoveIdentityHook {
class ContactManagerImpl implements ContactManager {
private final DatabaseComponent db;
private final KeyManager keyManager;
@@ -148,11 +146,4 @@ class ContactManagerImpl implements ContactManager, RemoveIdentityHook {
db.removeContact(txn, c);
}
@Override
public void removingIdentity(Transaction txn, LocalAuthor a)
throws DbException {
// Remove any contacts of the local pseudonym that's being removed
for (ContactId c : db.getContacts(txn, a.getId()))
removeContact(txn, c);
}
}

View File

@@ -2,17 +2,6 @@ package org.briarproject.contact;
import org.briarproject.api.contact.ContactExchangeTask;
import org.briarproject.api.contact.ContactManager;
import org.briarproject.api.crypto.CryptoComponent;
import org.briarproject.api.data.BdfReaderFactory;
import org.briarproject.api.data.BdfWriterFactory;
import org.briarproject.api.db.DatabaseComponent;
import org.briarproject.api.identity.AuthorFactory;
import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.plugins.ConnectionManager;
import org.briarproject.api.properties.TransportPropertyManager;
import org.briarproject.api.system.Clock;
import org.briarproject.api.transport.StreamReaderFactory;
import org.briarproject.api.transport.StreamWriterFactory;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -30,9 +19,7 @@ public class ContactModule {
@Provides
@Singleton
ContactManager getContactManager(IdentityManager identityManager,
ContactManagerImpl contactManager) {
identityManager.registerRemoveIdentityHook(contactManager);
ContactManager getContactManager(ContactManagerImpl contactManager) {
return contactManager;
}

View File

@@ -9,7 +9,6 @@ import com.rometools.rome.io.XmlReader;
import org.briarproject.api.FormatException;
import org.briarproject.api.TransportId;
import org.briarproject.api.blogs.Blog;
import org.briarproject.api.blogs.BlogManager;
import org.briarproject.api.blogs.BlogPost;
import org.briarproject.api.blogs.BlogPostFactory;
@@ -27,7 +26,6 @@ import org.briarproject.api.event.EventListener;
import org.briarproject.api.event.TransportEnabledEvent;
import org.briarproject.api.feed.Feed;
import org.briarproject.api.feed.FeedManager;
import org.briarproject.api.identity.AuthorId;
import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.identity.LocalAuthor;
import org.briarproject.api.lifecycle.IoExecutor;
@@ -459,9 +457,7 @@ class FeedManagerImpl implements FeedManager, Client, EventListener {
String body = getPostBody(b.toString());
try {
// create and store post
Blog blog = blogManager.getBlog(txn, groupId);
AuthorId authorId = blog.getAuthor().getId();
LocalAuthor author = identityManager.getLocalAuthor(txn, authorId);
LocalAuthor author = identityManager.getLocalAuthor(txn);
BlogPost post = blogPostFactory
.createBlogPost(groupId, time, null, author, body);
blogManager.addLocalPost(txn, post);

View File

@@ -10,8 +10,7 @@ import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.identity.LocalAuthor;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Logger;
import javax.inject.Inject;
@@ -22,104 +21,63 @@ import static org.briarproject.api.identity.Author.Status.VERIFIED;
class IdentityManagerImpl implements IdentityManager {
private final DatabaseComponent db;
private final List<AddIdentityHook> addHooks;
private final List<RemoveIdentityHook> removeHooks;
private static final Logger LOG =
Logger.getLogger(IdentityManagerImpl.class.getName());
// The local author is immutable so we can cache it
private volatile LocalAuthor cachedAuthor;
@Inject
IdentityManagerImpl(DatabaseComponent db) {
this.db = db;
addHooks = new CopyOnWriteArrayList<AddIdentityHook>();
removeHooks = new CopyOnWriteArrayList<RemoveIdentityHook>();
}
@Override
public void registerAddIdentityHook(AddIdentityHook hook) {
addHooks.add(hook);
}
@Override
public void registerRemoveIdentityHook(RemoveIdentityHook hook) {
removeHooks.add(hook);
}
@Override
public void addLocalAuthor(LocalAuthor localAuthor) throws DbException {
public void registerLocalAuthor(LocalAuthor localAuthor) throws DbException {
Transaction txn = db.startTransaction(false);
try {
db.addLocalAuthor(txn, localAuthor);
for (AddIdentityHook hook : addHooks)
hook.addingIdentity(txn, localAuthor);
txn.setComplete();
cachedAuthor = localAuthor;
LOG.info("Local author registered");
} finally {
db.endTransaction(txn);
}
}
@Override
public LocalAuthor getLocalAuthor(AuthorId a) throws DbException {
LocalAuthor author;
Transaction txn = db.startTransaction(true);
try {
author = getLocalAuthor(txn, a);
txn.setComplete();
} finally {
db.endTransaction(txn);
}
return author;
}
@Override
public LocalAuthor getLocalAuthor(Transaction txn, AuthorId a)
throws DbException {
return db.getLocalAuthor(txn, a);
}
@Override
public LocalAuthor getLocalAuthor() throws DbException {
return getLocalAuthors().iterator().next();
if (cachedAuthor == null) {
Transaction txn = db.startTransaction(true);
try {
cachedAuthor = loadLocalAuthor(txn);
LOG.info("Local author loaded");
txn.setComplete();
} finally {
db.endTransaction(txn);
}
}
return cachedAuthor;
}
@Override
public LocalAuthor getLocalAuthor(Transaction txn) throws DbException {
return getLocalAuthors(txn).iterator().next();
}
@Override
public Collection<LocalAuthor> getLocalAuthors() throws DbException {
Collection<LocalAuthor> authors;
Transaction txn = db.startTransaction(true);
try {
authors = getLocalAuthors(txn);
txn.setComplete();
} finally {
db.endTransaction(txn);
if (cachedAuthor == null) {
cachedAuthor = loadLocalAuthor(txn);
LOG.info("Local author loaded");
}
return authors;
return cachedAuthor;
}
private Collection<LocalAuthor> getLocalAuthors(Transaction txn)
throws DbException {
return db.getLocalAuthors(txn);
}
@Override
public void removeLocalAuthor(AuthorId a) throws DbException {
Transaction txn = db.startTransaction(false);
try {
LocalAuthor localAuthor = db.getLocalAuthor(txn, a);
for (RemoveIdentityHook hook : removeHooks)
hook.removingIdentity(txn, localAuthor);
db.removeLocalAuthor(txn, a);
txn.setComplete();
} finally {
db.endTransaction(txn);
}
private LocalAuthor loadLocalAuthor(Transaction txn) throws DbException{
return db.getLocalAuthors(txn).iterator().next();
}
@Override
public Status getAuthorStatus(AuthorId authorId) throws DbException {
Transaction txn = db.startTransaction(false);
Transaction txn = db.startTransaction(true);
try {
return getAuthorStatus(txn, authorId);
} finally {
@@ -130,12 +88,7 @@ class IdentityManagerImpl implements IdentityManager {
@Override
public Status getAuthorStatus(Transaction txn, AuthorId authorId)
throws DbException {
// Compare to the IDs of the user's identities
for (LocalAuthor a : db.getLocalAuthors(txn)) {
if (a.getId().equals(authorId)) return OURSELVES;
}
if (getLocalAuthor(txn).getId().equals(authorId)) return OURSELVES;
Collection<Contact> contacts = db.getContactsByAuthorId(txn, authorId);
if (contacts.isEmpty()) return UNKNOWN;
for (Contact c : contacts) {

View File

@@ -31,7 +31,7 @@ public class IdentityModule {
@Provides
@Singleton
IdentityManager provideIdendityModule(DatabaseComponent db) {
IdentityManager provideIdentityModule(DatabaseComponent db) {
return new IdentityManagerImpl(db);
}

View File

@@ -305,10 +305,7 @@ class IntroduceeManager {
boolean alice = comp < 0;
// get our local author
AuthorId localAuthorId =
new AuthorId(localState.getRaw(LOCAL_AUTHOR_ID));
LocalAuthor author =
identityManager.getLocalAuthor(txn, localAuthorId);
LocalAuthor author = identityManager.getLocalAuthor(txn);
SecretKey secretKey;
byte[] privateKeyBytes = localState.getRaw(OUR_PRIVATE_KEY);
@@ -336,7 +333,7 @@ class IntroduceeManager {
.createAuthor(localState.getString(NAME),
localState.getRaw(PUBLIC_KEY));
ContactId contactId = contactManager
.addContact(txn, remoteAuthor, localAuthorId, secretKey,
.addContact(txn, remoteAuthor, author.getId(), secretKey,
timestamp, alice, false, false);
// Update local state with ContactId, so we know what to activate

View File

@@ -2,26 +2,19 @@ package org.briarproject.invitation;
import org.briarproject.api.contact.ContactExchangeListener;
import org.briarproject.api.contact.ContactExchangeTask;
import org.briarproject.api.contact.ContactManager;
import org.briarproject.api.crypto.CryptoComponent;
import org.briarproject.api.crypto.PseudoRandom;
import org.briarproject.api.data.BdfReaderFactory;
import org.briarproject.api.data.BdfWriterFactory;
import org.briarproject.api.db.DbException;
import org.briarproject.api.identity.Author;
import org.briarproject.api.identity.AuthorFactory;
import org.briarproject.api.identity.AuthorId;
import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.identity.LocalAuthor;
import org.briarproject.api.invitation.InvitationListener;
import org.briarproject.api.invitation.InvitationState;
import org.briarproject.api.invitation.InvitationTask;
import org.briarproject.api.plugins.ConnectionManager;
import org.briarproject.api.plugins.PluginManager;
import org.briarproject.api.plugins.duplex.DuplexPlugin;
import org.briarproject.api.system.Clock;
import org.briarproject.api.transport.StreamReaderFactory;
import org.briarproject.api.transport.StreamWriterFactory;
import java.util.ArrayList;
import java.util.Collection;
@@ -51,7 +44,6 @@ class ConnectorGroup extends Thread implements InvitationTask,
private final ContactExchangeTask contactExchangeTask;
private final IdentityManager identityManager;
private final PluginManager pluginManager;
private final AuthorId localAuthorId;
private final int localInvitationCode, remoteInvitationCode;
private final Collection<InvitationListener> listeners;
private final AtomicBoolean connected;
@@ -66,11 +58,9 @@ class ConnectorGroup extends Thread implements InvitationTask,
private String remoteName = null;
ConnectorGroup(CryptoComponent crypto, BdfReaderFactory bdfReaderFactory,
BdfWriterFactory bdfWriterFactory,
ContactExchangeTask contactExchangeTask,
BdfWriterFactory bdfWriterFactory, ContactExchangeTask contactExchangeTask,
IdentityManager identityManager, PluginManager pluginManager,
AuthorId localAuthorId, int localInvitationCode,
int remoteInvitationCode) {
int localInvitationCode, int remoteInvitationCode) {
super("ConnectorGroup");
this.crypto = crypto;
this.bdfReaderFactory = bdfReaderFactory;
@@ -78,7 +68,6 @@ class ConnectorGroup extends Thread implements InvitationTask,
this.contactExchangeTask = contactExchangeTask;
this.identityManager = identityManager;
this.pluginManager = pluginManager;
this.localAuthorId = localAuthorId;
this.localInvitationCode = localInvitationCode;
this.remoteInvitationCode = remoteInvitationCode;
listeners = new CopyOnWriteArrayList<InvitationListener>();
@@ -113,7 +102,7 @@ class ConnectorGroup extends Thread implements InvitationTask,
LocalAuthor localAuthor;
// Load the local pseudonym
try {
localAuthor = identityManager.getLocalAuthor(localAuthorId);
localAuthor = identityManager.getLocalAuthor();
} catch (DbException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
lock.lock();

View File

@@ -4,7 +4,6 @@ import org.briarproject.api.contact.ContactExchangeTask;
import org.briarproject.api.crypto.CryptoComponent;
import org.briarproject.api.data.BdfReaderFactory;
import org.briarproject.api.data.BdfWriterFactory;
import org.briarproject.api.identity.AuthorId;
import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.invitation.InvitationTask;
import org.briarproject.api.invitation.InvitationTaskFactory;
@@ -35,10 +34,9 @@ class InvitationTaskFactoryImpl implements InvitationTaskFactory {
this.pluginManager = pluginManager;
}
public InvitationTask createTask(AuthorId localAuthorId, int localCode,
int remoteCode) {
public InvitationTask createTask(int localCode, int remoteCode) {
return new ConnectorGroup(crypto, bdfReaderFactory, bdfWriterFactory,
contactExchangeTask, identityManager, pluginManager,
localAuthorId, localCode, remoteCode);
localCode, remoteCode);
}
}

View File

@@ -1,11 +1,16 @@
package org.briarproject.lifecycle;
import org.briarproject.api.clients.Client;
import org.briarproject.api.crypto.CryptoComponent;
import org.briarproject.api.crypto.KeyPair;
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.identity.AuthorFactory;
import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.identity.LocalAuthor;
import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.lifecycle.Service;
import org.briarproject.api.lifecycle.ServiceException;
@@ -17,6 +22,7 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Semaphore;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.inject.Inject;
import static java.util.logging.Level.INFO;
@@ -36,15 +42,23 @@ class LifecycleManagerImpl implements LifecycleManager {
private final List<Service> services;
private final List<Client> clients;
private final List<ExecutorService> executors;
private final CryptoComponent crypto;
private final AuthorFactory authorFactory;
private final IdentityManager identityManager;
private final Semaphore startStopSemaphore = new Semaphore(1);
private final CountDownLatch dbLatch = new CountDownLatch(1);
private final CountDownLatch startupLatch = new CountDownLatch(1);
private final CountDownLatch shutdownLatch = new CountDownLatch(1);
@Inject
LifecycleManagerImpl(DatabaseComponent db, EventBus eventBus) {
LifecycleManagerImpl(DatabaseComponent db, EventBus eventBus,
CryptoComponent crypto, AuthorFactory authorFactory,
IdentityManager identityManager) {
this.db = db;
this.eventBus = eventBus;
this.crypto = crypto;
this.authorFactory = authorFactory;
this.identityManager = identityManager;
services = new CopyOnWriteArrayList<Service>();
clients = new CopyOnWriteArrayList<Client>();
executors = new CopyOnWriteArrayList<ExecutorService>();
@@ -70,8 +84,29 @@ class LifecycleManagerImpl implements LifecycleManager {
executors.add(e);
}
private LocalAuthor createLocalAuthor(final String nickname) {
long now = System.currentTimeMillis();
KeyPair keyPair = crypto.generateSignatureKeyPair();
byte[] publicKey = keyPair.getPublic().getEncoded();
byte[] privateKey = keyPair.getPrivate().getEncoded();
LocalAuthor localAuthor = authorFactory
.createLocalAuthor(nickname, publicKey, privateKey);
long duration = System.currentTimeMillis() - now;
if (LOG.isLoggable(INFO))
LOG.info("Creating local author took " + duration + " ms");
return localAuthor;
}
private void registerLocalAuthor(LocalAuthor author) throws DbException {
long now = System.currentTimeMillis();
identityManager.registerLocalAuthor(author);
long duration = System.currentTimeMillis() - now;
if (LOG.isLoggable(INFO))
LOG.info("Registering local author took " + duration + " ms");
}
@Override
public StartResult startServices() {
public StartResult startServices(@Nullable String nickname) {
if (!startStopSemaphore.tryAcquire()) {
LOG.info("Already starting or stopping");
return ALREADY_RUNNING;
@@ -79,6 +114,7 @@ class LifecycleManagerImpl implements LifecycleManager {
try {
LOG.info("Starting services");
long start = System.currentTimeMillis();
boolean reopened = db.open();
long duration = System.currentTimeMillis() - start;
if (LOG.isLoggable(INFO)) {
@@ -86,6 +122,11 @@ class LifecycleManagerImpl implements LifecycleManager {
LOG.info("Reopening database took " + duration + " ms");
else LOG.info("Creating database took " + duration + " ms");
}
if (nickname != null) {
registerLocalAuthor(createLocalAuthor(nickname));
}
dbLatch.countDown();
Transaction txn = db.startTransaction(false);
try {
@@ -181,4 +222,5 @@ class LifecycleManagerImpl implements LifecycleManager {
public void waitForShutdown() throws InterruptedException {
shutdownLatch.await();
}
}

View File

@@ -1,7 +1,10 @@
package org.briarproject.lifecycle;
import org.briarproject.api.crypto.CryptoComponent;
import org.briarproject.api.db.DatabaseComponent;
import org.briarproject.api.event.EventBus;
import org.briarproject.api.identity.AuthorFactory;
import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.lifecycle.IoExecutor;
import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.lifecycle.ShutdownManager;
@@ -26,7 +29,8 @@ public class LifecycleModule {
public static class EagerSingletons {
@Inject
@IoExecutor Executor executor;
@IoExecutor
Executor executor;
}
private final ExecutorService ioExecutor;
@@ -51,8 +55,10 @@ public class LifecycleModule {
@Provides
@Singleton
LifecycleManager provideLifecycleManager(DatabaseComponent db,
EventBus eventBus) {
return new LifecycleManagerImpl(db, eventBus);
EventBus eventBus, CryptoComponent crypto,
AuthorFactory authorFactory, IdentityManager identityManager) {
return new LifecycleManagerImpl(db, eventBus, crypto, authorFactory,
identityManager);
}
@Provides