Merge branch '378-replace-bdf-with-classes-for-forum-sharing-client' into 'master'

Replace BDF data structures with classes in forum sharing client

This introduces two new classes for protocol session states: One for the sharer and one for the invitee.
The respective classes for protocol state machines and actions have been moved into these classes as inner classes.
The two new classes replace the `BdfDictionary` that was used before to represent the local state information of a forum sharing session.

A similar technique is used for local actions and protocol messages.

Local actions are just represented by one Enum and protocol messages have their own classes now that also handle serialization into BdfDictionaries and BdfLists.

Closes #378 

See merge request !184
This commit is contained in:
Torsten Grote
2016-05-19 14:32:41 +00:00
12 changed files with 883 additions and 613 deletions

View File

@@ -0,0 +1,166 @@
package org.briarproject.api.forum;
import org.briarproject.api.FormatException;
import org.briarproject.api.clients.SessionId;
import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.data.BdfEntry;
import org.briarproject.api.data.BdfList;
import org.briarproject.api.sync.GroupId;
import static org.briarproject.api.forum.ForumConstants.FORUM_NAME;
import static org.briarproject.api.forum.ForumConstants.FORUM_SALT;
import static org.briarproject.api.forum.ForumConstants.GROUP_ID;
import static org.briarproject.api.forum.ForumConstants.INVITATION_MSG;
import static org.briarproject.api.forum.ForumConstants.SESSION_ID;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_ABORT;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_ACCEPT;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_DECLINE;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_INVITATION;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_LEAVE;
import static org.briarproject.api.forum.ForumConstants.TYPE;
public interface ForumSharingMessage {
abstract class BaseMessage {
private final GroupId groupId;
private final SessionId sessionId;
public BaseMessage(GroupId groupId, SessionId sessionId) {
this.groupId = groupId;
this.sessionId = sessionId;
}
public BdfList toBdfList() {
return BdfList.of(getType(), getSessionId());
}
public abstract BdfDictionary toBdfDictionary();
protected BdfDictionary toBdfDictionaryHelper() {
return BdfDictionary.of(
new BdfEntry(TYPE, getType()),
new BdfEntry(GROUP_ID, groupId),
new BdfEntry(SESSION_ID, sessionId)
);
}
public static BaseMessage from(GroupId groupId, BdfDictionary d)
throws FormatException {
long type = d.getLong(TYPE);
if (type == SHARE_MSG_TYPE_INVITATION)
return Invitation.from(groupId, d);
else
return SimpleMessage.from(type, groupId, d);
}
public abstract long getType();
public GroupId getGroupId() {
return groupId;
}
public SessionId getSessionId() {
return sessionId;
}
}
class Invitation extends BaseMessage {
private final String forumName;
private final byte[] forumSalt;
private final String message;
public Invitation(GroupId groupId, SessionId sessionId,
String forumName, byte[] forumSalt, String message) {
super(groupId, sessionId);
this.forumName = forumName;
this.forumSalt = forumSalt;
this.message = message;
}
@Override
public long getType() {
return SHARE_MSG_TYPE_INVITATION;
}
@Override
public BdfList toBdfList() {
BdfList list = super.toBdfList();
list.add(forumName);
list.add(forumSalt);
if (message != null) list.add(message);
return list;
}
@Override
public BdfDictionary toBdfDictionary() {
BdfDictionary d = toBdfDictionaryHelper();
d.put(FORUM_NAME, forumName);
d.put(FORUM_SALT, forumSalt);
if (message != null) d.put(INVITATION_MSG, message);
return d;
}
public static Invitation from(GroupId groupId, BdfDictionary d)
throws FormatException {
SessionId sessionId = new SessionId(d.getRaw(SESSION_ID));
String forumName = d.getString(FORUM_NAME);
byte[] forumSalt = d.getRaw(FORUM_SALT);
String message = d.getOptionalString(INVITATION_MSG);
return new Invitation(groupId, sessionId, forumName, forumSalt,
message);
}
public String getForumName() {
return forumName;
}
public byte[] getForumSalt() {
return forumSalt;
}
public String getMessage() {
return message;
}
}
class SimpleMessage extends BaseMessage {
private final long type;
public SimpleMessage(long type, GroupId groupId, SessionId sessionId) {
super(groupId, sessionId);
this.type = type;
}
@Override
public long getType() {
return type;
}
@Override
public BdfDictionary toBdfDictionary() {
return toBdfDictionaryHelper();
}
public static SimpleMessage from(long type, GroupId groupId,
BdfDictionary d) throws FormatException {
if (type != SHARE_MSG_TYPE_ACCEPT &&
type != SHARE_MSG_TYPE_DECLINE &&
type != SHARE_MSG_TYPE_LEAVE &&
type != SHARE_MSG_TYPE_ABORT) throw new FormatException();
SessionId sessionId = new SessionId(d.getRaw(SESSION_ID));
return new SimpleMessage(type, groupId, sessionId);
}
}
}

View File

@@ -1,34 +0,0 @@
package org.briarproject.api.forum;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_ABORT;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_ACCEPT;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_DECLINE;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_INVITATION;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_LEAVE;
public enum InviteeAction {
LOCAL_ACCEPT,
LOCAL_DECLINE,
LOCAL_LEAVE,
LOCAL_ABORT,
REMOTE_INVITATION,
REMOTE_LEAVE,
REMOTE_ABORT;
public static InviteeAction getLocal(long type) {
if (type == SHARE_MSG_TYPE_ACCEPT) return LOCAL_ACCEPT;
if (type == SHARE_MSG_TYPE_DECLINE) return LOCAL_DECLINE;
if (type == SHARE_MSG_TYPE_LEAVE) return LOCAL_LEAVE;
if (type == SHARE_MSG_TYPE_ABORT) return LOCAL_ABORT;
return null;
}
public static InviteeAction getRemote(long type) {
if (type == SHARE_MSG_TYPE_INVITATION) return REMOTE_INVITATION;
if (type == SHARE_MSG_TYPE_LEAVE) return REMOTE_LEAVE;
if (type == SHARE_MSG_TYPE_ABORT) return REMOTE_ABORT;
return null;
}
}

View File

