Add Message Dependencies to Database

This adds a new table to the database to hold message dependencies.
It introduces two more message states: pending and delivered
The valid column in the database was renamed to state to better reflect
its new extended meaning.

The DatabaseComponent was extended with three methods for:
* adding dependencies
* getting dependencies of a message
* getting messages that depend on a message (dependents)
* getting messages to be delivered (by startup hook)
* getting pending messages to be possibly delivered (by startup hook)

In order to reflect the new states, things that were previously true for
VALID messages have been changed to now be true for DELIVERED messages.

Since pending messages should not be available to clients, many database
queries have been modified to only return results for delivered
messages.

All added methods and changes should come with updated unit tests.

Please note that the database version was bumped in this commit.
This commit is contained in:
Torsten Grote
2016-05-18 18:24:03 -03:00
parent 512940e82b
commit b03d0a206b
18 changed files with 924 additions and 185 deletions

View File

@@ -18,7 +18,7 @@ import org.briarproject.api.event.Event;
import org.briarproject.api.event.EventListener;
import org.briarproject.api.event.ForumInvitationReceivedEvent;
import org.briarproject.api.event.ForumInvitationResponseReceivedEvent;
import org.briarproject.api.event.MessageValidatedEvent;
import org.briarproject.api.event.MessageStateChangedEvent;
import org.briarproject.api.forum.Forum;
import org.briarproject.api.forum.ForumInvitationMessage;
import org.briarproject.api.forum.ForumManager;
@@ -60,6 +60,7 @@ import static org.briarproject.TestPluginsModule.MAX_LATENCY;
import static org.briarproject.api.forum.ForumConstants.FORUM_SALT_LENGTH;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_INVITATION;
import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
import static org.briarproject.api.sync.ValidationManager.State.DELIVERED;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -669,14 +670,13 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
public volatile boolean responseReceived = false;
public void eventOccurred(Event e) {
if (e instanceof MessageValidatedEvent) {
MessageValidatedEvent event = (MessageValidatedEvent) e;
if (event.getClientId()
if (e instanceof MessageStateChangedEvent) {
MessageStateChangedEvent event = (MessageStateChangedEvent) e;
if (event.getState() == DELIVERED && event.getClientId()
.equals(forumSharingManager0.getClientId()) &&
!event.isLocal()) {
LOG.info("TEST: Sharer received message in group " +
((MessageValidatedEvent) e).getMessage()
.getGroupId().hashCode());
event.getMessage().getGroupId().hashCode());
msgWaiter.resume();
}
} else if (e instanceof ForumInvitationResponseReceivedEvent) {
@@ -722,14 +722,13 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
}
public void eventOccurred(Event e) {
if (e instanceof MessageValidatedEvent) {
MessageValidatedEvent event = (MessageValidatedEvent) e;
if (event.getClientId()
if (e instanceof MessageStateChangedEvent) {
MessageStateChangedEvent event = (MessageStateChangedEvent) e;
if (event.getState() == DELIVERED && event.getClientId()
.equals(forumSharingManager1.getClientId()) &&
!event.isLocal()) {
LOG.info("TEST: Invitee received message in group " +
((MessageValidatedEvent) e).getMessage()
.getGroupId().hashCode());
event.getMessage().getGroupId().hashCode());
msgWaiter.resume();
}
} else if (e instanceof ForumInvitationReceivedEvent) {

View File

@@ -19,7 +19,7 @@ import org.briarproject.api.event.IntroductionAbortedEvent;
import org.briarproject.api.event.IntroductionRequestReceivedEvent;
import org.briarproject.api.event.IntroductionResponseReceivedEvent;
import org.briarproject.api.event.IntroductionSucceededEvent;
import org.briarproject.api.event.MessageValidatedEvent;
import org.briarproject.api.event.MessageStateChangedEvent;
import org.briarproject.api.identity.AuthorFactory;
import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.identity.LocalAuthor;
@@ -70,6 +70,7 @@ import static org.briarproject.api.introduction.IntroductionConstants.PUBLIC_KEY
import static org.briarproject.api.introduction.IntroductionConstants.SESSION_ID;
import static org.briarproject.api.introduction.IntroductionConstants.TYPE;
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_REQUEST;
import static org.briarproject.api.sync.ValidationManager.State.DELIVERED;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -1047,15 +1048,14 @@ public class IntroductionIntegrationTest extends BriarTestCase {
@Override
public void eventOccurred(Event e) {
if (e instanceof MessageValidatedEvent) {
MessageValidatedEvent event = (MessageValidatedEvent) e;
if (event.getClientId()
if (e instanceof MessageStateChangedEvent) {
MessageStateChangedEvent event = (MessageStateChangedEvent) e;
if (event.getState() == DELIVERED && event.getClientId()
.equals(introductionManager0.getClientId()) &&
!event.isLocal()) {
LOG.info("TEST: Introducee" + introducee +
" received message in group " +
((MessageValidatedEvent) e).getMessage()
.getGroupId().hashCode());
event.getMessage().getGroupId().hashCode());
msgWaiter.resume();
}
} else if (e instanceof IntroductionRequestReceivedEvent) {
@@ -1114,14 +1114,13 @@ public class IntroductionIntegrationTest extends BriarTestCase {
@Override
public void eventOccurred(Event e) {
if (e instanceof MessageValidatedEvent) {
MessageValidatedEvent event = (MessageValidatedEvent) e;
if (event.getClientId()
if (e instanceof MessageStateChangedEvent) {
MessageStateChangedEvent event = (MessageStateChangedEvent) e;
if (event.getState() == DELIVERED && event.getClientId()
.equals(introductionManager0.getClientId()) &&
!event.isLocal()) {
LOG.info("TEST: Introducer received message in group " +
((MessageValidatedEvent) e).getMessage()
.getGroupId().hashCode());
event.getMessage().getGroupId().hashCode());
msgWaiter.resume();
}
} else if (e instanceof IntroductionResponseReceivedEvent) {

View File

@@ -24,7 +24,7 @@ import org.briarproject.api.event.ForumInvitationReceivedEvent;
import org.briarproject.api.event.IntroductionRequestReceivedEvent;
import org.briarproject.api.event.IntroductionResponseReceivedEvent;
import org.briarproject.api.event.IntroductionSucceededEvent;
import org.briarproject.api.event.MessageValidatedEvent;
import org.briarproject.api.event.MessageStateChangedEvent;
import org.briarproject.api.event.SettingsUpdatedEvent;
import org.briarproject.api.forum.ForumManager;
import org.briarproject.api.lifecycle.Service;
@@ -59,6 +59,7 @@ import static android.support.v4.app.NotificationCompat.VISIBILITY_SECRET;
import static java.util.logging.Level.WARNING;
import static org.briarproject.android.BriarActivity.GROUP_ID;
import static org.briarproject.android.fragment.SettingsFragment.SETTINGS_NAMESPACE;
import static org.briarproject.api.sync.ValidationManager.State.DELIVERED;
class AndroidNotificationManagerImpl implements AndroidNotificationManager,
Service, EventListener {
@@ -156,9 +157,9 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
if (e instanceof SettingsUpdatedEvent) {
SettingsUpdatedEvent s = (SettingsUpdatedEvent) e;
if (s.getNamespace().equals(SETTINGS_NAMESPACE)) loadSettings();
} else if (e instanceof MessageValidatedEvent) {
MessageValidatedEvent m = (MessageValidatedEvent) e;
if (m.isValid() && !m.isLocal()) {
} else if (e instanceof MessageStateChangedEvent) {
MessageStateChangedEvent m = (MessageStateChangedEvent) e;
if (!m.isLocal() && m.getState() == DELIVERED) {
ClientId c = m.getClientId();
if (c.equals(messagingManager.getClientId()))
showPrivateMessageNotification(m.getMessage().getGroupId());

View File

@@ -30,7 +30,7 @@ import org.briarproject.api.event.ContactStatusChangedEvent;
import org.briarproject.api.event.Event;
import org.briarproject.api.event.EventBus;
import org.briarproject.api.event.EventListener;
import org.briarproject.api.event.MessageValidatedEvent;
import org.briarproject.api.event.MessageStateChangedEvent;
import org.briarproject.api.forum.ForumInvitationMessage;
import org.briarproject.api.forum.ForumSharingManager;
import org.briarproject.api.identity.IdentityManager;
@@ -54,6 +54,7 @@ import static android.support.v4.app.ActivityOptionsCompat.makeSceneTransitionAn
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static org.briarproject.android.BriarActivity.GROUP_ID;
import static org.briarproject.api.sync.ValidationManager.State.DELIVERED;
public class ContactListFragment extends BaseFragment implements EventListener {
@@ -230,12 +231,13 @@ public class ContactListFragment extends BaseFragment implements EventListener {
} else if (e instanceof ContactRemovedEvent) {
LOG.info("Contact removed");
removeItem(((ContactRemovedEvent) e).getContactId());
} else if (e instanceof MessageValidatedEvent) {
MessageValidatedEvent m = (MessageValidatedEvent) e;
} else if (e instanceof MessageStateChangedEvent) {
MessageStateChangedEvent m = (MessageStateChangedEvent) e;
ClientId c = m.getClientId();
if (m.isValid() && (c.equals(messagingManager.getClientId()) ||
c.equals(introductionManager.getClientId()) ||
c.equals(forumSharingManager.getClientId()))) {
if (m.getState() == DELIVERED &&
(c.equals(messagingManager.getClientId()) ||
c.equals(introductionManager.getClientId()) ||
c.equals(forumSharingManager.getClientId()))) {
LOG.info("Message added, reloading");
reloadConversation(m.getMessage().getGroupId());
}

View File

@@ -47,7 +47,7 @@ import org.briarproject.api.event.EventListener;
import org.briarproject.api.event.ForumInvitationReceivedEvent;
import org.briarproject.api.event.IntroductionRequestReceivedEvent;
import org.briarproject.api.event.IntroductionResponseReceivedEvent;
import org.briarproject.api.event.MessageValidatedEvent;
import org.briarproject.api.event.MessageStateChangedEvent;
import org.briarproject.api.event.MessagesAckedEvent;
import org.briarproject.api.event.MessagesSentEvent;
import org.briarproject.api.forum.ForumInvitationMessage;
@@ -87,6 +87,7 @@ import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static org.briarproject.android.contact.ConversationItem.IncomingItem;
import static org.briarproject.android.contact.ConversationItem.OutgoingItem;
import static org.briarproject.api.sync.ValidationManager.State.DELIVERED;
public class ConversationActivity extends BriarActivity
implements EventListener, OnClickListener,
@@ -468,9 +469,10 @@ public class ConversationActivity extends BriarActivity
LOG.info("Contact removed");
finishOnUiThread();
}
} else if (e instanceof MessageValidatedEvent) {
MessageValidatedEvent m = (MessageValidatedEvent) e;
if (m.isValid() && m.getMessage().getGroupId().equals(groupId)) {
} else if (e instanceof MessageStateChangedEvent) {
MessageStateChangedEvent m = (MessageStateChangedEvent) e;
if (m.getState() == DELIVERED &&
m.getMessage().getGroupId().equals(groupId)) {
LOG.info("Message added, reloading");
// Mark new incoming messages as read directly
if (m.isLocal()) loadMessages();

View File

@@ -30,7 +30,7 @@ import org.briarproject.api.event.Event;
import org.briarproject.api.event.EventBus;
import org.briarproject.api.event.EventListener;
import org.briarproject.api.event.GroupRemovedEvent;
import org.briarproject.api.event.MessageValidatedEvent;
import org.briarproject.api.event.MessageStateChangedEvent;
import org.briarproject.api.forum.Forum;
import org.briarproject.api.forum.ForumManager;
import org.briarproject.api.forum.ForumPostHeader;
@@ -62,6 +62,7 @@ import static java.util.logging.Level.WARNING;
import static org.briarproject.android.forum.ReadForumPostActivity.RESULT_PREV_NEXT;
import static org.briarproject.android.util.CommonLayoutParams.MATCH_MATCH;
import static org.briarproject.android.util.CommonLayoutParams.MATCH_WRAP_1;
import static org.briarproject.api.sync.ValidationManager.State.DELIVERED;
public class ForumActivity extends BriarActivity implements EventListener,
OnItemClickListener {
@@ -356,9 +357,10 @@ public class ForumActivity extends BriarActivity implements EventListener,
}
public void eventOccurred(Event e) {
if (e instanceof MessageValidatedEvent) {
MessageValidatedEvent m = (MessageValidatedEvent) e;
if (m.isValid() && m.getMessage().getGroupId().equals(groupId)) {
if (e instanceof MessageStateChangedEvent) {
MessageStateChangedEvent m = (MessageStateChangedEvent) e;
if (m.getState() == DELIVERED &&
m.getMessage().getGroupId().equals(groupId)) {
LOG.info("Message added, reloading");
loadHeaders();
}

View File

@@ -25,7 +25,7 @@ import org.briarproject.api.event.Event;
import org.briarproject.api.event.ForumInvitationReceivedEvent;
import org.briarproject.api.event.GroupAddedEvent;
import org.briarproject.api.event.GroupRemovedEvent;
import org.briarproject.api.event.MessageValidatedEvent;
import org.briarproject.api.event.MessageStateChangedEvent;
import org.briarproject.api.forum.Forum;
import org.briarproject.api.forum.ForumManager;
import org.briarproject.api.forum.ForumPostHeader;
@@ -42,6 +42,7 @@ import javax.inject.Inject;
import static android.support.design.widget.Snackbar.LENGTH_INDEFINITE;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static org.briarproject.api.sync.ValidationManager.State.DELIVERED;
public class ForumListFragment extends BaseEventFragment implements
View.OnClickListener {
@@ -224,14 +225,13 @@ public class ForumListFragment extends BaseEventFragment implements
LOG.info("Forum removed, removing from list");
removeForum(g.getGroup().getId());
}
} else if (e instanceof MessageValidatedEvent) {
MessageValidatedEvent m = (MessageValidatedEvent) e;
if (m.isValid()) {
ClientId c = m.getClientId();
if (c.equals(forumManager.getClientId())) {
LOG.info("Forum post added, reloading");
loadForumHeaders(m.getMessage().getGroupId());
}
} else if (e instanceof MessageStateChangedEvent) {
MessageStateChangedEvent m = (MessageStateChangedEvent) e;
ClientId c = m.getClientId();
if (m.getState() == DELIVERED &&
c.equals(forumManager.getClientId())) {
LOG.info("Forum post added, reloading");
loadForumHeaders(m.getMessage().getGroupId());
}
} else if (e instanceof ForumInvitationReceivedEvent) {
loadAvailableForums();

View File

@@ -22,6 +22,8 @@ import org.briarproject.api.transport.TransportKeys;
import java.util.Collection;
import java.util.Map;
import static org.briarproject.api.sync.ValidationManager.State;
/**
* Encapsulates the database implementation and exposes high-level operations
* to other components.
@@ -233,6 +235,24 @@ public interface DatabaseComponent {
Collection<MessageId> getMessagesToValidate(Transaction txn, ClientId c)
throws DbException;
/**
* Returns the IDs of any messages that need to be delivered to the given
* client.
* <p/>
* Read-only.
*/
Collection<MessageId> getMessagesToDeliver(Transaction txn, ClientId c)
throws DbException;
/**
* Returns the IDs of any messages that are still pending due to
* dependencies to other messages for the given client.
* <p/>
* Read-only.
*/
Collection<MessageId> getPendingMessages(Transaction txn, ClientId c)
throws DbException;
/**
* Returns the message with the given ID, in serialised form, or null if
* the message has been deleted.
@@ -276,6 +296,22 @@ public interface DatabaseComponent {
Collection<MessageStatus> getMessageStatus(Transaction txn, ContactId c,
GroupId g) throws DbException;
/**
* Returns the dependencies of the given message.
* <p/>
* Read-only.
*/
Map<MessageId, State> getMessageDependencies(Transaction txn, MessageId m)
throws DbException;
/**
* Returns all IDs of messages that depend on the given message.
* <p/>
* Read-only.
*/
Map<MessageId, State> getMessageDependents(Transaction txn, MessageId m)
throws DbException;
/**
* Returns the status of the given message with respect to the given
* contact.
@@ -391,11 +427,17 @@ public interface DatabaseComponent {
throws DbException;
/**
* Marks the given message as valid or invalid.
* Sets the state of the message with respect to validation and delivery.
*/
void setMessageValid(Transaction txn, Message m, ClientId c, boolean valid)
void setMessageState(Transaction txn, Message m, ClientId c, State valid)
throws DbException;
/**
* Adds dependencies for a message
*/
void addMessageDependencies(Transaction txn, Message dependent,
Collection<MessageId> dependencies) throws DbException;
/**
* Sets the reordering window for the given contact and transport in the
* given rotation period.

View File

@@ -0,0 +1,42 @@
package org.briarproject.api.event;
import org.briarproject.api.sync.ClientId;
import org.briarproject.api.sync.Message;
import org.briarproject.api.sync.ValidationManager;
import static org.briarproject.api.sync.ValidationManager.State;
/**
* An event that is broadcast when a message state changed.
*/
public class MessageStateChangedEvent extends Event {
private final Message message;
private final ClientId clientId;
private final boolean local;
private final State state;
public MessageStateChangedEvent(Message message, ClientId clientId,
boolean local, State state) {
this.message = message;
this.clientId = clientId;
this.local = local;
this.state = state;
}
public Message getMessage() {
return message;
}
public ClientId getClientId() {
return clientId;
}
public boolean isLocal() {
return local;
}
public State getState() {
return state;
}
}

View File

@@ -1,39 +0,0 @@
package org.briarproject.api.event;
import org.briarproject.api.db.Metadata;
import org.briarproject.api.sync.ClientId;
import org.briarproject.api.sync.Message;
/**
* An event that is broadcast when a message has passed or failed validation.
*/
public class MessageValidatedEvent extends Event {
private final Message message;
private final ClientId clientId;
private final boolean local, valid;
public MessageValidatedEvent(Message message, ClientId clientId,
boolean local, boolean valid) {
this.message = message;
this.clientId = clientId;
this.local = local;
this.valid = valid;
}
public Message getMessage() {
return message;
}
public ClientId getClientId() {
return clientId;
}
public boolean isLocal() {
return local;
}
public boolean isValid() {
return valid;
}
}

View File

@@ -10,13 +10,13 @@ import org.briarproject.api.db.Transaction;
*/
public interface ValidationManager {
enum Validity {
enum State {
UNKNOWN(0), INVALID(1), VALID(2);
UNKNOWN(0), INVALID(1), PENDING(2), VALID(3), DELIVERED(4);
private final int value;
Validity(int value) {
State(int value) {
this.value = value;
}
@@ -24,8 +24,8 @@ public interface ValidationManager {
return value;
}
public static Validity fromValue(int value) {
for (Validity s : values()) if (s.value == value) return s;
public static State fromValue(int value) {
for (State s : values()) if (s.value == value) return s;
throw new IllegalArgumentException();
}
}

View File

@@ -16,7 +16,7 @@ import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.Message;
import org.briarproject.api.sync.MessageId;
import org.briarproject.api.sync.MessageStatus;
import org.briarproject.api.sync.ValidationManager.Validity;
import org.briarproject.api.sync.ValidationManager.State;
import org.briarproject.api.transport.TransportKeys;
import java.util.Collection;
@@ -79,7 +79,7 @@ interface Database<T> {
/**
* Stores a message.
*/
void addMessage(T txn, Message m, Validity validity, boolean shared)
void addMessage(T txn, Message m, State state, boolean shared)
throws DbException;
/**
@@ -326,6 +326,24 @@ interface Database<T> {
MessageStatus getMessageStatus(T txn, ContactId c, MessageId m)
throws DbException;
/**
* Returns the dependencies of the given message.
* This method makes sure that dependencies in different groups
* are returned as {@link ValidationManager.State.INVALID}.
* <p/>
* Read-only.
*/
Map<MessageId, State> getMessageDependencies(T txn, MessageId m)
throws DbException;
/**
* Returns all IDs of messages that depend on the given message.
* <p/>
* Read-only.
*/
Map<MessageId, State> getMessageDependents(T txn, MessageId m)
throws DbException;
/**
* Returns the IDs of some messages received from the given contact that
* need to be acknowledged, up to the given number of messages.
@@ -371,6 +389,24 @@ interface Database<T> {
Collection<MessageId> getMessagesToValidate(T txn, ClientId c)
throws DbException;
/**
* Returns the IDs of any messages that need to be delivered to the given
* client.
* <p/>
* Read-only.
*/
Collection<MessageId> getMessagesToDeliver(T txn, ClientId c)
throws DbException;
/**
* Returns the IDs of any messages that are still pending due to
* dependencies to other messages for the given client.
* <p/>
* Read-only.
*/
Collection<MessageId> getPendingMessages(T txn, ClientId c)
throws DbException;
/**
* Returns the message with the given ID, in serialised form, or null if
* the message has been deleted.
@@ -538,7 +574,14 @@ interface Database<T> {
/**
* Marks the given message as valid or invalid.
*/
void setMessageValid(T txn, MessageId m, boolean valid) throws DbException;
void setMessageState(T txn, MessageId m, State state)
throws DbException;
/*
* Adds a dependency between two MessageIds
*/
void addMessageDependency(T txn, MessageId dependentId,
MessageId dependencyId) throws DbException;
/**
* Sets the reordering window for the given contact and transport in the

View File

@@ -29,8 +29,8 @@ import org.briarproject.api.event.MessageRequestedEvent;
import org.briarproject.api.event.MessageSharedEvent;
import org.briarproject.api.event.MessageToAckEvent;
import org.briarproject.api.event.MessageToRequestEvent;
import org.briarproject.api.event.MessageValidatedEvent;
import org.briarproject.api.event.MessagesAckedEvent;
import org.briarproject.api.event.MessageStateChangedEvent;
import org.briarproject.api.event.MessagesSentEvent;
import org.briarproject.api.event.SettingsUpdatedEvent;
import org.briarproject.api.identity.Author;
@@ -47,7 +47,7 @@ import org.briarproject.api.sync.MessageId;
import org.briarproject.api.sync.MessageStatus;
import org.briarproject.api.sync.Offer;
import org.briarproject.api.sync.Request;
import org.briarproject.api.sync.ValidationManager.Validity;
import org.briarproject.api.sync.ValidationManager.State;
import org.briarproject.api.transport.TransportKeys;
import java.util.ArrayList;
@@ -64,8 +64,8 @@ import java.util.logging.Logger;
import javax.inject.Inject;
import static java.util.logging.Level.WARNING;
import static org.briarproject.api.sync.ValidationManager.Validity.UNKNOWN;
import static org.briarproject.api.sync.ValidationManager.Validity.VALID;
import static org.briarproject.api.sync.ValidationManager.State.DELIVERED;
import static org.briarproject.api.sync.ValidationManager.State.UNKNOWN;
import static org.briarproject.db.DatabaseConstants.MAX_OFFERED_MESSAGES;
class DatabaseComponentImpl<T> implements DatabaseComponent {
@@ -193,17 +193,18 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
if (!db.containsGroup(txn, m.getGroupId()))
throw new NoSuchGroupException();
if (!db.containsMessage(txn, m.getId())) {
addMessage(txn, m, VALID, shared);
addMessage(txn, m, DELIVERED, shared);
transaction.attach(new MessageAddedEvent(m, null));
transaction.attach(new MessageValidatedEvent(m, c, true, true));
transaction.attach(new MessageStateChangedEvent(m, c, true,
DELIVERED));
if (shared) transaction.attach(new MessageSharedEvent(m));
}
db.mergeMessageMetadata(txn, m.getId(), meta);
}
private void addMessage(T txn, Message m, Validity validity, boolean shared)
private void addMessage(T txn, Message m, State state, boolean shared)
throws DbException {
db.addMessage(txn, m, validity, shared);
db.addMessage(txn, m, state, shared);
for (ContactId c : db.getVisibility(txn, m.getGroupId())) {
boolean offered = db.removeOfferedMessage(txn, c, m.getId());
db.addStatus(txn, c, m.getId(), offered, offered);
@@ -411,6 +412,18 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
return db.getMessagesToValidate(txn, c);
}
public Collection<MessageId> getMessagesToDeliver(Transaction transaction,
ClientId c) throws DbException {
T txn = unbox(transaction);
return db.getMessagesToDeliver(txn, c);
}
public Collection<MessageId> getPendingMessages(Transaction transaction,
ClientId c) throws DbException {
T txn = unbox(transaction);
return db.getPendingMessages(txn, c);
}
public byte[] getRawMessage(Transaction transaction, MessageId m)
throws DbException {
T txn = unbox(transaction);
@@ -463,6 +476,22 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
return db.getMessageStatus(txn, c, m);
}
public Map<MessageId, State> getMessageDependencies(Transaction transaction,
MessageId m) throws DbException {
T txn = unbox(transaction);
if (!db.containsMessage(txn, m))
throw new NoSuchMessageException();
return db.getMessageDependencies(txn, m);
}
public Map<MessageId, State> getMessageDependents(Transaction transaction,
MessageId m) throws DbException {
T txn = unbox(transaction);
if (!db.containsMessage(txn, m))
throw new NoSuchMessageException();
return db.getMessageDependents(txn, m);
}
public Settings getSettings(Transaction transaction, String namespace)
throws DbException {
T txn = unbox(transaction);
@@ -664,14 +693,26 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
if (shared) transaction.attach(new MessageSharedEvent(m));
}
public void setMessageValid(Transaction transaction, Message m, ClientId c,
boolean valid) throws DbException {
public void setMessageState(Transaction transaction, Message m, ClientId c,
State state) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsMessage(txn, m.getId()))
throw new NoSuchMessageException();
db.setMessageValid(txn, m.getId(), valid);
transaction.attach(new MessageValidatedEvent(m, c, false, valid));
db.setMessageState(txn, m.getId(), state);
transaction.attach(new MessageStateChangedEvent(m, c, false, state));
}
public void addMessageDependencies(Transaction transaction,
Message dependent, Collection<MessageId> dependencies)
throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsMessage(txn, dependent.getId()))
throw new NoSuchMessageException();
for (MessageId dependencyId : dependencies) {
db.addMessageDependency(txn, dependent.getId(), dependencyId);
}
}
public void setReorderingWindow(Transaction transaction, ContactId c,

View File

@@ -19,7 +19,7 @@ import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.Message;
import org.briarproject.api.sync.MessageId;
import org.briarproject.api.sync.MessageStatus;
import org.briarproject.api.sync.ValidationManager.Validity;
import org.briarproject.api.sync.ValidationManager.State;
import org.briarproject.api.system.Clock;
import org.briarproject.api.transport.IncomingKeys;
import org.briarproject.api.transport.OutgoingKeys;
@@ -49,9 +49,11 @@ import java.util.logging.Logger;
import static java.util.logging.Level.WARNING;
import static org.briarproject.api.db.Metadata.REMOVE;
import static org.briarproject.api.sync.ValidationManager.Validity.INVALID;
import static org.briarproject.api.sync.ValidationManager.Validity.UNKNOWN;
import static org.briarproject.api.sync.ValidationManager.Validity.VALID;
import static org.briarproject.api.sync.ValidationManager.State.DELIVERED;
import static org.briarproject.api.sync.ValidationManager.State.INVALID;
import static org.briarproject.api.sync.ValidationManager.State.PENDING;
import static org.briarproject.api.sync.ValidationManager.State.UNKNOWN;
import static org.briarproject.api.sync.ValidationManager.State.VALID;
import static org.briarproject.db.DatabaseConstants.DB_SETTINGS_NAMESPACE;
import static org.briarproject.db.DatabaseConstants.DEVICE_ID_KEY;
import static org.briarproject.db.DatabaseConstants.DEVICE_SETTINGS_NAMESPACE;
@@ -65,8 +67,8 @@ import static org.briarproject.db.ExponentialBackoff.calculateExpiry;
*/
abstract class JdbcDatabase implements Database<Connection> {
private static final int SCHEMA_VERSION = 24;
private static final int MIN_SCHEMA_VERSION = 24;
private static final int SCHEMA_VERSION = 25;
private static final int MIN_SCHEMA_VERSION = 25;
private static final String CREATE_SETTINGS =
"CREATE TABLE settings"
@@ -131,7 +133,7 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " (messageId HASH NOT NULL,"
+ " groupId HASH NOT NULL,"
+ " timestamp BIGINT NOT NULL,"
+ " valid INT NOT NULL,"
+ " state INT NOT NULL,"
+ " shared BOOLEAN NOT NULL,"
+ " length INT NOT NULL,"
+ " raw BLOB," // Null if message has been deleted
@@ -150,6 +152,14 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " REFERENCES messages (messageId)"
+ " ON DELETE CASCADE)";
private static final String CREATE_MESSAGE_DEPENDENCIES =
"CREATE TABLE messageDependencies"
+ " (messageId HASH NOT NULL,"
+ " dependencyId HASH NOT NULL," // Not a foreign key
+ " FOREIGN KEY (messageId)"
+ " REFERENCES messages (messageId)"
+ " ON DELETE CASCADE)";
private static final String CREATE_OFFERS =
"CREATE TABLE offers"
+ " (messageId HASH NOT NULL," // Not a foreign key
@@ -320,6 +330,7 @@ abstract class JdbcDatabase implements Database<Connection> {
s.executeUpdate(insertTypeNames(CREATE_GROUP_VISIBILITIES));
s.executeUpdate(insertTypeNames(CREATE_MESSAGES));
s.executeUpdate(insertTypeNames(CREATE_MESSAGE_METADATA));
s.executeUpdate(insertTypeNames(CREATE_MESSAGE_DEPENDENCIES));
s.executeUpdate(insertTypeNames(CREATE_OFFERS));
s.executeUpdate(insertTypeNames(CREATE_STATUSES));
s.executeUpdate(insertTypeNames(CREATE_TRANSPORTS));
@@ -520,18 +531,18 @@ abstract class JdbcDatabase implements Database<Connection> {
}
}
public void addMessage(Connection txn, Message m, Validity validity,
public void addMessage(Connection txn, Message m, State state,
boolean shared) throws DbException {
PreparedStatement ps = null;
try {
String sql = "INSERT INTO messages (messageId, groupId, timestamp,"
+ " valid, shared, length, raw)"
+ " state, shared, length, raw)"
+ " VALUES (?, ?, ?, ?, ?, ?, ?)";
ps = txn.prepareStatement(sql);
ps.setBytes(1, m.getId().getBytes());
ps.setBytes(2, m.getGroupId().getBytes());
ps.setLong(3, m.getTimestamp());
ps.setInt(4, validity.getValue());
ps.setInt(4, state.getValue());
ps.setBoolean(5, shared);
byte[] raw = m.getRaw();
ps.setInt(6, raw.length);
@@ -596,6 +607,25 @@ abstract class JdbcDatabase implements Database<Connection> {
}
}
public void addMessageDependency(Connection txn, MessageId dependentId,
MessageId dependencyId) throws DbException {
PreparedStatement ps = null;
try {
String sql =
"INSERT INTO messageDependencies (messageId, dependencyId)"
+ " VALUES (?, ?)";
ps = txn.prepareStatement(sql);
ps.setBytes(1, dependentId.getBytes());
ps.setBytes(2, dependencyId.getBytes());
int affected = ps.executeUpdate();
if (affected != 1) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps);
throw new DbException(e);
}
}
public void addTransport(Connection txn, TransportId t, int maxLatency)
throws DbException {
PreparedStatement ps = null;
@@ -1135,10 +1165,33 @@ abstract class JdbcDatabase implements Database<Connection> {
}
}
private Collection<MessageId> getMessageIds(Connection txn, GroupId g,
State state) throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT messageId FROM messages"
+ " WHERE state = ? AND groupId = ?";
ps = txn.prepareStatement(sql);
ps.setInt(1, state.getValue());
ps.setBytes(2, g.getBytes());
rs = ps.executeQuery();
List<MessageId> ids = new ArrayList<MessageId>();
while (rs.next()) ids.add(new MessageId(rs.getBytes(1)));
rs.close();
ps.close();
return Collections.unmodifiableList(ids);
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
throw new DbException(e);
}
}
public Collection<MessageId> getMessageIds(Connection txn, GroupId g,
Metadata query) throws DbException {
// If there are no query terms, return all messages
if (query.isEmpty()) return getMessageIds(txn, g);
// If there are no query terms, return all delivered messages
if (query.isEmpty()) return getMessageIds(txn, g, DELIVERED);
PreparedStatement ps = null;
ResultSet rs = null;
try {
@@ -1148,12 +1201,14 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " FROM messages AS m"
+ " JOIN messageMetadata AS md"
+ " ON m.messageId = md.messageId"
+ " WHERE groupId = ? AND key = ? AND value = ?";
+ " WHERE state = ? AND groupId = ?"
+ " AND key = ? AND value = ?";
for (Entry<String, byte[]> e : query.entrySet()) {
ps = txn.prepareStatement(sql);
ps.setBytes(1, g.getBytes());
ps.setString(2, e.getKey());
ps.setBytes(3, e.getValue());
ps.setInt(1, DELIVERED.getValue());
ps.setBytes(2, g.getBytes());
ps.setString(3, e.getKey());
ps.setBytes(4, e.getValue());
rs = ps.executeQuery();
Set<MessageId> ids = new HashSet<MessageId>();
while (rs.next()) ids.add(new MessageId(rs.getBytes(1)));
@@ -1182,10 +1237,11 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " FROM messages AS m"
+ " JOIN messageMetadata AS md"
+ " ON m.messageId = md.messageId"
+ " WHERE groupId = ?"
+ " WHERE state = ? AND groupId = ?"
+ " ORDER BY m.messageId";
ps = txn.prepareStatement(sql);
ps.setBytes(1, g.getBytes());
ps.setInt(1, DELIVERED.getValue());
ps.setBytes(2, g.getBytes());
rs = ps.executeQuery();
Map<MessageId, Metadata> all = new HashMap<MessageId, Metadata>();
Metadata metadata = null;
@@ -1223,23 +1279,38 @@ abstract class JdbcDatabase implements Database<Connection> {
public Metadata getGroupMetadata(Connection txn, GroupId g)
throws DbException {
return getMetadata(txn, g.getBytes(), "groupMetadata", "groupId");
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT key, value FROM groupMetadata"
+ " WHERE groupId = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, g.getBytes());
rs = ps.executeQuery();
Metadata metadata = new Metadata();
while (rs.next()) metadata.put(rs.getString(1), rs.getBytes(2));
rs.close();
ps.close();
return metadata;
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
throw new DbException(e);
}
}
public Metadata getMessageMetadata(Connection txn, MessageId m)
throws DbException {
return getMetadata(txn, m.getBytes(), "messageMetadata", "messageId");
}
private Metadata getMetadata(Connection txn, byte[] id, String tableName,
String columnName) throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT key, value FROM " + tableName
+ " WHERE " + columnName + " = ?";
String sql = "SELECT key, value FROM messageMetadata AS md"
+ " JOIN messages AS m"
+ " ON m.messageId = md.messageId"
+ " WHERE m.state = ? AND md.messageId = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, id);
ps.setInt(1, DELIVERED.getValue());
ps.setBytes(2, m.getBytes());
rs = ps.executeQuery();
Metadata metadata = new Metadata();
while (rs.next()) metadata.put(rs.getString(1), rs.getBytes(2));
@@ -1312,6 +1383,91 @@ abstract class JdbcDatabase implements Database<Connection> {
}
}
public Map<MessageId, State> getMessageDependencies(Connection txn,
MessageId m) throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT d.dependencyId, m.state, m.groupId"
+ " FROM messageDependencies AS d"
+ " LEFT OUTER JOIN messages AS m"
+ " ON d.dependencyId = m.messageId"
+ " WHERE d.messageId = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, m.getBytes());
rs = ps.executeQuery();
Map<MessageId, State> dependencies = new HashMap<MessageId, State>();
while (rs.next()) {
MessageId messageId = new MessageId(rs.getBytes(1));
State state = State.fromValue(rs.getInt(2));
if (state != UNKNOWN) {
// set dependency invalid if it is in a different group
if (!hasGroupId(txn, m, rs.getBytes(3))) state = INVALID;
}
dependencies.put(messageId, state);
}
rs.close();
ps.close();
return Collections.unmodifiableMap(dependencies);
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
throw new DbException(e);
}
}
private boolean hasGroupId(Connection txn, MessageId m, byte[] g)
throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT NULL FROM messages"
+ " WHERE messageId = ? AND groupId = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, m.getBytes());
ps.setBytes(2, g);
rs = ps.executeQuery();
boolean same = rs.next();
if (rs.next()) throw new DbStateException();
rs.close();
ps.close();
return same;
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
throw new DbException(e);
}
}
public Map<MessageId, State> getMessageDependents(Connection txn,
MessageId m) throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT d.messageId, m.state"
+ " FROM messageDependencies AS d"
+ " LEFT OUTER JOIN messages AS m"
+ " ON d.messageId = m.messageId"
+ " WHERE dependencyId = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, m.getBytes());
rs = ps.executeQuery();
Map<MessageId, State> dependents = new HashMap<MessageId, State>();
while (rs.next()) {
MessageId messageId = new MessageId(rs.getBytes(1));
State state = State.fromValue(rs.getInt(2));
dependents.put(messageId, state);
}
rs.close();
ps.close();
return Collections.unmodifiableMap(dependents);
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
throw new DbException(e);
}
}
public Collection<MessageId> getMessagesToAck(Connection txn, ContactId c,
int maxMessages) throws DbException {
PreparedStatement ps = null;
@@ -1346,13 +1502,13 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " JOIN statuses AS s"
+ " ON m.messageId = s.messageId"
+ " WHERE contactId = ?"
+ " AND valid = ? AND shared = TRUE AND raw IS NOT NULL"
+ " AND state = ? AND shared = TRUE AND raw IS NOT NULL"
+ " AND seen = FALSE AND requested = FALSE"
+ " AND expiry < ?"
+ " ORDER BY timestamp DESC LIMIT ?";
ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt());
ps.setInt(2, VALID.getValue());
ps.setInt(2, DELIVERED.getValue());
ps.setLong(3, now);
ps.setInt(4, maxMessages);
rs = ps.executeQuery();
@@ -1402,13 +1558,13 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " JOIN statuses AS s"
+ " ON m.messageId = s.messageId"
+ " WHERE contactId = ?"
+ " AND valid = ? AND shared = TRUE AND raw IS NOT NULL"
+ " AND state = ? AND shared = TRUE AND raw IS NOT NULL"
+ " AND seen = FALSE"
+ " AND expiry < ?"
+ " ORDER BY timestamp DESC";
ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt());
ps.setInt(2, VALID.getValue());
ps.setInt(2, DELIVERED.getValue());
ps.setLong(3, now);
rs = ps.executeQuery();
List<MessageId> ids = new ArrayList<MessageId>();
@@ -1431,14 +1587,29 @@ abstract class JdbcDatabase implements Database<Connection> {
public Collection<MessageId> getMessagesToValidate(Connection txn,
ClientId c) throws DbException {
return getMessagesInState(txn, c, UNKNOWN);
}
public Collection<MessageId> getMessagesToDeliver(Connection txn,
ClientId c) throws DbException {
return getMessagesInState(txn, c, VALID);
}
public Collection<MessageId> getPendingMessages(Connection txn,
ClientId c) throws DbException {
return getMessagesInState(txn, c, PENDING);
}
private Collection<MessageId> getMessagesInState(Connection txn, ClientId c,
State state) throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT messageId FROM messages AS m"
+ " JOIN groups AS g ON m.groupId = g.groupId"
+ " WHERE valid = ? AND clientId = ? AND raw IS NOT NULL";
+ " WHERE state = ? AND clientId = ? AND raw IS NOT NULL";
ps = txn.prepareStatement(sql);
ps.setInt(1, UNKNOWN.getValue());
ps.setInt(1, state.getValue());
ps.setBytes(2, c.getBytes());
rs = ps.executeQuery();
List<MessageId> ids = new ArrayList<MessageId>();
@@ -1485,13 +1656,13 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " JOIN statuses AS s"
+ " ON m.messageId = s.messageId"
+ " WHERE contactId = ?"
+ " AND valid = ? AND shared = TRUE AND raw IS NOT NULL"
+ " AND state = ? AND shared = TRUE AND raw IS NOT NULL"
+ " AND seen = FALSE AND requested = TRUE"
+ " AND expiry < ?"
+ " ORDER BY timestamp DESC";
ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt());
ps.setInt(2, VALID.getValue());
ps.setInt(2, DELIVERED.getValue());
ps.setLong(3, now);
rs = ps.executeQuery();
List<MessageId> ids = new ArrayList<MessageId>();
@@ -2081,13 +2252,13 @@ abstract class JdbcDatabase implements Database<Connection> {
}
}
public void setMessageValid(Connection txn, MessageId m, boolean valid)
public void setMessageState(Connection txn, MessageId m, State state)
throws DbException {
PreparedStatement ps = null;
try {
String sql = "UPDATE messages SET valid = ? WHERE messageId = ?";
String sql = "UPDATE messages SET state = ? WHERE messageId = ?";
ps = txn.prepareStatement(sql);
ps.setInt(1, valid ? VALID.getValue() : INVALID.getValue());
ps.setInt(1, state.getValue());
ps.setBytes(2, m.getBytes());
int affected = ps.executeUpdate();
if (affected < 0 || affected > 1) throw new DbStateException();

View File

@@ -36,6 +36,8 @@ import javax.inject.Inject;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static org.briarproject.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
import static org.briarproject.api.sync.ValidationManager.State.INVALID;
import static org.briarproject.api.sync.ValidationManager.State.VALID;
class ValidationManagerImpl implements ValidationManager, Service,
EventListener {
@@ -178,7 +180,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
try {
Metadata meta = result.getMetadata();
db.mergeMessageMetadata(txn, m.getId(), meta);
db.setMessageValid(txn, m, c, true);
db.setMessageState(txn, m.getId(), c, VALID);
db.setMessageShared(txn, m, true);
IncomingMessageHook hook = hooks.get(c);
if (hook != null)
@@ -202,7 +204,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
try {
Transaction txn = db.startTransaction(false);
try {
db.setMessageValid(txn, m, c, false);
db.setMessageState(txn, m.getId(), c, INVALID);
txn.setComplete();
} finally {
db.endTransaction(txn);

View File

@@ -29,7 +29,7 @@ import org.briarproject.api.event.MessageRequestedEvent;
import org.briarproject.api.event.MessageSharedEvent;
import org.briarproject.api.event.MessageToAckEvent;
import org.briarproject.api.event.MessageToRequestEvent;
import org.briarproject.api.event.MessageValidatedEvent;
import org.briarproject.api.event.MessageStateChangedEvent;
import org.briarproject.api.event.MessagesAckedEvent;
import org.briarproject.api.event.MessagesSentEvent;
import org.briarproject.api.event.SettingsUpdatedEvent;
@@ -53,6 +53,7 @@ import org.jmock.Expectations;
import org.jmock.Mockery;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
@@ -60,8 +61,9 @@ import java.util.Map;
import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
import static org.briarproject.api.sync.SyncConstants.MAX_GROUP_DESCRIPTOR_LENGTH;
import static org.briarproject.api.sync.ValidationManager.Validity.UNKNOWN;
import static org.briarproject.api.sync.ValidationManager.Validity.VALID;
import static org.briarproject.api.sync.ValidationManager.State.DELIVERED;
import static org.briarproject.api.sync.ValidationManager.State.UNKNOWN;
import static org.briarproject.api.sync.ValidationManager.State.VALID;
import static org.briarproject.api.transport.TransportConstants.REORDERING_WINDOW_SIZE;
import static org.briarproject.db.DatabaseConstants.MAX_OFFERED_MESSAGES;
import static org.junit.Assert.assertEquals;
@@ -264,7 +266,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
will(returnValue(true));
oneOf(database).containsMessage(txn, messageId);
will(returnValue(false));
oneOf(database).addMessage(txn, message, VALID, true);
oneOf(database).addMessage(txn, message, DELIVERED, true);
oneOf(database).mergeMessageMetadata(txn, messageId, metadata);
oneOf(database).getVisibility(txn, groupId);
will(returnValue(Collections.singletonList(contactId)));
@@ -274,7 +276,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
oneOf(database).commitTransaction(txn);
// The message was added, so the listeners should be called
oneOf(eventBus).broadcast(with(any(MessageAddedEvent.class)));
oneOf(eventBus).broadcast(with(any(MessageValidatedEvent.class)));
oneOf(eventBus).broadcast(with(any(MessageStateChangedEvent.class)));
oneOf(eventBus).broadcast(with(any(MessageSharedEvent.class)));
}});
DatabaseComponent db = createDatabaseComponent(database, eventBus,
@@ -675,11 +677,11 @@ public class DatabaseComponentImplTest extends BriarTestCase {
final EventBus eventBus = context.mock(EventBus.class);
context.checking(new Expectations() {{
// Check whether the message is in the DB (which it's not)
exactly(8).of(database).startTransaction();
exactly(10).of(database).startTransaction();
will(returnValue(txn));
exactly(8).of(database).containsMessage(txn, messageId);
exactly(10).of(database).containsMessage(txn, messageId);
will(returnValue(false));
exactly(8).of(database).abortTransaction(txn);
exactly(10).of(database).abortTransaction(txn);
// This is needed for getMessageStatus() to proceed
exactly(1).of(database).containsContact(txn, contactId);
will(returnValue(true));
@@ -759,7 +761,27 @@ public class DatabaseComponentImplTest extends BriarTestCase {
transaction = db.startTransaction(false);
try {
db.setMessageValid(transaction, message, clientId, true);
db.setMessageState(transaction, message, clientId, VALID);
fail();
} catch (NoSuchMessageException expected) {
// Expected
} finally {
db.endTransaction(transaction);
}
transaction = db.startTransaction(true);
try {
db.getMessageDependencies(transaction, messageId);
fail();
} catch (NoSuchMessageException expected) {
// Expected
} finally {
db.endTransaction(transaction);
}
transaction = db.startTransaction(true);
try {
db.getMessageDependents(transaction, messageId);
fail();
} catch (NoSuchMessageException expected) {
// Expected
@@ -1595,4 +1617,79 @@ public class DatabaseComponentImplTest extends BriarTestCase {
context.assertIsSatisfied();
}
@Test
@SuppressWarnings("unchecked")
public void testMessageDependencies() throws Exception {
final int shutdownHandle = 12345;
Mockery context = new Mockery();
final Database<Object> database = context.mock(Database.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
final EventBus eventBus = context.mock(EventBus.class);
final MessageId messageId2 = new MessageId(TestUtils.getRandomId());
context.checking(new Expectations() {{
// open()
oneOf(database).open();
will(returnValue(false));
oneOf(shutdown).addShutdownHook(with(any(Runnable.class)));
will(returnValue(shutdownHandle));
// startTransaction()
oneOf(database).startTransaction();
will(returnValue(txn));
// addLocalMessage()
oneOf(database).containsGroup(txn, groupId);
will(returnValue(true));
oneOf(database).containsMessage(txn, messageId);
will(returnValue(false));
oneOf(database).addMessage(txn, message, DELIVERED, true);
oneOf(database).getVisibility(txn, groupId);
will(returnValue(Collections.singletonList(contactId)));
oneOf(database).mergeMessageMetadata(txn, messageId, metadata);
oneOf(database).removeOfferedMessage(txn, contactId, messageId);
will(returnValue(false));
oneOf(database).addStatus(txn, contactId, messageId, false, false);
// addMessageDependencies()
oneOf(database).containsMessage(txn, messageId);
will(returnValue(true));
oneOf(database).addMessageDependency(txn, messageId, messageId1);
oneOf(database).addMessageDependency(txn, messageId, messageId2);
// getMessageDependencies()
oneOf(database).containsMessage(txn, messageId);
will(returnValue(true));
oneOf(database).getMessageDependencies(txn, messageId);
// getMessageDependents()
oneOf(database).containsMessage(txn, messageId);
will(returnValue(true));
oneOf(database).getMessageDependents(txn, messageId);
// broadcast for message added event
oneOf(eventBus).broadcast(with(any(MessageAddedEvent.class)));
oneOf(eventBus).broadcast(with(any(MessageStateChangedEvent.class)));
oneOf(eventBus).broadcast(with(any(MessageSharedEvent.class)));
// endTransaction()
oneOf(database).commitTransaction(txn);
// close()
oneOf(shutdown).removeShutdownHook(shutdownHandle);
oneOf(database).close();
}});
DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown);
assertFalse(db.open());
Transaction transaction = db.startTransaction(false);
try {
db.addLocalMessage(transaction, message, clientId, metadata, true);
Collection<MessageId> dependencies = new ArrayList<>(2);
dependencies.add(messageId1);
dependencies.add(messageId2);
db.addMessageDependencies(transaction, message, dependencies);
db.getMessageDependencies(transaction, messageId);
db.getMessageDependents(transaction, messageId);
transaction.setComplete();
} finally {
db.endTransaction(transaction);
}
db.close();
context.assertIsSatisfied();
}
}

View File

@@ -19,6 +19,7 @@ import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.Message;
import org.briarproject.api.sync.MessageId;
import org.briarproject.api.sync.MessageStatus;
import org.briarproject.api.sync.ValidationManager.State;
import org.briarproject.api.transport.IncomingKeys;
import org.briarproject.api.transport.OutgoingKeys;
import org.briarproject.api.transport.TransportKeys;
@@ -46,8 +47,11 @@ import static org.briarproject.api.db.Metadata.REMOVE;
import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
import static org.briarproject.api.sync.SyncConstants.MAX_GROUP_DESCRIPTOR_LENGTH;
import static org.briarproject.api.sync.SyncConstants.MAX_MESSAGE_LENGTH;
import static org.briarproject.api.sync.ValidationManager.Validity.UNKNOWN;
import static org.briarproject.api.sync.ValidationManager.Validity.VALID;
import static org.briarproject.api.sync.ValidationManager.State.DELIVERED;
import static org.briarproject.api.sync.ValidationManager.State.INVALID;
import static org.briarproject.api.sync.ValidationManager.State.PENDING;
import static org.briarproject.api.sync.ValidationManager.State.UNKNOWN;
import static org.briarproject.api.sync.ValidationManager.State.VALID;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -114,7 +118,7 @@ public class H2DatabaseTest extends BriarTestCase {
db.addGroup(txn, group);
assertTrue(db.containsGroup(txn, groupId));
assertFalse(db.containsMessage(txn, messageId));
db.addMessage(txn, message, VALID, true);
db.addMessage(txn, message, DELIVERED, true);
assertTrue(db.containsMessage(txn, messageId));
db.commitTransaction(txn);
db.close();
@@ -152,7 +156,7 @@ public class H2DatabaseTest extends BriarTestCase {
// Add a group and a message
db.addGroup(txn, group);
db.addMessage(txn, message, VALID, true);
db.addMessage(txn, message, DELIVERED, true);
// Removing the group should remove the message
assertTrue(db.containsMessage(txn, messageId));
@@ -174,7 +178,7 @@ public class H2DatabaseTest extends BriarTestCase {
true));
db.addGroup(txn, group);
db.addVisibility(txn, contactId, groupId);
db.addMessage(txn, message, VALID, true);
db.addMessage(txn, message, DELIVERED, true);
// The message has no status yet, so it should not be sendable
Collection<MessageId> ids = db.getMessagesToSend(txn, contactId,
@@ -202,7 +206,7 @@ public class H2DatabaseTest extends BriarTestCase {
}
@Test
public void testSendableMessagesMustBeValid() throws Exception {
public void testSendableMessagesMustBeDelivered() throws Exception {
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
@@ -222,15 +226,31 @@ public class H2DatabaseTest extends BriarTestCase {
ids = db.getMessagesToOffer(txn, contactId, 100);
assertTrue(ids.isEmpty());
// Marking the message valid should make it sendable
db.setMessageValid(txn, messageId, true);
// Marking the message delivered should make it sendable
db.setMessageState(txn, messageId, DELIVERED);
ids = db.getMessagesToSend(txn, contactId, ONE_MEGABYTE);
assertEquals(Collections.singletonList(messageId), ids);
ids = db.getMessagesToOffer(txn, contactId, 100);
assertEquals(Collections.singletonList(messageId), ids);
// Marking the message invalid should make it unsendable
db.setMessageValid(txn, messageId, false);
db.setMessageState(txn, messageId, INVALID);
ids = db.getMessagesToSend(txn, contactId, ONE_MEGABYTE);
assertTrue(ids.isEmpty());
ids = db.getMessagesToOffer(txn, contactId, 100);
assertTrue(ids.isEmpty());
// Marking the message valid should make it unsendable
// TODO do we maybe want to already send valid messages? If we do, we need also to call db.setMessageShared() earlier.
db.setMessageState(txn, messageId, VALID);
ids = db.getMessagesToSend(txn, contactId, ONE_MEGABYTE);
assertTrue(ids.isEmpty());
ids = db.getMessagesToOffer(txn, contactId, 100);
assertTrue(ids.isEmpty());
// Marking the message pending should make it unsendable
// TODO do we maybe want to already send pending messages? If we do, we need also to call db.setMessageShared() earlier.
db.setMessageState(txn, messageId, PENDING);
ids = db.getMessagesToSend(txn, contactId, ONE_MEGABYTE);
assertTrue(ids.isEmpty());
ids = db.getMessagesToOffer(txn, contactId, 100);
@@ -251,7 +271,7 @@ public class H2DatabaseTest extends BriarTestCase {
true));
db.addGroup(txn, group);
db.addVisibility(txn, contactId, groupId);
db.addMessage(txn, message, VALID, false);
db.addMessage(txn, message, DELIVERED, false);
db.addStatus(txn, contactId, messageId, false, false);
// The message is not shared, so it should not be sendable
@@ -290,7 +310,7 @@ public class H2DatabaseTest extends BriarTestCase {
true));
db.addGroup(txn, group);
db.addVisibility(txn, contactId, groupId);
db.addMessage(txn, message, VALID, true);
db.addMessage(txn, message, DELIVERED, true);
db.addStatus(txn, contactId, messageId, false, false);
// The message is sendable, but too large to send
@@ -321,10 +341,10 @@ public class H2DatabaseTest extends BriarTestCase {
// Add some messages to ack
MessageId messageId1 = new MessageId(TestUtils.getRandomId());
Message message1 = new Message(messageId1, groupId, timestamp, raw);
db.addMessage(txn, message, VALID, true);
db.addMessage(txn, message, DELIVERED, true);
db.addStatus(txn, contactId, messageId, false, true);
db.raiseAckFlag(txn, contactId, messageId);
db.addMessage(txn, message1, VALID, true);
db.addMessage(txn, message1, DELIVERED, true);
db.addStatus(txn, contactId, messageId1, false, true);
db.raiseAckFlag(txn, contactId, messageId1);
@@ -354,7 +374,7 @@ public class H2DatabaseTest extends BriarTestCase {
true));
db.addGroup(txn, group);
db.addVisibility(txn, contactId, groupId);
db.addMessage(txn, message, VALID, true);
db.addMessage(txn, message, DELIVERED, true);
db.addStatus(txn, contactId, messageId, false, false);
// Retrieve the message from the database and mark it as sent
@@ -396,7 +416,7 @@ public class H2DatabaseTest extends BriarTestCase {
// Storing a message should reduce the free space
Connection txn = db.startTransaction();
db.addGroup(txn, group);
db.addMessage(txn, message, VALID, true);
db.addMessage(txn, message, DELIVERED, true);
db.commitTransaction(txn);
assertTrue(db.getFreeSpace() < free);
@@ -555,7 +575,7 @@ public class H2DatabaseTest extends BriarTestCase {
assertEquals(contactId, db.addContact(txn, author, localAuthorId,
true));
db.addGroup(txn, group);
db.addMessage(txn, message, VALID, true);
db.addMessage(txn, message, DELIVERED, true);
db.addStatus(txn, contactId, messageId, false, false);
// The group is not visible
@@ -866,7 +886,7 @@ public class H2DatabaseTest extends BriarTestCase {
// Add a group and a message
db.addGroup(txn, group);
db.addMessage(txn, message, VALID, true);
db.addMessage(txn, message, DELIVERED, true);
// Attach some metadata to the message
Metadata metadata = new Metadata();
@@ -930,6 +950,67 @@ public class H2DatabaseTest extends BriarTestCase {
db.close();
}
@Test
public void testMessageMetadataOnlyForDeliveredMessages() throws Exception {
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
// Add a group and a message
db.addGroup(txn, group);
db.addMessage(txn, message, DELIVERED, true);
// Attach some metadata to the message
Metadata metadata = new Metadata();
metadata.put("foo", new byte[]{'b', 'a', 'r'});
metadata.put("baz", new byte[]{'b', 'a', 'm'});
db.mergeMessageMetadata(txn, messageId, metadata);
// Retrieve the metadata for the message
Metadata retrieved = db.getMessageMetadata(txn, messageId);
assertEquals(2, retrieved.size());
assertTrue(retrieved.containsKey("foo"));
assertArrayEquals(metadata.get("foo"), retrieved.get("foo"));
assertTrue(retrieved.containsKey("baz"));
assertArrayEquals(metadata.get("baz"), retrieved.get("baz"));
Map<MessageId, Metadata> map = db.getMessageMetadata(txn, groupId);
assertEquals(1, map.size());
assertTrue(map.get(messageId).containsKey("foo"));
assertArrayEquals(metadata.get("foo"), map.get(messageId).get("foo"));
assertTrue(map.get(messageId).containsKey("baz"));
assertArrayEquals(metadata.get("baz"), map.get(messageId).get("baz"));
// No metadata for unknown messages
db.setMessageState(txn, messageId, UNKNOWN);
retrieved = db.getMessageMetadata(txn, messageId);
assertTrue(retrieved.isEmpty());
map = db.getMessageMetadata(txn, groupId);
assertTrue(map.isEmpty());
// No metadata for invalid messages
db.setMessageState(txn, messageId, INVALID);
retrieved = db.getMessageMetadata(txn, messageId);
assertTrue(retrieved.isEmpty());
map = db.getMessageMetadata(txn, groupId);
assertTrue(map.isEmpty());
// No metadata for valid messages
db.setMessageState(txn, messageId, VALID);
retrieved = db.getMessageMetadata(txn, messageId);
assertTrue(retrieved.isEmpty());
map = db.getMessageMetadata(txn, groupId);
assertTrue(map.isEmpty());
// No metadata for pending messages
db.setMessageState(txn, messageId, PENDING);
retrieved = db.getMessageMetadata(txn, messageId);
assertTrue(retrieved.isEmpty());
map = db.getMessageMetadata(txn, groupId);
assertTrue(map.isEmpty());
db.commitTransaction(txn);
db.close();
}
@Test
public void testMetadataQueries() throws Exception {
MessageId messageId1 = new MessageId(TestUtils.getRandomId());
@@ -940,8 +1021,8 @@ public class H2DatabaseTest extends BriarTestCase {
// Add a group and two messages
db.addGroup(txn, group);
db.addMessage(txn, message, VALID, true);
db.addMessage(txn, message1, VALID, true);
db.addMessage(txn, message, DELIVERED, true);
db.addMessage(txn, message1, DELIVERED, true);
// Attach some metadata to the messages
Metadata metadata = new Metadata();
@@ -1034,6 +1115,257 @@ public class H2DatabaseTest extends BriarTestCase {
db.close();
}
@Test
public void testMetadataQueriesOnlyForDeliveredMessages() throws Exception {
MessageId messageId1 = new MessageId(TestUtils.getRandomId());
Message message1 = new Message(messageId1, groupId, timestamp, raw);
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
// Add a group and two messages
db.addGroup(txn, group);
db.addMessage(txn, message, DELIVERED, true);
db.addMessage(txn, message1, DELIVERED, true);
// Attach some metadata to the messages
Metadata metadata = new Metadata();
metadata.put("foo", new byte[]{'b', 'a', 'r'});
metadata.put("baz", new byte[]{'b', 'a', 'm'});
db.mergeMessageMetadata(txn, messageId, metadata);
Metadata metadata1 = new Metadata();
metadata1.put("foo", new byte[]{'b', 'a', 'r'});
db.mergeMessageMetadata(txn, messageId1, metadata1);
for (int i = 1; i <= 2; i++) {
Metadata query;
if (i == 1) {
// Query the metadata with an empty query
query = new Metadata();
} else {
// Query for foo
query = new Metadata();
query.put("foo", metadata.get("foo"));
}
db.setMessageState(txn, messageId, DELIVERED);
db.setMessageState(txn, messageId1, DELIVERED);
Map<MessageId, Metadata> all =
db.getMessageMetadata(txn, groupId, query);
assertEquals(2, all.size());
assertEquals(2, all.get(messageId).size());
assertEquals(1, all.get(messageId1).size());
// No metadata for unknown messages
db.setMessageState(txn, messageId, UNKNOWN);
db.setMessageState(txn, messageId1, UNKNOWN);
all = db.getMessageMetadata(txn, groupId, query);
assertTrue(all.isEmpty());
// No metadata for invalid messages
db.setMessageState(txn, messageId, INVALID);
db.setMessageState(txn, messageId1, INVALID);
all = db.getMessageMetadata(txn, groupId, query);
assertTrue(all.isEmpty());
// No metadata for valid messages
db.setMessageState(txn, messageId, VALID);
db.setMessageState(txn, messageId1, VALID);
all = db.getMessageMetadata(txn, groupId, query);
assertTrue(all.isEmpty());
// No metadata for pending messages
db.setMessageState(txn, messageId, PENDING);
db.setMessageState(txn, messageId1, PENDING);
all = db.getMessageMetadata(txn, groupId, query);
assertTrue(all.isEmpty());
}
db.commitTransaction(txn);
db.close();
}
@Test
public void testMessageDependencies() throws Exception {
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
// Add a group and a message
db.addGroup(txn, group);
db.addMessage(txn, message, VALID, true);
// Create more messages
MessageId mId1 = new MessageId(TestUtils.getRandomId());
MessageId mId2 = new MessageId(TestUtils.getRandomId());
MessageId dId1 = new MessageId(TestUtils.getRandomId());
MessageId dId2 = new MessageId(TestUtils.getRandomId());
Message m1 = new Message(mId1, groupId, timestamp, raw);
Message m2 = new Message(mId2, groupId, timestamp, raw);
// Add new messages
db.addMessage(txn, m1, VALID, true);
db.addMessage(txn, m2, INVALID, true);
// Add dependencies
db.addMessageDependency(txn, messageId, mId1);
db.addMessageDependency(txn, messageId, mId2);
db.addMessageDependency(txn, mId1, dId1);
db.addMessageDependency(txn, mId2, dId2);
Map<MessageId, State> dependencies;
// Retrieve dependencies for root
dependencies = db.getMessageDependencies(txn, messageId);
assertEquals(2, dependencies.size());
assertEquals(VALID, dependencies.get(mId1));
assertEquals(INVALID, dependencies.get(mId2));
// Retrieve dependencies for m1
dependencies = db.getMessageDependencies(txn, mId1);
assertEquals(1, dependencies.size());
assertEquals(UNKNOWN, dependencies.get(dId1));
// Retrieve dependencies for m2
dependencies = db.getMessageDependencies(txn, mId2);
assertEquals(1, dependencies.size());
assertEquals(UNKNOWN, dependencies.get(dId2));
// Make sure d's have no dependencies
dependencies = db.getMessageDependencies(txn, dId1);
assertTrue(dependencies.isEmpty());
dependencies = db.getMessageDependencies(txn, dId2);
assertTrue(dependencies.isEmpty());
Map<MessageId, State> dependents;
// Root message does not have dependents
dependents = db.getMessageDependents(txn, messageId);
assertTrue(dependents.isEmpty());
// The root message depends on both m's
dependents = db.getMessageDependents(txn, mId1);
assertEquals(1, dependents.size());
assertEquals(VALID, dependents.get(messageId));
dependents = db.getMessageDependents(txn, mId2);
assertEquals(1, dependents.size());
assertEquals(VALID, dependents.get(messageId));
// Both m's depend on the d's
dependents = db.getMessageDependents(txn, dId1);
assertEquals(1, dependents.size());
assertEquals(VALID, dependents.get(mId1));
dependents = db.getMessageDependents(txn, dId2);
assertEquals(1, dependents.size());
assertEquals(INVALID, dependents.get(mId2));
db.commitTransaction(txn);
db.close();
}
@Test
public void testMessageDependenciesInSameGroup() throws Exception {
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
// Add a group and a message
db.addGroup(txn, group);
db.addMessage(txn, message, DELIVERED, true);
// Add a second group
GroupId groupId1 = new GroupId(TestUtils.getRandomId());
Group group1 = new Group(groupId1, group.getClientId(),
TestUtils.getRandomBytes(MAX_GROUP_DESCRIPTOR_LENGTH));
db.addGroup(txn, group1);
// Add a message to the second group
MessageId mId1 = new MessageId(TestUtils.getRandomId());
Message m1 = new Message(mId1, groupId1, timestamp, raw);
db.addMessage(txn, m1, DELIVERED, true);
// Create a fake dependency as well
MessageId mId2 = new MessageId(TestUtils.getRandomId());
// Create and add a real and proper dependency
MessageId mId3 = new MessageId(TestUtils.getRandomId());
Message m3 = new Message(mId3, groupId, timestamp, raw);
db.addMessage(txn, m3, PENDING, true);
// Add dependencies
db.addMessageDependency(txn, messageId, mId1);
db.addMessageDependency(txn, messageId, mId2);
db.addMessageDependency(txn, messageId, mId3);
// Return invalid dependencies for delivered message m1
Map<MessageId, State> dependencies;
dependencies = db.getMessageDependencies(txn, messageId);
assertEquals(INVALID, dependencies.get(mId1));
assertEquals(UNKNOWN, dependencies.get(mId2));
assertEquals(PENDING, dependencies.get(mId3));
// Return invalid dependencies for valid message m1
db.setMessageState(txn, mId1, VALID);
dependencies = db.getMessageDependencies(txn, messageId);
assertEquals(INVALID, dependencies.get(mId1));
assertEquals(UNKNOWN, dependencies.get(mId2));
assertEquals(PENDING, dependencies.get(mId3));
// Return invalid dependencies for pending message m1
db.setMessageState(txn, mId1, PENDING);
dependencies = db.getMessageDependencies(txn, messageId);
assertEquals(INVALID, dependencies.get(mId1));
assertEquals(UNKNOWN, dependencies.get(mId2));
assertEquals(PENDING, dependencies.get(mId3));
db.commitTransaction(txn);
db.close();
}
@Test
public void testGetMessagesForValidationAndDelivery() throws Exception {
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
// Add a group and a message
db.addGroup(txn, group);
db.addMessage(txn, message, VALID, true);
// Create more messages
MessageId mId1 = new MessageId(TestUtils.getRandomId());
MessageId mId2 = new MessageId(TestUtils.getRandomId());
MessageId mId3 = new MessageId(TestUtils.getRandomId());
MessageId mId4 = new MessageId(TestUtils.getRandomId());
Message m1 = new Message(mId1, groupId, timestamp, raw);
Message m2 = new Message(mId2, groupId, timestamp, raw);
Message m3 = new Message(mId3, groupId, timestamp, raw);
Message m4 = new Message(mId4, groupId, timestamp, raw);
// Add new messages with different states
db.addMessage(txn, m1, UNKNOWN, true);
db.addMessage(txn, m2, INVALID, true);
db.addMessage(txn, m3, PENDING, true);
db.addMessage(txn, m4, DELIVERED, true);
Collection<MessageId> result;
// Retrieve messages to be validated
result = db.getMessagesToValidate(txn, group.getClientId());
assertEquals(1, result.size());
assertTrue(result.contains(mId1));
// Retrieve messages to be delivered
result = db.getMessagesToDeliver(txn, group.getClientId());
assertEquals(1, result.size());
assertTrue(result.contains(messageId));
// Retrieve pending messages
result = db.getPendingMessages(txn, group.getClientId());
assertEquals(1, result.size());
assertTrue(result.contains(mId3));
db.commitTransaction(txn);
db.close();
}
@Test
public void testGetMessageStatus() throws Exception {
Database<Connection> db = open(false);
@@ -1049,7 +1381,7 @@ public class H2DatabaseTest extends BriarTestCase {
db.addVisibility(txn, contactId, groupId);
// Add a message to the group
db.addMessage(txn, message, VALID, true);
db.addMessage(txn, message, DELIVERED, true);
db.addStatus(txn, contactId, messageId, false, false);
// The message should not be sent or seen
@@ -1176,7 +1508,7 @@ public class H2DatabaseTest extends BriarTestCase {
true));
db.addGroup(txn, group);
db.addVisibility(txn, contactId, groupId);
db.addMessage(txn, message, VALID, true);
db.addMessage(txn, message, DELIVERED, true);
db.addStatus(txn, contactId, messageId, false, false);
// The message should be visible to the contact

View File

@@ -28,6 +28,9 @@ import org.junit.Test;
import java.util.Arrays;
import java.util.concurrent.Executor;
import static org.briarproject.api.sync.ValidationManager.State.INVALID;
import static org.briarproject.api.sync.ValidationManager.State.VALID;
public class ValidationManagerImplTest extends BriarTestCase {
private final ClientId clientId = new ClientId(TestUtils.getRandomId());
@@ -88,7 +91,7 @@ public class ValidationManagerImplTest extends BriarTestCase {
oneOf(db).startTransaction(false);
will(returnValue(txn2));
oneOf(db).mergeMessageMetadata(txn2, messageId, metadata);
oneOf(db).setMessageValid(txn2, message, clientId, true);
oneOf(db).setMessageState(txn2, messageId, clientId, VALID);
oneOf(db).setMessageShared(txn2, message, true);
// Call the hook for the first message
oneOf(hook).incomingMessage(txn2, message, metadata);
@@ -107,7 +110,7 @@ public class ValidationManagerImplTest extends BriarTestCase {
// Store the validation result for the second message
oneOf(db).startTransaction(false);
will(returnValue(txn4));
oneOf(db).setMessageValid(txn4, message1, clientId, false);
oneOf(db).setMessageState(txn4, messageId1, clientId, INVALID);
oneOf(db).endTransaction(txn4);
}});
@@ -161,7 +164,7 @@ public class ValidationManagerImplTest extends BriarTestCase {
// Store the validation result for the second message
oneOf(db).startTransaction(false);
will(returnValue(txn3));
oneOf(db).setMessageValid(txn3, message1, clientId, false);
oneOf(db).setMessageState(txn3, messageId1, clientId, INVALID);
oneOf(db).endTransaction(txn3);
}});
@@ -218,7 +221,7 @@ public class ValidationManagerImplTest extends BriarTestCase {
// Store the validation result for the second message
oneOf(db).startTransaction(false);
will(returnValue(txn3));
oneOf(db).setMessageValid(txn3, message1, clientId, false);
oneOf(db).setMessageState(txn3, messageId1, clientId, INVALID);
oneOf(db).endTransaction(txn3);
}});
@@ -256,7 +259,7 @@ public class ValidationManagerImplTest extends BriarTestCase {
oneOf(db).startTransaction(false);
will(returnValue(txn1));
oneOf(db).mergeMessageMetadata(txn1, messageId, metadata);
oneOf(db).setMessageValid(txn1, message, clientId, true);
oneOf(db).setMessageState(txn1, messageId, clientId, VALID);
oneOf(db).setMessageShared(txn1, message, true);
// Call the hook
oneOf(hook).incomingMessage(txn1, message, metadata);