Turn local session state into its own class instead of BdfDictionary

This commit is contained in:
Torsten Grote
2016-05-11 19:23:54 -03:00
parent 886c8feb34
commit d2722eed92
10 changed files with 631 additions and 439 deletions

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,7 +55,6 @@ 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;
@@ -76,11 +71,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 +83,8 @@ 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.forum.ForumSharingSessionState.fromBdfDictionary;
import static org.briarproject.forum.SharerSessionState.Action;
class ForumSharingManagerImpl extends BdfIncomingMessageHook
implements ForumSharingManager, Client, RemoveForumHook,
@@ -214,10 +205,10 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
throw new FormatException();
// initialize state and process invitation
BdfDictionary state =
InviteeSessionState state =
initializeInviteeState(txn, contactId, msg);
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);
@@ -226,23 +217,25 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
} else if (type == SHARE_MSG_TYPE_ACCEPT ||
type == 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) {
// 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,7 +257,8 @@ 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();
@@ -275,7 +269,7 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
// start engine and process its state update
SharerEngine engine = new SharerEngine();
processStateUpdate(txn, null,
processSharerStateUpdate(txn, null,
engine.onLocalAction(localState, localAction));
txn.setComplete();
@@ -293,7 +287,7 @@ 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();
@@ -305,7 +299,7 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
// start engine and process its state update
InviteeEngine engine = new InviteeEngine(forumFactory);
processStateUpdate(txn, null,
processInviteeStateUpdate(txn, null,
engine.onLocalAction(localState, localAction));
txn.setComplete();
@@ -346,12 +340,12 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
boolean available = false;
if (!local) {
// figure out whether the forum is still available
BdfDictionary sessionState =
ForumSharingSessionState s =
getSessionState(txn, sessionId, true);
InviteeProtocolState state = InviteeProtocolState
.fromValue(
sessionState.getLong(STATE).intValue());
available = state == AWAIT_LOCAL_RESPONSE;
if (!(s instanceof InviteeSessionState))
continue;
available = ((InviteeSessionState) s).getState() ==
InviteeSessionState.State.AWAIT_LOCAL_RESPONSE;
}
ForumInvitationMessage im =
new ForumInvitationMessage(m.getKey(), sessionId,
@@ -490,7 +484,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,26 +496,20 @@ 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,
private InviteeSessionState initializeInviteeState(Transaction txn,
ContactId contactId, BdfDictionary msg)
throws FormatException, DbException {
@@ -538,29 +526,25 @@ 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);
SessionId sessionId = new SessionId(msg.getRaw(SESSION_ID));
InviteeSessionState s = new InviteeSessionState(sessionId, 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 +554,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 +566,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 +605,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,16 +648,16 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
}
private void processStateUpdate(Transaction txn, MessageId messageId,
StateUpdate<BdfDictionary, BdfDictionary> result)
StateUpdate<ForumSharingSessionState, BdfDictionary> 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) {
@@ -677,24 +679,47 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
}
}
private void performTasks(Transaction txn, BdfDictionary localState)
private void processSharerStateUpdate(Transaction txn, MessageId messageId,
StateUpdate<SharerSessionState, BdfDictionary> result)
throws DbException, FormatException {
StateUpdate<ForumSharingSessionState, BdfDictionary> r =
new StateUpdate<ForumSharingSessionState, BdfDictionary>(
result.deleteMessage, result.deleteState,
result.localState, result.toSend, result.toBroadcast);
processStateUpdate(txn, messageId, r);
}
private void processInviteeStateUpdate(Transaction txn, MessageId messageId,
StateUpdate<InviteeSessionState, BdfDictionary> result)
throws DbException, FormatException {
StateUpdate<ForumSharingSessionState, BdfDictionary> r =
new StateUpdate<ForumSharingSessionState, BdfDictionary>(
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
@@ -786,17 +811,17 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
private void leaveForum(Transaction txn, ContactId c, Forum f)
throws DbException, FormatException {
BdfDictionary state = getSessionStateForLeaving(txn, f, c);
ForumSharingSessionState state = getSessionStateForLeaving(txn, f, c);
BdfDictionary action = new BdfDictionary();
action.put(TYPE, SHARE_MSG_TYPE_LEAVE);
if (state.getBoolean(IS_SHARER)) {
if (state instanceof SharerSessionState) {
SharerEngine engine = new SharerEngine();
processStateUpdate(txn, null,
engine.onLocalAction(state, action));
processSharerStateUpdate(txn, null,
engine.onLocalAction((SharerSessionState) state, action));
} else {
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

@@ -9,8 +9,6 @@ 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;
@@ -19,9 +17,6 @@ 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;
@@ -29,25 +24,25 @@ 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.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<BdfDictionary, InviteeSessionState, BdfDictionary> {
private final ForumFactory forumFactory;
private static final Logger LOG =
@@ -58,16 +53,15 @@ public class InviteeEngine
}
@Override
public StateUpdate<BdfDictionary, BdfDictionary> onLocalAction(
BdfDictionary localState, BdfDictionary localAction) {
public StateUpdate<InviteeSessionState, BdfDictionary> onLocalAction(
InviteeSessionState localState, BdfDictionary localAction) {
try {
InviteeProtocolState currentState =
getState(localState.getLong(STATE));
State currentState = localState.getState();
long type = localAction.getLong(TYPE);
InviteeAction action = InviteeAction.getLocal(type);
InviteeProtocolState nextState = currentState.next(action);
localState.put(STATE, nextState.getValue());
Action action = Action.getLocal(type);
State nextState = currentState.next(action);
localState.setState(nextState);
if (action == LOCAL_ABORT && currentState != ERROR) {
return abortSession(currentState, localState);
@@ -85,14 +79,14 @@ public class InviteeEngine
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))
new BdfEntry(SESSION_ID, localState.getSessionId()),
new BdfEntry(GROUP_ID, localState.getGroupId())
);
if (action == LOCAL_ACCEPT) {
localState.put(TASK, TASK_ADD_SHARED_FORUM);
localState.setTask(TASK_ADD_SHARED_FORUM);
msg.put(TYPE, SHARE_MSG_TYPE_ACCEPT);
} else {
localState.put(TASK,
localState.setTask(
TASK_REMOVE_FORUM_FROM_LIST_SHARED_WITH_US);
msg.put(TYPE, SHARE_MSG_TYPE_DECLINE);
}
@@ -102,15 +96,15 @@ public class InviteeEngine
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));
msg.put(SESSION_ID, localState.getSessionId());
msg.put(GROUP_ID, localState.getGroupId());
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, BdfDictionary>(false,
false, localState, messages, events);
} catch (FormatException e) {
throw new IllegalArgumentException(e);
@@ -118,16 +112,15 @@ public class InviteeEngine
}
@Override
public StateUpdate<BdfDictionary, BdfDictionary> onMessageReceived(
BdfDictionary localState, BdfDictionary msg) {
public StateUpdate<InviteeSessionState, BdfDictionary> onMessageReceived(
InviteeSessionState localState, BdfDictionary msg) {
try {
InviteeProtocolState currentState =
getState(localState.getLong(STATE));
State currentState = localState.getState();
long type = msg.getLong(TYPE);
InviteeAction action = InviteeAction.getRemote(type);
InviteeProtocolState nextState = currentState.next(action);
localState.put(STATE, nextState.getValue());
Action action = Action.getRemote(type);
State nextState = currentState.next(action);
localState.setState(nextState);
logMessageReceived(currentState, nextState, type, msg);
@@ -149,7 +142,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,31 +151,30 @@ 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, BdfDictionary>(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, BdfDictionary msg) {
if (!LOG.isLoggable(INFO)) return;
@@ -194,16 +186,16 @@ public class InviteeEngine
" 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()
"Moving on to state " + localState.getState().name()
);
} catch (FormatException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
}
private void logMessageReceived(InviteeProtocolState currentState,
InviteeProtocolState nextState, long type, BdfDictionary msg) {
private void logMessageReceived(State currentState, State nextState,
long type, BdfDictionary msg) {
if (!LOG.isLoggable(INFO)) return;
try {
@@ -224,8 +216,8 @@ public class InviteeEngine
}
@Override
public StateUpdate<BdfDictionary, BdfDictionary> onMessageDelivered(
BdfDictionary localState, BdfDictionary delivered) {
public StateUpdate<InviteeSessionState, BdfDictionary> onMessageDelivered(
InviteeSessionState localState, BdfDictionary delivered) {
try {
return noUpdate(localState, false);
} catch (FormatException e) {
@@ -234,37 +226,33 @@ 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, BdfDictionary> 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());
localState.setState(ERROR);
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));
msg.put(SESSION_ID, localState.getSessionId());
msg.put(GROUP_ID, localState.getGroupId());
List<BdfDictionary> messages = Collections.singletonList(msg);
List<Event> events = Collections.emptyList();
return new StateUpdate<BdfDictionary, BdfDictionary>(false, false,
return new StateUpdate<InviteeSessionState, BdfDictionary>(false, false,
localState, messages, events);
}
private StateUpdate<BdfDictionary, BdfDictionary> noUpdate(
BdfDictionary localState, boolean delete) throws FormatException {
private StateUpdate<InviteeSessionState, BdfDictionary> noUpdate(
InviteeSessionState localState, boolean delete) throws FormatException {
return new StateUpdate<BdfDictionary, BdfDictionary>(delete, false,
return new StateUpdate<InviteeSessionState, BdfDictionary>(delete, false,
localState, Collections.<BdfDictionary>emptyList(),
Collections.<Event>emptyList());
}

View File

@@ -0,0 +1,130 @@
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_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 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 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

@@ -6,8 +6,7 @@ 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 static org.briarproject.forum.SharerSessionState.Action;
import java.util.Arrays;
import java.util.Collections;
@@ -16,7 +15,6 @@ 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;
@@ -27,40 +25,38 @@ 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.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<BdfDictionary, SharerSessionState, BdfDictionary> {
private static final Logger LOG =
Logger.getLogger(SharerEngine.class.getName());
@Override
public StateUpdate<BdfDictionary, BdfDictionary> onLocalAction(
BdfDictionary localState, BdfDictionary localAction) {
public StateUpdate<SharerSessionState, BdfDictionary> onLocalAction(
SharerSessionState localState, BdfDictionary localAction) {
try {
SharerProtocolState currentState =
getState(localState.getLong(STATE));
State currentState = localState.getState();
long type = localAction.getLong(TYPE);
SharerAction action = SharerAction.getLocal(type);
SharerProtocolState nextState = currentState.next(action);
localState.put(STATE, nextState.getValue());
Action action = Action.getLocal(type);
State nextState = currentState.next(action);
localState.setState(nextState);
if (action == LOCAL_ABORT && currentState != ERROR) {
return abortSession(currentState, localState);
@@ -79,32 +75,32 @@ public class SharerEngine
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));
msg.put(SESSION_ID, localState.getSessionId());
msg.put(GROUP_ID, localState.getGroupId());
msg.put(FORUM_NAME, localState.getForumName());
msg.put(FORUM_SALT, localState.getForumSalt());
if (localAction.containsKey(INVITATION_MSG)) {
msg.put(INVITATION_MSG,
localAction.getString(INVITATION_MSG));
}
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));
msg.put(SESSION_ID, localState.getSessionId());
msg.put(GROUP_ID, localState.getGroupId());
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, BdfDictionary>(false,
false, localState, messages, events);
} catch (FormatException e) {
throw new IllegalArgumentException(e);
@@ -112,16 +108,15 @@ public class SharerEngine
}
@Override
public StateUpdate<BdfDictionary, BdfDictionary> onMessageReceived(
BdfDictionary localState, BdfDictionary msg) {
public StateUpdate<SharerSessionState, BdfDictionary> onMessageReceived(
SharerSessionState localState, BdfDictionary msg) {
try {
SharerProtocolState currentState =
getState(localState.getLong(STATE));
State currentState = localState.getState();
long type = msg.getLong(TYPE);
SharerAction action = SharerAction.getRemote(type);
SharerProtocolState nextState = currentState.next(action);
localState.put(STATE, nextState.getValue());
Action action = Action.getRemote(type);
State nextState = currentState.next(action);
localState.setState(nextState);
logMessageReceived(currentState, nextState, type, msg);
@@ -141,7 +136,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,30 +146,29 @@ 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, BdfDictionary>(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,
BdfDictionary msg) {
if (!LOG.isLoggable(INFO)) return;
@@ -182,20 +176,20 @@ public class SharerEngine
if (msg.getLong(TYPE, -1L) == SHARE_MSG_TYPE_LEAVE) a = "leave";
try {
LOG.info("Sending " + a + " in state " + state.name() +
LOG.info("Sending " + a + " 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 " +
getState(localState.getLong(STATE)).name()
"Moving on to state " + nextState.name()
);
} catch (FormatException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
}
private void logMessageReceived(SharerProtocolState currentState,
SharerProtocolState nextState, long type, BdfDictionary msg) {
private void logMessageReceived(State currentState, State nextState,
long type, BdfDictionary msg) {
if (!LOG.isLoggable(INFO)) return;
try {
@@ -217,8 +211,8 @@ public class SharerEngine
}
@Override
public StateUpdate<BdfDictionary, BdfDictionary> onMessageDelivered(
BdfDictionary localState, BdfDictionary delivered) {
public StateUpdate<SharerSessionState, BdfDictionary> onMessageDelivered(
SharerSessionState localState, BdfDictionary delivered) {
try {
return noUpdate(localState, false);
} catch (FormatException e) {
@@ -227,38 +221,36 @@ 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, BdfDictionary> 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());
localState.setState(ERROR);
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));
msg.put(SESSION_ID, localState.getSessionId());
msg.put(GROUP_ID, localState.getGroupId());
List<BdfDictionary> messages = Collections.singletonList(msg);
List<Event> events = Collections.emptyList();
return new StateUpdate<BdfDictionary, BdfDictionary>(false, false,
return new StateUpdate<SharerSessionState, BdfDictionary>(false, false,
localState, messages, events);
}
private StateUpdate<BdfDictionary, BdfDictionary> noUpdate(
BdfDictionary localState, boolean delete) throws FormatException {
private StateUpdate<SharerSessionState, BdfDictionary> noUpdate(
SharerSessionState localState, boolean delete)
throws FormatException {
return new StateUpdate<BdfDictionary, BdfDictionary>(delete, false,
return new StateUpdate<SharerSessionState, BdfDictionary>(delete, false,
localState, Collections.<BdfDictionary>emptyList(),
Collections.<Event>emptyList());
}
}

View File

@@ -0,0 +1,130 @@
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_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.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;
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 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 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 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;
}
}
}