@@ -1,62 +0,0 @@
package org.briarproject.api.forum;
import static org.briarproject.api.forum.InviteeAction.LOCAL_ACCEPT;
import static org.briarproject.api.forum.InviteeAction.LOCAL_DECLINE;
import static org.briarproject.api.forum.InviteeAction.LOCAL_LEAVE;
import static org.briarproject.api.forum.InviteeAction.REMOTE_INVITATION;
import static org.briarproject.api.forum.InviteeAction.REMOTE_LEAVE;
public enum InviteeProtocolState {
ERROR(0),
AWAIT_INVITATION(1) {
@Override
public InviteeProtocolState next(InviteeAction a) {
if (a == REMOTE_INVITATION) return AWAIT_LOCAL_RESPONSE;
return ERROR;
}
},
AWAIT_LOCAL_RESPONSE(2) {
@Override
public InviteeProtocolState next(InviteeAction a) {
if (a == LOCAL_ACCEPT || a == LOCAL_DECLINE) return FINISHED;
if (a == REMOTE_LEAVE) return LEFT;
return ERROR;
}
},
FINISHED(3) {
@Override
public InviteeProtocolState next(InviteeAction a) {
if (a == LOCAL_LEAVE || a == REMOTE_LEAVE) return LEFT;
return FINISHED;
}
},
LEFT(4) {
@Override
public InviteeProtocolState next(InviteeAction a) {
if (a == LOCAL_LEAVE) return ERROR;
return LEFT;
}
};
private final int value;
InviteeProtocolState(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public static InviteeProtocolState fromValue(int value) {
for (InviteeProtocolState s : values()) {
if (s.value == value) return s;
}
throw new IllegalArgumentException();
}
public InviteeProtocolState next(InviteeAction a) {
return this;
}
}

View File

@@ -1,34 +0,0 @@
package org.briarproject.api.forum;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_ABORT;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_ACCEPT;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_DECLINE;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_INVITATION;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_LEAVE;
public enum SharerAction {
LOCAL_INVITATION,
LOCAL_LEAVE,
LOCAL_ABORT,
REMOTE_ACCEPT,
REMOTE_DECLINE,
REMOTE_LEAVE,
REMOTE_ABORT;
public static SharerAction getLocal(long type) {
if (type == SHARE_MSG_TYPE_INVITATION) return LOCAL_INVITATION;
if (type == SHARE_MSG_TYPE_LEAVE) return LOCAL_LEAVE;
if (type == SHARE_MSG_TYPE_ABORT) return LOCAL_ABORT;
return null;
}
public static SharerAction getRemote(long type) {
if (type == SHARE_MSG_TYPE_ACCEPT) return REMOTE_ACCEPT;
if (type == SHARE_MSG_TYPE_DECLINE) return REMOTE_DECLINE;
if (type == SHARE_MSG_TYPE_LEAVE) return REMOTE_LEAVE;
if (type == SHARE_MSG_TYPE_ABORT) return REMOTE_ABORT;
return null;
}
}

View File

@@ -1,62 +0,0 @@
package org.briarproject.api.forum;
import static org.briarproject.api.forum.SharerAction.LOCAL_INVITATION;
import static org.briarproject.api.forum.SharerAction.LOCAL_LEAVE;
import static org.briarproject.api.forum.SharerAction.REMOTE_ACCEPT;
import static org.briarproject.api.forum.SharerAction.REMOTE_DECLINE;
import static org.briarproject.api.forum.SharerAction.REMOTE_LEAVE;
public enum SharerProtocolState {
ERROR(0),
PREPARE_INVITATION(1) {
@Override
public SharerProtocolState next(SharerAction a) {
if (a == LOCAL_INVITATION) return AWAIT_RESPONSE;
return ERROR;
}
},
AWAIT_RESPONSE(2) {
@Override
public SharerProtocolState next(SharerAction a) {
if (a == REMOTE_ACCEPT || a == REMOTE_DECLINE) return FINISHED;
if (a == LOCAL_LEAVE) return LEFT;
return ERROR;
}
},
FINISHED(3) {
@Override
public SharerProtocolState next(SharerAction a) {
if (a == LOCAL_LEAVE || a == REMOTE_LEAVE) return LEFT;
return FINISHED;
}
},
LEFT(4) {
@Override
public SharerProtocolState next(SharerAction a) {
if (a == LOCAL_LEAVE) return ERROR;
return LEFT;
}
};
private final int value;
SharerProtocolState(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public static SharerProtocolState fromValue(int value) {
for (SharerProtocolState s : values()) {
if (s.value == value) return s;
}
throw new IllegalArgumentException();
}
public SharerProtocolState next(SharerAction a) {
return this;
}
}

View File

@@ -27,10 +27,6 @@ import org.briarproject.api.forum.ForumFactory;
import org.briarproject.api.forum.ForumInvitationMessage;
import org.briarproject.api.forum.ForumManager;
import org.briarproject.api.forum.ForumSharingManager;
import org.briarproject.api.forum.InviteeAction;
import org.briarproject.api.forum.InviteeProtocolState;
import org.briarproject.api.forum.SharerAction;
import org.briarproject.api.forum.SharerProtocolState;
import org.briarproject.api.sync.ClientId;
import org.briarproject.api.sync.Group;
import org.briarproject.api.sync.GroupId;
@@ -59,12 +55,7 @@ import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static org.briarproject.api.clients.ProtocolEngine.StateUpdate;
import static org.briarproject.api.forum.ForumConstants.CONTACT_ID;
import static org.briarproject.api.forum.ForumConstants.FORUM_ID;
import static org.briarproject.api.forum.ForumConstants.FORUM_NAME;
import static org.briarproject.api.forum.ForumConstants.FORUM_SALT;
import static org.briarproject.api.forum.ForumConstants.FORUM_SALT_LENGTH;
import static org.briarproject.api.forum.ForumConstants.GROUP_ID;
import static org.briarproject.api.forum.ForumConstants.INVITATION_MSG;
import static org.briarproject.api.forum.ForumConstants.IS_SHARER;
import static org.briarproject.api.forum.ForumConstants.LOCAL;
import static org.briarproject.api.forum.ForumConstants.READ;
@@ -76,11 +67,8 @@ import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_ACCEPT;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_DECLINE;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_INVITATION;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_LEAVE;
import static org.briarproject.api.forum.ForumConstants.STATE;
import static org.briarproject.api.forum.ForumConstants.STORAGE_ID;
import static org.briarproject.api.forum.ForumConstants.TASK;
import static org.briarproject.api.forum.ForumConstants.TASK_ADD_FORUM_TO_LIST_TO_BE_SHARED_BY_US;
import static org.briarproject.api.forum.ForumConstants.TASK_ADD_FORUM_TO_LIST_SHARED_WITH_US;
import static org.briarproject.api.forum.ForumConstants.TASK_ADD_FORUM_TO_LIST_TO_BE_SHARED_BY_US;
import static org.briarproject.api.forum.ForumConstants.TASK_ADD_SHARED_FORUM;
import static org.briarproject.api.forum.ForumConstants.TASK_REMOVE_FORUM_FROM_LIST_SHARED_WITH_US;
import static org.briarproject.api.forum.ForumConstants.TASK_REMOVE_FORUM_FROM_LIST_TO_BE_SHARED_BY_US;
@@ -91,9 +79,10 @@ import static org.briarproject.api.forum.ForumConstants.TIME;
import static org.briarproject.api.forum.ForumConstants.TO_BE_SHARED_BY_US;
import static org.briarproject.api.forum.ForumConstants.TYPE;
import static org.briarproject.api.forum.ForumManager.RemoveForumHook;
import static org.briarproject.api.forum.InviteeProtocolState.AWAIT_INVITATION;
import static org.briarproject.api.forum.InviteeProtocolState.AWAIT_LOCAL_RESPONSE;
import static org.briarproject.api.forum.SharerProtocolState.PREPARE_INVITATION;
import static org.briarproject.api.forum.ForumSharingMessage.BaseMessage;
import static org.briarproject.api.forum.ForumSharingMessage.Invitation;
import static org.briarproject.forum.ForumSharingSessionState.fromBdfDictionary;
import static org.briarproject.forum.SharerSessionState.Action;
class ForumSharingManagerImpl extends BdfIncomingMessageHook
implements ForumSharingManager, Client, RemoveForumHook,
@@ -187,11 +176,12 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
@Override
protected void incomingMessage(Transaction txn, Message m, BdfList body,
BdfDictionary msg) throws DbException, FormatException {
BdfDictionary d) throws DbException, FormatException {
SessionId sessionId = new SessionId(msg.getRaw(SESSION_ID));
long type = msg.getLong(TYPE);
if (type == SHARE_MSG_TYPE_INVITATION) {
BaseMessage msg = BaseMessage.from(m.getGroupId(), d);
SessionId sessionId = msg.getSessionId();
if (msg.getType() == SHARE_MSG_TYPE_INVITATION) {
// we are an invitee who just received a new invitation
boolean stateExists = true;
try {
@@ -206,43 +196,46 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
if (stateExists) throw new FormatException();
// check if forum can be shared
Forum f = forumFactory.createForum(msg.getString(FORUM_NAME),
msg.getRaw(FORUM_SALT));
Invitation invitation = (Invitation) msg;
Forum f = forumFactory.createForum(invitation.getForumName(),
invitation.getForumSalt());
ContactId contactId = getContactId(txn, m.getGroupId());
Contact contact = db.getContact(txn, contactId);
if (!canBeShared(txn, f.getId(), contact))
throw new FormatException();
// initialize state and process invitation
BdfDictionary state =
initializeInviteeState(txn, contactId, msg);
InviteeSessionState state =
initializeInviteeState(txn, contactId, invitation);
InviteeEngine engine = new InviteeEngine(forumFactory);
processStateUpdate(txn, m.getId(),
processInviteeStateUpdate(txn, m.getId(),
engine.onMessageReceived(state, msg));
} catch (FormatException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
deleteMessage(txn, m.getId());
}
} else if (type == SHARE_MSG_TYPE_ACCEPT ||
type == SHARE_MSG_TYPE_DECLINE) {
} else if (msg.getType() == SHARE_MSG_TYPE_ACCEPT ||
msg.getType() == SHARE_MSG_TYPE_DECLINE) {
// we are a sharer who just received a response
BdfDictionary state = getSessionState(txn, sessionId, true);
SharerSessionState state = getSessionStateForSharer(txn, sessionId);
SharerEngine engine = new SharerEngine();
processStateUpdate(txn, m.getId(),
processSharerStateUpdate(txn, m.getId(),
engine.onMessageReceived(state, msg));
} else if (type == SHARE_MSG_TYPE_LEAVE ||
type == SHARE_MSG_TYPE_ABORT) {
} else if (msg.getType() == SHARE_MSG_TYPE_LEAVE ||
msg.getType() == SHARE_MSG_TYPE_ABORT) {
// we don't know who we are, so figure it out
BdfDictionary state = getSessionState(txn, sessionId, true);
if (state.getBoolean(IS_SHARER)) {
ForumSharingSessionState s = getSessionState(txn, sessionId, true);
if (s instanceof SharerSessionState) {
// we are a sharer and the invitee wants to leave or abort
SharerSessionState state = (SharerSessionState) s;
SharerEngine engine = new SharerEngine();
processStateUpdate(txn, m.getId(),
processSharerStateUpdate(txn, m.getId(),
engine.onMessageReceived(state, msg));
} else {
// we are an invitee and the sharer wants to leave or abort
InviteeSessionState state = (InviteeSessionState) s;
InviteeEngine engine = new InviteeEngine(forumFactory);
processStateUpdate(txn, m.getId(),
processInviteeStateUpdate(txn, m.getId(),
engine.onMessageReceived(state, msg));
}
} else {
@@ -264,19 +257,18 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
try {
// initialize local state for sharer
Forum f = forumManager.getForum(txn, groupId);
BdfDictionary localState = initializeSharerState(txn, f, contactId);
SharerSessionState localState =
initializeSharerState(txn, f, contactId);
// define action
BdfDictionary localAction = new BdfDictionary();
localAction.put(TYPE, SHARE_MSG_TYPE_INVITATION);
// add invitation message to local state to be available for engine
if (!StringUtils.isNullOrEmpty(msg)) {
localAction.put(INVITATION_MSG, msg);
localState.setMessage(msg);
}
// start engine and process its state update
SharerEngine engine = new SharerEngine();
processStateUpdate(txn, null,
engine.onLocalAction(localState, localAction));
processSharerStateUpdate(txn, null,
engine.onLocalAction(localState, Action.LOCAL_INVITATION));
txn.setComplete();
} catch (FormatException e) {
@@ -293,19 +285,19 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
Transaction txn = db.startTransaction(false);
try {
// find session state based on forum
BdfDictionary localState = getSessionStateForResponse(txn, f);
InviteeSessionState localState = getSessionStateForResponse(txn, f);
// define action
BdfDictionary localAction = new BdfDictionary();
InviteeSessionState.Action localAction;
if (accept) {
localAction.put(TYPE, SHARE_MSG_TYPE_ACCEPT);
localAction = InviteeSessionState.Action.LOCAL_ACCEPT;
} else {
localAction.put(TYPE, SHARE_MSG_TYPE_DECLINE);
localAction = InviteeSessionState.Action.LOCAL_DECLINE;
}
// start engine and process its state update
InviteeEngine engine = new InviteeEngine(forumFactory);
processStateUpdate(txn, null,
processInviteeStateUpdate(txn, null,
engine.onLocalAction(localState, localAction));
txn.setComplete();
@@ -330,34 +322,33 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
Map<MessageId, BdfDictionary> map = clientHelper
.getMessageMetadataAsDictionary(txn, group.getId());
for (Map.Entry<MessageId, BdfDictionary> m : map.entrySet()) {
BdfDictionary msg = m.getValue();
BdfDictionary d = m.getValue();
try {
if (msg.getLong(TYPE) != SHARE_MSG_TYPE_INVITATION)
if (d.getLong(TYPE) != SHARE_MSG_TYPE_INVITATION)
continue;
Invitation msg = Invitation.from(group.getId(), d);
MessageStatus status =
db.getMessageStatus(txn, contactId, m.getKey());
SessionId sessionId = new SessionId(msg.getRaw(SESSION_ID));
String name = msg.getString(FORUM_NAME);
String message = msg.getOptionalString(INVITATION_MSG);
long time = msg.getLong(TIME);
boolean local = msg.getBoolean(LOCAL);
boolean read = msg.getBoolean(READ, false);
long time = d.getLong(TIME);
boolean local = d.getBoolean(LOCAL);
boolean read = d.getBoolean(READ, false);
boolean available = false;
if (!local) {
// figure out whether the forum is still available
BdfDictionary sessionState =
getSessionState(txn, sessionId, true);
InviteeProtocolState state = InviteeProtocolState
.fromValue(
sessionState.getLong(STATE).intValue());
available = state == AWAIT_LOCAL_RESPONSE;
ForumSharingSessionState s =
getSessionState(txn, msg.getSessionId(), true);
if (!(s instanceof InviteeSessionState))
continue;
available = ((InviteeSessionState) s).getState() ==
InviteeSessionState.State.AWAIT_LOCAL_RESPONSE;
}
ForumInvitationMessage im =
new ForumInvitationMessage(m.getKey(), sessionId,
contactId, name, message, available, time,
local, status.isSent(), status.isSeen(),
read);
new ForumInvitationMessage(m.getKey(),
msg.getSessionId(), contactId,
msg.getForumName(), msg.getMessage(),
available, time, local, status.isSent(),
status.isSeen(), read);
list.add(im);
} catch (FormatException e) {
if (LOG.isLoggable(WARNING))
@@ -490,7 +481,7 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
}
}
private BdfDictionary initializeSharerState(Transaction txn, Forum f,
private SharerSessionState initializeSharerState(Transaction txn, Forum f,
ContactId contactId) throws FormatException, DbException {
Contact c = db.getContact(txn, contactId);
@@ -502,33 +493,27 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
random.nextBytes(salt.getBytes());
Message m = clientHelper.createMessage(localGroup.getId(), now,
BdfList.of(salt));
MessageId sessionId = m.getId();
SessionId sessionId = new SessionId(m.getId().getBytes());
BdfDictionary d = new BdfDictionary();
d.put(SESSION_ID, sessionId);
d.put(STORAGE_ID, sessionId);
d.put(GROUP_ID, group.getId());
d.put(IS_SHARER, true);
d.put(STATE, PREPARE_INVITATION.getValue());
d.put(CONTACT_ID, contactId.getInt());
d.put(FORUM_ID, f.getId());
d.put(FORUM_NAME, f.getName());
d.put(FORUM_SALT, f.getSalt());
SharerSessionState s = new SharerSessionState(sessionId, sessionId,
group.getId(), SharerSessionState.State.PREPARE_INVITATION,
contactId, f.getId(), f.getName(), f.getSalt());
// save local state to database
BdfDictionary d = s.toBdfDictionary();
clientHelper.addLocalMessage(txn, m, getClientId(), d, false);
return d;
return s;
}
private BdfDictionary initializeInviteeState(Transaction txn,
ContactId contactId, BdfDictionary msg)
private InviteeSessionState initializeInviteeState(Transaction txn,
ContactId contactId, Invitation msg)
throws FormatException, DbException {
Contact c = db.getContact(txn, contactId);
Group group = getContactGroup(c);
String name = msg.getString(FORUM_NAME);
byte[] salt = msg.getRaw(FORUM_SALT);
String name = msg.getForumName();
byte[] salt = msg.getForumSalt();
Forum f = forumFactory.createForum(name, salt);
// create local message to keep engine state
@@ -538,29 +523,24 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
Message m = clientHelper.createMessage(localGroup.getId(), now,
BdfList.of(mSalt));
BdfDictionary d = new BdfDictionary();
d.put(SESSION_ID, msg.getRaw(SESSION_ID));
d.put(STORAGE_ID, m.getId());
d.put(GROUP_ID, group.getId());
d.put(IS_SHARER, false);
d.put(STATE, AWAIT_INVITATION.getValue());
d.put(CONTACT_ID, contactId.getInt());
d.put(FORUM_ID, f.getId());
d.put(FORUM_NAME, name);
d.put(FORUM_SALT, salt);
InviteeSessionState s = new InviteeSessionState(msg.getSessionId(),
m.getId(), group.getId(),
InviteeSessionState.State.AWAIT_INVITATION, contactId,
f.getId(), f.getName(), f.getSalt());
// save local state to database
BdfDictionary d = s.toBdfDictionary();
clientHelper.addLocalMessage(txn, m, getClientId(), d, false);
return d;
return s;
}
private BdfDictionary getSessionState(Transaction txn, SessionId sessionId,
boolean warn) throws DbException, FormatException {
private ForumSharingSessionState getSessionState(Transaction txn,
SessionId sessionId, boolean warn)
throws DbException, FormatException {
try {
// we should be able to get the sharer state directly from sessionId
return clientHelper.getMessageMetadataAsDictionary(txn, sessionId);
return getSessionStateForSharer(txn, sessionId);
} catch (NoSuchMessageException e) {
// State not found directly, so iterate over all states
// to find state for invitee
@@ -570,7 +550,7 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
BdfDictionary state = m.getValue();
if (Arrays.equals(state.getRaw(SESSION_ID),
sessionId.getBytes())) {
return state;
return fromBdfDictionary(state);
}
}
if (warn && LOG.isLoggable(WARNING)) {
@@ -582,23 +562,37 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
}
}
private BdfDictionary getSessionStateForResponse(Transaction txn, Forum f)
private SharerSessionState getSessionStateForSharer(Transaction txn,
SessionId sessionId)
throws DbException, FormatException {
// we should be able to get the sharer state directly from sessionId
BdfDictionary d =
clientHelper.getMessageMetadataAsDictionary(txn, sessionId);
if (!d.getBoolean(IS_SHARER)) throw new FormatException();
return (SharerSessionState) fromBdfDictionary(d);
}
private InviteeSessionState getSessionStateForResponse(Transaction txn,
Forum f) throws DbException, FormatException {
Map<MessageId, BdfDictionary> map = clientHelper
.getMessageMetadataAsDictionary(txn, localGroup.getId());
for (Map.Entry<MessageId, BdfDictionary> m : map.entrySet()) {
BdfDictionary d = m.getValue();
try {
InviteeProtocolState state = InviteeProtocolState
.fromValue(d.getLong(STATE).intValue());
if (state == AWAIT_LOCAL_RESPONSE) {
byte[] id = d.getRaw(FORUM_ID);
if (Arrays.equals(f.getId().getBytes(), id)) {
// Note that there should always be only one session
// in this state for the same forum
return d;
}
ForumSharingSessionState s = fromBdfDictionary(d);
if (!(s instanceof InviteeSessionState)) continue;
if (!f.getId().equals(s.getForumId())) continue;
InviteeSessionState state = (InviteeSessionState) s;
if (state.getState() ==
InviteeSessionState.State.AWAIT_LOCAL_RESPONSE) {
// Note that there should always be only one session
// in this state for the same forum
return state;
}
} catch (FormatException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
@@ -607,36 +601,40 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
throw new DbException();
}
private BdfDictionary getSessionStateForLeaving(Transaction txn, Forum f,
ContactId c) throws DbException, FormatException {
private ForumSharingSessionState getSessionStateForLeaving(Transaction txn,
Forum f, ContactId c) throws DbException, FormatException {
Map<MessageId, BdfDictionary> map = clientHelper
.getMessageMetadataAsDictionary(txn, localGroup.getId());
for (Map.Entry<MessageId, BdfDictionary> m : map.entrySet()) {
BdfDictionary d = m.getValue();
try {
ForumSharingSessionState s = fromBdfDictionary(d);
// check that this session is with the right contact
if (c.getInt() != d.getLong(CONTACT_ID)) continue;
// check that a forum get be left in current session
int intState = d.getLong(STATE).intValue();
if (d.getBoolean(IS_SHARER)) {
SharerProtocolState state =
SharerProtocolState.fromValue(intState);
if (state.next(SharerAction.LOCAL_LEAVE) ==
SharerProtocolState.ERROR) continue;
} else {
InviteeProtocolState state = InviteeProtocolState
.fromValue(intState);
if (state.next(InviteeAction.LOCAL_LEAVE) ==
InviteeProtocolState.ERROR) continue;
}
if (!c.equals(s.getContactId())) continue;
// check that this state actually concerns this forum
String name = d.getString(FORUM_NAME);
byte[] salt = d.getRaw(FORUM_SALT);
if (name.equals(f.getName()) &&
Arrays.equals(salt, f.getSalt())) {
// TODO what happens when there is more than one invitation?
return d;
if (!s.getForumName().equals(f.getName()) ||
!Arrays.equals(s.getForumSalt(), f.getSalt())) {
continue;
}
// check that a forum get be left in current session
if (s instanceof SharerSessionState) {
SharerSessionState state = (SharerSessionState) s;
SharerSessionState.State nextState =
state.getState().next(Action.LOCAL_LEAVE);
if (nextState != SharerSessionState.State.ERROR) {
return state;
}
} else {
InviteeSessionState state = (InviteeSessionState) s;
InviteeSessionState.State nextState = state.getState()
.next(InviteeSessionState.Action.LOCAL_LEAVE);
if (nextState != InviteeSessionState.State.ERROR) {
return state;
}
}
} catch (FormatException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
@@ -646,20 +644,20 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
}
private void processStateUpdate(Transaction txn, MessageId messageId,
StateUpdate<BdfDictionary, BdfDictionary> result)
StateUpdate<ForumSharingSessionState, BaseMessage> result)
throws DbException, FormatException {
// perform actions based on new local state
performTasks(txn, result.localState);
// save new local state
MessageId storageId =
new MessageId(result.localState.getRaw(STORAGE_ID));
clientHelper.mergeMessageMetadata(txn, storageId, result.localState);
MessageId storageId = result.localState.getStorageId();
clientHelper.mergeMessageMetadata(txn, storageId,
result.localState.toBdfDictionary());
// send messages
for (BdfDictionary d : result.toSend) {
sendMessage(txn, d);
for (BaseMessage msg : result.toSend) {
sendMessage(txn, msg);
}
// broadcast events
@@ -677,24 +675,47 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
}
}
private void performTasks(Transaction txn, BdfDictionary localState)
private void processSharerStateUpdate(Transaction txn, MessageId messageId,
StateUpdate<SharerSessionState, BaseMessage> result)
throws DbException, FormatException {
StateUpdate<ForumSharingSessionState, BaseMessage> r =
new StateUpdate<ForumSharingSessionState, BaseMessage>(
result.deleteMessage, result.deleteState,
result.localState, result.toSend, result.toBroadcast);
processStateUpdate(txn, messageId, r);
}
private void processInviteeStateUpdate(Transaction txn, MessageId messageId,
StateUpdate<InviteeSessionState, BaseMessage> result)
throws DbException, FormatException {
StateUpdate<ForumSharingSessionState, BaseMessage> r =
new StateUpdate<ForumSharingSessionState, BaseMessage>(
result.deleteMessage, result.deleteState,
result.localState, result.toSend, result.toBroadcast);
processStateUpdate(txn, messageId, r);
}
private void performTasks(Transaction txn, ForumSharingSessionState localState)
throws FormatException, DbException {
if (!localState.containsKey(TASK)) return;
if (localState.getTask() == -1) return;
// remember task and remove it from localState
long task = localState.getLong(TASK);
localState.put(TASK, BdfDictionary.NULL_VALUE);
long task = localState.getTask();
localState.setTask(-1);
// get group ID for later
GroupId groupId = new GroupId(localState.getRaw(GROUP_ID));
GroupId groupId = localState.getGroupId();
// get contact ID for later
ContactId contactId =
new ContactId(localState.getLong(CONTACT_ID).intValue());
ContactId contactId = localState.getContactId();
// get forum for later
String name = localState.getString(FORUM_NAME);
byte[] salt = localState.getRaw(FORUM_SALT);
String name = localState.getForumName();
byte[] salt = localState.getForumSalt();
Forum f = forumFactory.createForum(name, salt);
// perform tasks
@@ -728,50 +749,23 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
}
}
private void sendMessage(Transaction txn, BdfDictionary m)
private void sendMessage(Transaction txn, BaseMessage m)
throws FormatException, DbException {
BdfList list = encodeMessage(m);
byte[] body = clientHelper.toByteArray(list);
GroupId groupId = new GroupId(m.getRaw(GROUP_ID));
Group group = db.getGroup(txn, groupId);
byte[] body = clientHelper.toByteArray(m.toBdfList());
Group group = db.getGroup(txn, m.getGroupId());
long timestamp = clock.currentTimeMillis();
// add message itself as metadata
m.put(LOCAL, true);
m.put(TIME, timestamp);
Metadata meta = metadataEncoder.encode(m);
BdfDictionary d = m.toBdfDictionary();
d.put(LOCAL, true);
d.put(TIME, timestamp);
Metadata meta = metadataEncoder.encode(d);
messageQueueManager
.sendMessage(txn, group, timestamp, body, meta);
}
private BdfList encodeMessage(BdfDictionary m) throws FormatException {
long type = m.getLong(TYPE);
BdfList list;
if (type == SHARE_MSG_TYPE_INVITATION) {
list = BdfList.of(type,
m.getRaw(SESSION_ID),
m.getString(FORUM_NAME),
m.getRaw(FORUM_SALT)
);
String msg = m.getOptionalString(INVITATION_MSG);
if (msg != null) list.add(msg);
} else if (type == SHARE_MSG_TYPE_ACCEPT) {
list = BdfList.of(type, m.getRaw(SESSION_ID));
} else if (type == SHARE_MSG_TYPE_DECLINE) {
list = BdfList.of(type, m.getRaw(SESSION_ID));
} else if (type == SHARE_MSG_TYPE_LEAVE) {
list = BdfList.of(type, m.getRaw(SESSION_ID));
} else if (type == SHARE_MSG_TYPE_ABORT) {
list = BdfList.of(type, m.getRaw(SESSION_ID));
} else {
throw new FormatException();
}
return list;
}
private Group getContactGroup(Contact c) {
return privateGroupFactory.createPrivateGroup(CLIENT_ID, c);
}
@@ -786,17 +780,18 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
private void leaveForum(Transaction txn, ContactId c, Forum f)
throws DbException, FormatException {
BdfDictionary state = getSessionStateForLeaving(txn, f, c);
BdfDictionary action = new BdfDictionary();
action.put(TYPE, SHARE_MSG_TYPE_LEAVE);
if (state.getBoolean(IS_SHARER)) {
ForumSharingSessionState state = getSessionStateForLeaving(txn, f, c);
if (state instanceof SharerSessionState) {
Action action = Action.LOCAL_LEAVE;
SharerEngine engine = new SharerEngine();
processStateUpdate(txn, null,
engine.onLocalAction(state, action));
processSharerStateUpdate(txn, null,
engine.onLocalAction((SharerSessionState) state, action));
} else {
InviteeSessionState.Action action =
InviteeSessionState.Action.LOCAL_LEAVE;
InviteeEngine engine = new InviteeEngine(forumFactory);
processStateUpdate(txn, null,
engine.onLocalAction(state, action));
processInviteeStateUpdate(txn, null,
engine.onLocalAction((InviteeSessionState) state, action));
}
}

View File

@@ -0,0 +1,119 @@
package org.briarproject.forum;
import org.briarproject.api.FormatException;
import org.briarproject.api.clients.SessionId;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.MessageId;
import static org.briarproject.api.forum.ForumConstants.CONTACT_ID;
import static org.briarproject.api.forum.ForumConstants.FORUM_ID;
import static org.briarproject.api.forum.ForumConstants.FORUM_NAME;
import static org.briarproject.api.forum.ForumConstants.FORUM_SALT;
import static org.briarproject.api.forum.ForumConstants.GROUP_ID;
import static org.briarproject.api.forum.ForumConstants.IS_SHARER;
import static org.briarproject.api.forum.ForumConstants.SESSION_ID;
import static org.briarproject.api.forum.ForumConstants.STATE;
import static org.briarproject.api.forum.ForumConstants.STORAGE_ID;
// This class is not thread-safe
public abstract class ForumSharingSessionState {
private final SessionId sessionId;
private final MessageId storageId;
private final GroupId groupId;
private final ContactId contactId;
private final GroupId forumId;
private final String forumName;
private final byte[] forumSalt;
private int task = -1; // TODO get rid of task, see #376
public ForumSharingSessionState(SessionId sessionId, MessageId storageId,
GroupId groupId, ContactId contactId, GroupId forumId,
String forumName, byte[] forumSalt) {
this.sessionId = sessionId;
this.storageId = storageId;
this.groupId = groupId;
this.contactId = contactId;
this.forumId = forumId;
this.forumName = forumName;
this.forumSalt = forumSalt;
}
public static ForumSharingSessionState fromBdfDictionary(BdfDictionary d)
throws FormatException{
SessionId sessionId = new SessionId(d.getRaw(SESSION_ID));
MessageId messageId = new MessageId(d.getRaw(STORAGE_ID));
GroupId groupId = new GroupId(d.getRaw(GROUP_ID));
ContactId contactId = new ContactId(d.getLong(CONTACT_ID).intValue());
GroupId forumId = new GroupId(d.getRaw(FORUM_ID));
String forumName = d.getString(FORUM_NAME);
byte[] forumSalt = d.getRaw(FORUM_SALT);
int intState = d.getLong(STATE).intValue();
if (d.getBoolean(IS_SHARER)) {
SharerSessionState.State state =
SharerSessionState.State.fromValue(intState);
return new SharerSessionState(sessionId, messageId, groupId, state,
contactId, forumId, forumName, forumSalt);
} else {
InviteeSessionState.State state =
InviteeSessionState.State.fromValue(intState);
return new InviteeSessionState(sessionId, messageId, groupId, state,
contactId, forumId, forumName, forumSalt);
}
}
public BdfDictionary toBdfDictionary() {
BdfDictionary d = new BdfDictionary();
d.put(SESSION_ID, getSessionId());
d.put(STORAGE_ID, getStorageId());
d.put(GROUP_ID, getGroupId());
d.put(CONTACT_ID, getContactId().getInt());
d.put(FORUM_ID, getForumId());
d.put(FORUM_NAME, getForumName());
d.put(FORUM_SALT, getForumSalt());
return d;
}
public SessionId getSessionId() {
return sessionId;
}
public MessageId getStorageId() {
return storageId;
}
public GroupId getGroupId() {
return groupId;
}
public ContactId getContactId() {
return contactId;
}
public GroupId getForumId() {
return forumId;
}
public String getForumName() {
return forumName;
}
public byte[] getForumSalt() {
return forumSalt;
}
public void setTask(int task) {
this.task = task;
}
public int getTask() {
return task;
}
}

View File

@@ -73,7 +73,6 @@ class ForumSharingValidator extends BdfMessageValidator {
// Return the metadata
d.put(TYPE, type);
d.put(SESSION_ID, id);
d.put(GROUP_ID, m.getGroupId());
d.put(LOCAL, false);
d.put(TIME, m.getTimestamp());
return d;

View File

@@ -3,51 +3,42 @@ package org.briarproject.forum;
import org.briarproject.api.FormatException;
import org.briarproject.api.clients.ProtocolEngine;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.data.BdfEntry;
import org.briarproject.api.event.Event;
import org.briarproject.api.event.ForumInvitationReceivedEvent;
import org.briarproject.api.forum.Forum;
import org.briarproject.api.forum.ForumFactory;
import org.briarproject.api.forum.InviteeAction;
import org.briarproject.api.forum.InviteeProtocolState;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.logging.Logger;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static org.briarproject.api.forum.ForumConstants.CONTACT_ID;
import static org.briarproject.api.forum.ForumConstants.FORUM_NAME;
import static org.briarproject.api.forum.ForumConstants.FORUM_SALT;
import static org.briarproject.api.forum.ForumConstants.GROUP_ID;
import static org.briarproject.api.forum.ForumConstants.SESSION_ID;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_ABORT;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_ACCEPT;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_DECLINE;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_INVITATION;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_LEAVE;
import static org.briarproject.api.forum.ForumConstants.STATE;
import static org.briarproject.api.forum.ForumConstants.TASK;
import static org.briarproject.api.forum.ForumConstants.TASK_ADD_FORUM_TO_LIST_SHARED_WITH_US;
import static org.briarproject.api.forum.ForumConstants.TASK_ADD_SHARED_FORUM;
import static org.briarproject.api.forum.ForumConstants.TASK_REMOVE_FORUM_FROM_LIST_SHARED_WITH_US;
import static org.briarproject.api.forum.ForumConstants.TASK_UNSHARE_FORUM_SHARED_WITH_US;
import static org.briarproject.api.forum.ForumConstants.TYPE;
import static org.briarproject.api.forum.InviteeAction.LOCAL_ABORT;
import static org.briarproject.api.forum.InviteeAction.LOCAL_ACCEPT;
import static org.briarproject.api.forum.InviteeAction.LOCAL_DECLINE;
import static org.briarproject.api.forum.InviteeAction.LOCAL_LEAVE;
import static org.briarproject.api.forum.InviteeAction.REMOTE_INVITATION;
import static org.briarproject.api.forum.InviteeAction.REMOTE_LEAVE;
import static org.briarproject.api.forum.InviteeProtocolState.ERROR;
import static org.briarproject.api.forum.InviteeProtocolState.FINISHED;
import static org.briarproject.api.forum.InviteeProtocolState.LEFT;
import static org.briarproject.api.forum.ForumSharingMessage.SimpleMessage;
import static org.briarproject.api.forum.ForumSharingMessage.BaseMessage;
import static org.briarproject.forum.InviteeSessionState.Action;
import static org.briarproject.forum.InviteeSessionState.Action.LOCAL_ABORT;
import static org.briarproject.forum.InviteeSessionState.Action.LOCAL_ACCEPT;
import static org.briarproject.forum.InviteeSessionState.Action.LOCAL_DECLINE;
import static org.briarproject.forum.InviteeSessionState.Action.LOCAL_LEAVE;
import static org.briarproject.forum.InviteeSessionState.Action.REMOTE_INVITATION;
import static org.briarproject.forum.InviteeSessionState.Action.REMOTE_LEAVE;
import static org.briarproject.forum.InviteeSessionState.State;
import static org.briarproject.forum.InviteeSessionState.State.ERROR;
import static org.briarproject.forum.InviteeSessionState.State.FINISHED;
import static org.briarproject.forum.InviteeSessionState.State.LEFT;
public class InviteeEngine
implements ProtocolEngine<BdfDictionary, BdfDictionary, BdfDictionary> {
implements ProtocolEngine<Action, InviteeSessionState, BaseMessage> {
private final ForumFactory forumFactory;
private static final Logger LOG =
@@ -58,16 +49,13 @@ public class InviteeEngine
}
@Override
public StateUpdate<BdfDictionary, BdfDictionary> onLocalAction(
BdfDictionary localState, BdfDictionary localAction) {
public StateUpdate<InviteeSessionState, BaseMessage> onLocalAction(
InviteeSessionState localState, Action action) {
try {
InviteeProtocolState currentState =
getState(localState.getLong(STATE));
long type = localAction.getLong(TYPE);
InviteeAction action = InviteeAction.getLocal(type);
InviteeProtocolState nextState = currentState.next(action);
localState.put(STATE, nextState.getValue());
State currentState = localState.getState();
State nextState = currentState.next(action);
localState.setState(nextState);
if (action == LOCAL_ABORT && currentState != ERROR) {
return abortSession(currentState, localState);
@@ -80,37 +68,34 @@ public class InviteeEngine
}
return noUpdate(localState, true);
}
List<BdfDictionary> messages;
List<BaseMessage> messages;
List<Event> events = Collections.emptyList();
if (action == LOCAL_ACCEPT || action == LOCAL_DECLINE) {
BdfDictionary msg = BdfDictionary.of(
new BdfEntry(SESSION_ID, localState.getRaw(SESSION_ID)),
new BdfEntry(GROUP_ID, localState.getRaw(GROUP_ID))
);
BaseMessage msg;
if (action == LOCAL_ACCEPT) {
localState.put(TASK, TASK_ADD_SHARED_FORUM);
msg.put(TYPE, SHARE_MSG_TYPE_ACCEPT);
localState.setTask(TASK_ADD_SHARED_FORUM);
msg = new SimpleMessage(SHARE_MSG_TYPE_ACCEPT,
localState.getGroupId(), localState.getSessionId());
} else {
localState.put(TASK,
localState.setTask(
TASK_REMOVE_FORUM_FROM_LIST_SHARED_WITH_US);
msg.put(TYPE, SHARE_MSG_TYPE_DECLINE);
msg = new SimpleMessage(SHARE_MSG_TYPE_DECLINE,
localState.getGroupId(), localState.getSessionId());
}
messages = Collections.singletonList(msg);
logLocalAction(currentState, localState, msg);
}
else if (action == LOCAL_LEAVE) {
BdfDictionary msg = new BdfDictionary();
msg.put(TYPE, SHARE_MSG_TYPE_LEAVE);
msg.put(SESSION_ID, localState.getRaw(SESSION_ID));
msg.put(GROUP_ID, localState.getRaw(GROUP_ID));
BaseMessage msg = new SimpleMessage(SHARE_MSG_TYPE_LEAVE,
localState.getGroupId(), localState.getSessionId());
messages = Collections.singletonList(msg);
logLocalAction(currentState, localState, msg);
}
else {
throw new IllegalArgumentException("Unknown Local Action");
}
return new StateUpdate<BdfDictionary, BdfDictionary>(false,
return new StateUpdate<InviteeSessionState, BaseMessage>(false,
false, localState, messages, events);
} catch (FormatException e) {
throw new IllegalArgumentException(e);
@@ -118,18 +103,16 @@ public class InviteeEngine
}
@Override
public StateUpdate<BdfDictionary, BdfDictionary> onMessageReceived(
BdfDictionary localState, BdfDictionary msg) {
public StateUpdate<InviteeSessionState, BaseMessage> onMessageReceived(
InviteeSessionState localState, BaseMessage msg) {
try {
InviteeProtocolState currentState =
getState(localState.getLong(STATE));
long type = msg.getLong(TYPE);
InviteeAction action = InviteeAction.getRemote(type);
InviteeProtocolState nextState = currentState.next(action);
localState.put(STATE, nextState.getValue());
State currentState = localState.getState();
Action action = Action.getRemote(msg.getType());
State nextState = currentState.next(action);
localState.setState(nextState);
logMessageReceived(currentState, nextState, type, msg);
logMessageReceived(currentState, nextState, msg.getType(), msg);
if (nextState == ERROR) {
if (currentState != ERROR) {
@@ -139,7 +122,7 @@ public class InviteeEngine
}
}
List<BdfDictionary> messages = Collections.emptyList();
List<BaseMessage> messages = Collections.emptyList();
List<Event> events = Collections.emptyList();
boolean deleteMsg = false;
@@ -149,7 +132,7 @@ public class InviteeEngine
}
// the sharer left the forum she had shared with us
else if (action == REMOTE_LEAVE && currentState == FINISHED) {
localState.put(TASK, TASK_UNSHARE_FORUM_SHARED_WITH_US);
localState.setTask(TASK_UNSHARE_FORUM_SHARED_WITH_US);
}
else if (currentState == FINISHED) {
// ignore and delete messages coming in while in that state
@@ -158,74 +141,65 @@ public class InviteeEngine
}
// the sharer left the forum before we couldn't even respond
else if (action == REMOTE_LEAVE) {
localState.put(TASK, TASK_REMOVE_FORUM_FROM_LIST_SHARED_WITH_US);
localState.setTask(TASK_REMOVE_FORUM_FROM_LIST_SHARED_WITH_US);
}
// we have just received our invitation
else if (action == REMOTE_INVITATION) {
localState.put(TASK, TASK_ADD_FORUM_TO_LIST_SHARED_WITH_US);
Forum forum = forumFactory
.createForum(localState.getString(FORUM_NAME),
localState.getRaw(FORUM_SALT));
ContactId contactId = new ContactId(
localState.getLong(CONTACT_ID).intValue());
.createForum(localState.getForumName(),
localState.getForumSalt());
localState.setTask(TASK_ADD_FORUM_TO_LIST_SHARED_WITH_US);
ContactId contactId = localState.getContactId();
Event event = new ForumInvitationReceivedEvent(forum, contactId);
events = Collections.singletonList(event);
}
else {
throw new IllegalArgumentException("Bad state");
}
return new StateUpdate<BdfDictionary, BdfDictionary>(deleteMsg,
return new StateUpdate<InviteeSessionState, BaseMessage>(deleteMsg,
false, localState, messages, events);
} catch (FormatException e) {
throw new IllegalArgumentException(e);
}
}
private void logLocalAction(InviteeProtocolState state,
BdfDictionary localState, BdfDictionary msg) {
private void logLocalAction(State state,
InviteeSessionState localState, BaseMessage msg) {
if (!LOG.isLoggable(INFO)) return;
String a = "response";
if (msg.getLong(TYPE, -1L) == SHARE_MSG_TYPE_LEAVE) a = "leave";
if (msg.getType() == SHARE_MSG_TYPE_LEAVE) a = "leave";
try {
LOG.info("Sending " + a + " in state " + state.name() +
" with session ID " +
Arrays.hashCode(msg.getRaw(SESSION_ID)) + " in group " +
Arrays.hashCode(msg.getRaw(GROUP_ID)) + ". " +
"Moving on to state " +
getState(localState.getLong(STATE)).name()
);
} catch (FormatException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
LOG.info("Sending " + a + " in state " + state.name() +
" with session ID " +
msg.getSessionId().hashCode() + " in group " +
msg.getGroupId().hashCode() + ". " +
"Moving on to state " + localState.getState().name()
);
}
private void logMessageReceived(InviteeProtocolState currentState,
InviteeProtocolState nextState, long type, BdfDictionary msg) {
private void logMessageReceived(State currentState, State nextState,
long type, BaseMessage msg) {
if (!LOG.isLoggable(INFO)) return;
try {
String t = "unknown";
if (type == SHARE_MSG_TYPE_INVITATION) t = "INVITE";
else if (type == SHARE_MSG_TYPE_LEAVE) t = "LEAVE";
else if (type == SHARE_MSG_TYPE_ABORT) t = "ABORT";
String t = "unknown";
if (type == SHARE_MSG_TYPE_INVITATION) t = "INVITE";
else if (type == SHARE_MSG_TYPE_LEAVE) t = "LEAVE";
else if (type == SHARE_MSG_TYPE_ABORT) t = "ABORT";
LOG.info("Received " + t + " in state " + currentState.name() +
" with session ID " +
Arrays.hashCode(msg.getRaw(SESSION_ID)) + " in group " +
Arrays.hashCode(msg.getRaw(GROUP_ID)) + ". " +
"Moving on to state " + nextState.name()
);
} catch (FormatException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
LOG.info("Received " + t + " in state " + currentState.name() +
" with session ID " +
msg.getSessionId().hashCode() + " in group " +
msg.getGroupId().hashCode() + ". " +
"Moving on to state " + nextState.name()
);
}
@Override
public StateUpdate<BdfDictionary, BdfDictionary> onMessageDelivered(
BdfDictionary localState, BdfDictionary delivered) {
public StateUpdate<InviteeSessionState, BaseMessage> onMessageDelivered(
InviteeSessionState localState, BaseMessage delivered) {
try {
return noUpdate(localState, false);
} catch (FormatException e) {
@@ -234,38 +208,32 @@ public class InviteeEngine
}
}
private InviteeProtocolState getState(Long state) {
return InviteeProtocolState.fromValue(state.intValue());
}
private StateUpdate<BdfDictionary, BdfDictionary> abortSession(
InviteeProtocolState currentState, BdfDictionary localState)
private StateUpdate<InviteeSessionState, BaseMessage> abortSession(
State currentState, InviteeSessionState localState)
throws FormatException {
if (LOG.isLoggable(WARNING)) {
LOG.warning("Aborting protocol session " +
Arrays.hashCode(localState.getRaw(SESSION_ID)) +
localState.getSessionId().hashCode() +
" in state " + currentState.name());
}
localState.put(STATE, ERROR.getValue());
BdfDictionary msg = new BdfDictionary();
msg.put(TYPE, SHARE_MSG_TYPE_ABORT);
msg.put(SESSION_ID, localState.getRaw(SESSION_ID));
msg.put(GROUP_ID, localState.getRaw(GROUP_ID));
List<BdfDictionary> messages = Collections.singletonList(msg);
localState.setState(ERROR);
BaseMessage msg =
new SimpleMessage(SHARE_MSG_TYPE_ABORT, localState.getGroupId(),
localState.getSessionId());
List<BaseMessage> messages = Collections.singletonList(msg);
List<Event> events = Collections.emptyList();
return new StateUpdate<BdfDictionary, BdfDictionary>(false, false,
return new StateUpdate<InviteeSessionState, BaseMessage>(false, false,
localState, messages, events);
}
private StateUpdate<BdfDictionary, BdfDictionary> noUpdate(
BdfDictionary localState, boolean delete) throws FormatException {
private StateUpdate<InviteeSessionState, BaseMessage> noUpdate(
InviteeSessionState localState, boolean delete) throws FormatException {
return new StateUpdate<BdfDictionary, BdfDictionary>(delete, false,
localState, Collections.<BdfDictionary>emptyList(),
return new StateUpdate<InviteeSessionState, BaseMessage>(delete, false,
localState, Collections.<BaseMessage>emptyList(),
Collections.<Event>emptyList());
}
}

View File

@@ -0,0 +1,120 @@
package org.briarproject.forum;
import org.briarproject.api.clients.SessionId;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.MessageId;
import static org.briarproject.api.forum.ForumConstants.IS_SHARER;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_ABORT;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_INVITATION;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_LEAVE;
import static org.briarproject.api.forum.ForumConstants.STATE;
import static org.briarproject.forum.InviteeSessionState.Action.LOCAL_ACCEPT;
import static org.briarproject.forum.InviteeSessionState.Action.LOCAL_DECLINE;
import static org.briarproject.forum.InviteeSessionState.Action.LOCAL_LEAVE;
import static org.briarproject.forum.InviteeSessionState.Action.REMOTE_INVITATION;
import static org.briarproject.forum.InviteeSessionState.Action.REMOTE_LEAVE;
// This class is not thread-safe
public class InviteeSessionState extends ForumSharingSessionState {
private State state;
public InviteeSessionState(SessionId sessionId, MessageId storageId,
GroupId groupId, State state, ContactId contactId, GroupId forumId,
String forumName, byte[] forumSalt) {
super(sessionId, storageId, groupId, contactId, forumId, forumName,
forumSalt);
this.state = state;
}
public BdfDictionary toBdfDictionary() {
BdfDictionary d = super.toBdfDictionary();
d.put(STATE, getState().getValue());
d.put(IS_SHARER, false);
return d;
}
public void setState(State state) {
this.state = state;
}
public State getState() {
return state;
}
public enum State {
ERROR(0),
AWAIT_INVITATION(1) {
@Override
public State next(Action a) {
if (a == REMOTE_INVITATION) return AWAIT_LOCAL_RESPONSE;
return ERROR;
}
},
AWAIT_LOCAL_RESPONSE(2) {
@Override
public State next(Action a) {
if (a == LOCAL_ACCEPT || a == LOCAL_DECLINE) return FINISHED;
if (a == REMOTE_LEAVE) return LEFT;
return ERROR;
}
},
FINISHED(3) {
@Override
public State next(Action a) {
if (a == LOCAL_LEAVE || a == REMOTE_LEAVE) return LEFT;
return FINISHED;
}
},
LEFT(4) {
@Override
public State next(Action a) {
if (a == LOCAL_LEAVE) return ERROR;
return LEFT;
}
};
private final int value;
State(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public static State fromValue(int value) {
for (State s : values()) {
if (s.value == value) return s;
}
throw new IllegalArgumentException();
}
public State next(Action a) {
return this;
}
}
public enum Action {
LOCAL_ACCEPT,
LOCAL_DECLINE,
LOCAL_LEAVE,
LOCAL_ABORT,
REMOTE_INVITATION,
REMOTE_LEAVE,
REMOTE_ABORT;
public static Action getRemote(long type) {
if (type == SHARE_MSG_TYPE_INVITATION) return REMOTE_INVITATION;
if (type == SHARE_MSG_TYPE_LEAVE) return REMOTE_LEAVE;
if (type == SHARE_MSG_TYPE_ABORT) return REMOTE_ABORT;
return null;
}
}
}

View File

@@ -3,64 +3,52 @@ package org.briarproject.forum;
import org.briarproject.api.FormatException;
import org.briarproject.api.clients.ProtocolEngine;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.event.Event;
import org.briarproject.api.event.ForumInvitationResponseReceivedEvent;
import org.briarproject.api.forum.SharerAction;
import org.briarproject.api.forum.SharerProtocolState;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.logging.Logger;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static org.briarproject.api.forum.ForumConstants.CONTACT_ID;
import static org.briarproject.api.forum.ForumConstants.FORUM_NAME;
import static org.briarproject.api.forum.ForumConstants.FORUM_SALT;
import static org.briarproject.api.forum.ForumConstants.GROUP_ID;
import static org.briarproject.api.forum.ForumConstants.INVITATION_MSG;
import static org.briarproject.api.forum.ForumConstants.SESSION_ID;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_ABORT;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_ACCEPT;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_DECLINE;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_INVITATION;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_LEAVE;
import static org.briarproject.api.forum.ForumConstants.STATE;
import static org.briarproject.api.forum.ForumConstants.TASK;
import static org.briarproject.api.forum.ForumConstants.TASK_ADD_FORUM_TO_LIST_TO_BE_SHARED_BY_US;
import static org.briarproject.api.forum.ForumConstants.TASK_REMOVE_FORUM_FROM_LIST_TO_BE_SHARED_BY_US;
import static org.briarproject.api.forum.ForumConstants.TASK_SHARE_FORUM;
import static org.briarproject.api.forum.ForumConstants.TASK_UNSHARE_FORUM_SHARED_BY_US;
import static org.briarproject.api.forum.ForumConstants.TYPE;
import static org.briarproject.api.forum.SharerAction.LOCAL_ABORT;
import static org.briarproject.api.forum.SharerAction.LOCAL_INVITATION;
import static org.briarproject.api.forum.SharerAction.LOCAL_LEAVE;
import static org.briarproject.api.forum.SharerAction.REMOTE_ACCEPT;
import static org.briarproject.api.forum.SharerAction.REMOTE_DECLINE;
import static org.briarproject.api.forum.SharerAction.REMOTE_LEAVE;
import static org.briarproject.api.forum.SharerProtocolState.ERROR;
import static org.briarproject.api.forum.SharerProtocolState.FINISHED;
import static org.briarproject.api.forum.SharerProtocolState.LEFT;
import static org.briarproject.api.forum.ForumSharingMessage.BaseMessage;
import static org.briarproject.api.forum.ForumSharingMessage.Invitation;
import static org.briarproject.api.forum.ForumSharingMessage.SimpleMessage;
import static org.briarproject.forum.SharerSessionState.Action;
import static org.briarproject.forum.SharerSessionState.Action.LOCAL_ABORT;
import static org.briarproject.forum.SharerSessionState.Action.LOCAL_INVITATION;
import static org.briarproject.forum.SharerSessionState.Action.LOCAL_LEAVE;
import static org.briarproject.forum.SharerSessionState.Action.REMOTE_ACCEPT;
import static org.briarproject.forum.SharerSessionState.Action.REMOTE_DECLINE;
import static org.briarproject.forum.SharerSessionState.Action.REMOTE_LEAVE;
import static org.briarproject.forum.SharerSessionState.State;
import static org.briarproject.forum.SharerSessionState.State.ERROR;
import static org.briarproject.forum.SharerSessionState.State.FINISHED;
import static org.briarproject.forum.SharerSessionState.State.LEFT;
public class SharerEngine
implements ProtocolEngine<BdfDictionary, BdfDictionary, BdfDictionary> {
implements ProtocolEngine<Action, SharerSessionState, BaseMessage> {
private static final Logger LOG =
Logger.getLogger(SharerEngine.class.getName());
@Override
public StateUpdate<BdfDictionary, BdfDictionary> onLocalAction(
BdfDictionary localState, BdfDictionary localAction) {
public StateUpdate<SharerSessionState, BaseMessage> onLocalAction(
SharerSessionState localState, Action action) {
try {
SharerProtocolState currentState =
getState(localState.getLong(STATE));
long type = localAction.getLong(TYPE);
SharerAction action = SharerAction.getLocal(type);
SharerProtocolState nextState = currentState.next(action);
localState.put(STATE, nextState.getValue());
State currentState = localState.getState();
State nextState = currentState.next(action);
localState.setState(nextState);
if (action == LOCAL_ABORT && currentState != ERROR) {
return abortSession(currentState, localState);
@@ -73,38 +61,29 @@ public class SharerEngine
}
return noUpdate(localState, true);
}
List<BdfDictionary> messages;
List<BaseMessage> messages;
List<Event> events = Collections.emptyList();
if (action == LOCAL_INVITATION) {
BdfDictionary msg = new BdfDictionary();
msg.put(TYPE, SHARE_MSG_TYPE_INVITATION);
msg.put(SESSION_ID, localState.getRaw(SESSION_ID));
msg.put(GROUP_ID, localState.getRaw(GROUP_ID));
msg.put(FORUM_NAME, localState.getString(FORUM_NAME));
msg.put(FORUM_SALT, localState.getRaw(FORUM_SALT));
if (localAction.containsKey(INVITATION_MSG)) {
msg.put(INVITATION_MSG,
localAction.getString(INVITATION_MSG));
}
BaseMessage msg = new Invitation(localState.getGroupId(),
localState.getSessionId(), localState.getForumName(),
localState.getForumSalt(), localState.getMessage());
messages = Collections.singletonList(msg);
logLocalAction(currentState, localState, msg);
logLocalAction(currentState, nextState, msg);
// remember that we offered to share this forum
localState.put(TASK, TASK_ADD_FORUM_TO_LIST_TO_BE_SHARED_BY_US);
localState.setTask(TASK_ADD_FORUM_TO_LIST_TO_BE_SHARED_BY_US);
}
else if (action == LOCAL_LEAVE) {
BdfDictionary msg = new BdfDictionary();
msg.put(TYPE, SHARE_MSG_TYPE_LEAVE);
msg.put(SESSION_ID, localState.getRaw(SESSION_ID));
msg.put(GROUP_ID, localState.getRaw(GROUP_ID));
BaseMessage msg = new SimpleMessage(SHARE_MSG_TYPE_LEAVE,
localState.getGroupId(), localState.getSessionId());
messages = Collections.singletonList(msg);
logLocalAction(currentState, localState, msg);
logLocalAction(currentState, nextState, msg);
}
else {
throw new IllegalArgumentException("Unknown Local Action");
}
return new StateUpdate<BdfDictionary, BdfDictionary>(false,
return new StateUpdate<SharerSessionState, BaseMessage>(false,
false, localState, messages, events);
} catch (FormatException e) {
throw new IllegalArgumentException(e);
@@ -112,18 +91,16 @@ public class SharerEngine
}
@Override
public StateUpdate<BdfDictionary, BdfDictionary> onMessageReceived(
BdfDictionary localState, BdfDictionary msg) {
public StateUpdate<SharerSessionState, BaseMessage> onMessageReceived(
SharerSessionState localState, BaseMessage msg) {
try {
SharerProtocolState currentState =
getState(localState.getLong(STATE));
long type = msg.getLong(TYPE);
SharerAction action = SharerAction.getRemote(type);
SharerProtocolState nextState = currentState.next(action);
localState.put(STATE, nextState.getValue());
State currentState = localState.getState();
Action action = Action.getRemote(msg.getType());
State nextState = currentState.next(action);
localState.setState(nextState);
logMessageReceived(currentState, nextState, type, msg);
logMessageReceived(currentState, nextState, msg.getType(), msg);
if (nextState == ERROR) {
if (currentState != ERROR) {
@@ -132,7 +109,7 @@ public class SharerEngine
return noUpdate(localState, true);
}
}
List<BdfDictionary> messages = Collections.emptyList();
List<BaseMessage> messages = Collections.emptyList();
List<Event> events = Collections.emptyList();
boolean deleteMsg = false;
@@ -141,7 +118,7 @@ public class SharerEngine
deleteMsg = true;
}
else if (action == REMOTE_LEAVE) {
localState.put(TASK, TASK_UNSHARE_FORUM_SHARED_BY_US);
localState.setTask(TASK_UNSHARE_FORUM_SHARED_BY_US);
}
else if (currentState == FINISHED) {
// ignore and delete messages coming in while in that state
@@ -151,74 +128,65 @@ public class SharerEngine
// we have sent our invitation and just got a response
else if (action == REMOTE_ACCEPT || action == REMOTE_DECLINE) {
if (action == REMOTE_ACCEPT) {
localState.put(TASK, TASK_SHARE_FORUM);
localState.setTask(TASK_SHARE_FORUM);
} else {
// this ensures that the forum can be shared again
localState.put(TASK,
localState.setTask(
TASK_REMOVE_FORUM_FROM_LIST_TO_BE_SHARED_BY_US);
}
String name = localState.getString(FORUM_NAME);
ContactId c = new ContactId(
localState.getLong(CONTACT_ID).intValue());
String name = localState.getForumName();
ContactId c = localState.getContactId();
Event event = new ForumInvitationResponseReceivedEvent(name, c);
events = Collections.singletonList(event);
}
else {
throw new IllegalArgumentException("Bad state");
}
return new StateUpdate<BdfDictionary, BdfDictionary>(deleteMsg,
return new StateUpdate<SharerSessionState, BaseMessage>(deleteMsg,
false, localState, messages, events);
} catch (FormatException e) {
throw new IllegalArgumentException(e);
}
}
private void logLocalAction(SharerProtocolState state,
BdfDictionary localState, BdfDictionary msg) {
private void logLocalAction(State currentState, State nextState,
BaseMessage msg) {
if (!LOG.isLoggable(INFO)) return;
String a = "invitation";
if (msg.getLong(TYPE, -1L) == SHARE_MSG_TYPE_LEAVE) a = "leave";
if (msg.getType() == SHARE_MSG_TYPE_LEAVE) a = "leave";
try {
LOG.info("Sending " + a + " in state " + state.name() +
" with session ID " +
Arrays.hashCode(msg.getRaw(SESSION_ID)) + " in group " +
Arrays.hashCode(msg.getRaw(GROUP_ID)) + ". " +
"Moving on to state " +
getState(localState.getLong(STATE)).name()
);
} catch (FormatException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
LOG.info("Sending " + a + " in state " + currentState.name() +
" with session ID " +
msg.getSessionId().hashCode() + " in group " +
msg.getGroupId().hashCode() + ". " +
"Moving on to state " + nextState.name()
);
}
private void logMessageReceived(SharerProtocolState currentState,
SharerProtocolState nextState, long type, BdfDictionary msg) {
private void logMessageReceived(State currentState, State nextState,
long type, BaseMessage msg) {
if (!LOG.isLoggable(INFO)) return;
try {
String t = "unknown";
if (type == SHARE_MSG_TYPE_ACCEPT) t = "ACCEPT";
else if (type == SHARE_MSG_TYPE_DECLINE) t = "DECLINE";
else if (type == SHARE_MSG_TYPE_LEAVE) t = "LEAVE";
else if (type == SHARE_MSG_TYPE_ABORT) t = "ABORT";
String t = "unknown";
if (type == SHARE_MSG_TYPE_ACCEPT) t = "ACCEPT";
else if (type == SHARE_MSG_TYPE_DECLINE) t = "DECLINE";
else if (type == SHARE_MSG_TYPE_LEAVE) t = "LEAVE";
else if (type == SHARE_MSG_TYPE_ABORT) t = "ABORT";
LOG.info("Received " + t + " in state " + currentState.name() +
" with session ID " +
Arrays.hashCode(msg.getRaw(SESSION_ID)) + " in group " +
Arrays.hashCode(msg.getRaw(GROUP_ID)) + ". " +
"Moving on to state " + nextState.name()
);
} catch (FormatException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
LOG.info("Received " + t + " in state " + currentState.name() +
" with session ID " +
msg.getSessionId().hashCode() + " in group " +
msg.getGroupId().hashCode() + ". " +
"Moving on to state " + nextState.name()
);
}
@Override
public StateUpdate<BdfDictionary, BdfDictionary> onMessageDelivered(
BdfDictionary localState, BdfDictionary delivered) {
public StateUpdate<SharerSessionState, BaseMessage> onMessageDelivered(
SharerSessionState localState, BaseMessage delivered) {
try {
return noUpdate(localState, false);
} catch (FormatException e) {
@@ -227,38 +195,34 @@ public class SharerEngine
}
}
private SharerProtocolState getState(Long state) {
return SharerProtocolState.fromValue(state.intValue());
}
private StateUpdate<BdfDictionary, BdfDictionary> abortSession(
SharerProtocolState currentState, BdfDictionary localState)
private StateUpdate<SharerSessionState, BaseMessage> abortSession(
State currentState, SharerSessionState localState)
throws FormatException {
if (LOG.isLoggable(WARNING)) {
LOG.warning("Aborting protocol session " +
Arrays.hashCode(localState.getRaw(SESSION_ID)) +
localState.getSessionId().hashCode() +
" in state " + currentState.name());
}
localState.put(STATE, ERROR.getValue());
BdfDictionary msg = new BdfDictionary();
msg.put(TYPE, SHARE_MSG_TYPE_ABORT);
msg.put(SESSION_ID, localState.getRaw(SESSION_ID));
msg.put(GROUP_ID, localState.getRaw(GROUP_ID));
List<BdfDictionary> messages = Collections.singletonList(msg);
localState.setState(ERROR);
BaseMessage msg = new SimpleMessage(SHARE_MSG_TYPE_ABORT,
localState.getGroupId(), localState.getSessionId());
List<BaseMessage> messages = Collections.singletonList(msg);
List<Event> events = Collections.emptyList();
return new StateUpdate<BdfDictionary, BdfDictionary>(false, false,
return new StateUpdate<SharerSessionState, BaseMessage>(false, false,
localState, messages, events);
}
private StateUpdate<BdfDictionary, BdfDictionary> noUpdate(
BdfDictionary localState, boolean delete) throws FormatException {
private StateUpdate<SharerSessionState, BaseMessage> noUpdate(
SharerSessionState localState, boolean delete)
throws FormatException {
return new StateUpdate<BdfDictionary, BdfDictionary>(delete, false,
localState, Collections.<BdfDictionary>emptyList(),
return new StateUpdate<SharerSessionState, BaseMessage>(delete, false,
localState, Collections.<BaseMessage>emptyList(),
Collections.<Event>emptyList());
}
}

View File

@@ -0,0 +1,131 @@
package org.briarproject.forum;
import org.briarproject.api.clients.SessionId;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.MessageId;
import static org.briarproject.api.forum.ForumConstants.IS_SHARER;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_ABORT;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_ACCEPT;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_DECLINE;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_LEAVE;
import static org.briarproject.api.forum.ForumConstants.STATE;
import static org.briarproject.forum.SharerSessionState.Action.LOCAL_INVITATION;
import static org.briarproject.forum.SharerSessionState.Action.LOCAL_LEAVE;
import static org.briarproject.forum.SharerSessionState.Action.REMOTE_ACCEPT;
import static org.briarproject.forum.SharerSessionState.Action.REMOTE_DECLINE;
import static org.briarproject.forum.SharerSessionState.Action.REMOTE_LEAVE;
// This class is not thread-safe
public class SharerSessionState extends ForumSharingSessionState {
private State state;
private String msg = null;
public SharerSessionState(SessionId sessionId, MessageId storageId,
GroupId groupId, State state, ContactId contactId, GroupId forumId,
String forumName, byte[] forumSalt) {
super(sessionId, storageId, groupId, contactId, forumId, forumName,
forumSalt);
this.state = state;
}
public BdfDictionary toBdfDictionary() {
BdfDictionary d = super.toBdfDictionary();
d.put(STATE, getState().getValue());
d.put(IS_SHARER, true);
return d;
}
public void setState(State state) {
this.state = state;
}
public State getState() {
return state;
}
public void setMessage(String msg) {
this.msg = msg;
}
public String getMessage() {
return this.msg;
}
public enum State {
ERROR(0),
PREPARE_INVITATION(1) {
@Override
public State next(Action a) {
if (a == LOCAL_INVITATION) return AWAIT_RESPONSE;
return ERROR;
}
},
AWAIT_RESPONSE(2) {
@Override
public State next(Action a) {
if (a == REMOTE_ACCEPT || a == REMOTE_DECLINE) return FINISHED;
if (a == LOCAL_LEAVE) return LEFT;
return ERROR;
}
},
FINISHED(3) {
@Override
public State next(Action a) {
if (a == LOCAL_LEAVE || a == REMOTE_LEAVE) return LEFT;
return FINISHED;
}
},
LEFT(4) {
@Override
public State next(Action a) {
if (a == LOCAL_LEAVE) return ERROR;
return LEFT;
}
};
private final int value;
State(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public static State fromValue(int value) {
for (State s : values()) {
if (s.value == value) return s;
}
throw new IllegalArgumentException();
}
public State next(Action a) {
return this;
}
}
public enum Action {
LOCAL_INVITATION,
LOCAL_LEAVE,
LOCAL_ABORT,
REMOTE_ACCEPT,
REMOTE_DECLINE,
REMOTE_LEAVE,
REMOTE_ABORT;
public static Action getRemote(long type) {
if (type == SHARE_MSG_TYPE_ACCEPT) return REMOTE_ACCEPT;
if (type == SHARE_MSG_TYPE_DECLINE) return REMOTE_DECLINE;
if (type == SHARE_MSG_TYPE_LEAVE) return REMOTE_LEAVE;
if (type == SHARE_MSG_TYPE_ABORT) return REMOTE_ABORT;
return null;
}
}
}