Am 23.04.2011 00:10, schrieb Matthew Chan:
Hi Everyone,
(This is a long email. Sorry!)
I've been taking a look at how to implement the read-only layers +
ownership transfer idea over XMPP today and I would like to
continue laying out this plan on the mailing list. Please comment
on this as much as possible so we can have a good plan before
implementing. There's probably some assumptions that I am making
from a lack of knowledge about xournal implementation details
--
Alex: one quick note about how I think about layer sharing that is
different from yours. I think that sharing every page in a
document would be simpler. Also, all layers would be shared. I
don't know whether that would be a loss of useful functionality. I
would welcome any feedback on this.
The situation that I think this alternate system solves is the
possibility of user A sharing page 63, layer 5 and user B
receiving it on page 1, layer 1. I could see this being pretty
confusing when users communicate.
I agree with you.
Also if we implement ownership transfer, it saves
users the hassle of managing ownership of each layers
independently.
Notice: We should search for a new word for shared layer, even if
it's technical only a layer, I would show on the sidebar all users
which are connected to you, so you can see who is connected to you
and may disconnect or do something else, but this is only an
implementation detail and don't really matter.
In the notes following, I am assuming we're using a 'share
everything, single layer' model. If we decide not to go with it,
we can add the necessary details afterwards.
--
XMPP challenges
In the read-only layer implementation implemented over XMPP
(without ownership transfer for now), we need to copy strokes from
one client to another (No server if we use XMPP).
I don't know the details about XMPP, can you send binary or only
XML?
Anyway, Xournal++ supports serialising of all contents (Stroke,
Image, Text, everything) to a binary array, but because it's C++ you
can overwrite the handler which stores this and let the new handler
create an XML file, so you don't have to think about this, this is
all implemented.
The challenge with XMPP is showing that regardless of
(a) missing commands or (b) reordered commands, our
implementation won't have issues. Since we don't have a server we
might need to issue IDs per client to work around the reordered
commands (i.e. command from client=2, id=4)
(a) To show we can handle dropped transmissions, we can define our
messaging 'protocol' as follows (implemented on top of XMPP). For
every command sent (i.e. client A: create ITEM_STROKE id=4),
follow with a confirmation (i.e. client B: OK id=4). If client A
doesn't get the confirmation, resend a request for confirmation
(i.e. client A: confirm create ITEM_STROKE id=4). Note the whole
command is in the confirmation request. If client B has already
created the item id=4, then just send OK. If not, then create the
item and send OK. Client A keeps resending the confirmation
request until it gets a response from client B or until there's a
timeout. This handles the possibility that there's a dropped
message in the command or in the confirmation.
Don't send it multiple times, because XMPP is based on TCP
(http://de.wikipedia.org/wiki/Extensible_Messaging_and_Presence_Protocol)
no package can be lost, TCP ensures all data will get correctly to
the server.
If there is a problem, e.g. with the bandwidth and you're sending it
again and again the problem will not be solved, it will be make
worse.
(b) To work around the reordering of commands, we should check to
see whether the current command from the client has an ID which is
incrementally bigger (to see if it is next in the series or if it
was reordered). If it was reordered, wait for it to arrive, and
store the current command.
TCP ensures you get all data in the right order, before you
implement something like this find out if XMPP does this also, then
it's not necessary to think about this. (but check it and print an
error message to stderr, because theory and practise are not always
the same;-))
Layer ownership transfer challenges
If we further try to implement the layer ownership model I
described earlier, any layer would only have 1 editor at a time.
This is kind of like a lock. Since we're using potentially flaky
connections, we can run into problems with abandoned locked etc.
The situations we need to avoid are (a) orphaned ownership and (b)
dual ownership.
(a) The possibility of orphaned ownership arises when a user
disconnects while in possession of someone else's lock. The simple
solution would be to have the original owner implement a timeout
and reclaim ownership after that period of time. However, the
issue is that if the temporary owner continues editing and
reconnects at a later time, both are in possession of the layer
Somebody is the client and somebody is the server, If somebody
connection to MY DOCUMENT I'm the server, if I'm connecting to
another document I'm the client.
If a client loses her connection I get again the owner, if I was
writing before I own now two layer, but I'm still writing on my
layer (as before) and the other layer is not used, if the other
connects again he can have it as long as I'm not writing on it. Else
it is handled as a normal connect (I don't know how a normal connect
would work).
(b) In addition to the timeout/reconnect possibility, dual
ownership can also arise from a dropped message. We need to make
sure ownership transfers implement the 'protocol' as well. As Alex
suggested, perhaps the best way to resolve these types of conflict
are to let the users decide whose changes to keep. We'll need to
implement a way of deleting the unimportant changes.
If you're implementation is good enough dual ownership cannot
happen. We can talk later about this problem.
(ps. are you a developer? If not: this is a typical
"multi-user-problem", we have to possibility to handle this:
possible loss of data, or possible loss of time;-))
Commands
As Denis said in an earlier message, there are several commands we
need to implement to communicate between xournal clients. However
if we use a read-only layer implementation, we do not need to
implement every command in the same way. I've taken the liberty of
splitting them according to whether they interact with a specific
layer.
==layer dependent interaction==
don't number them now...
#define ITEM_STROKE
#define ITEM_TEXT
->
#define ITEM_IMAGE
#define ITEM_ERASURE 2
->
#define ITEM_DELETE?
#define ITEM_TEMP_TEXT 19
#define ITEM_TEXT_EDIT 20
#define ITEM_PASTE 5
#define ITEM_MOVESEL 4
#define ITEM_REPAINTSEL 15
why repaintsel?
->
#define ITEM_TEXT_ATTRIB 21
why? No:
#define ITEM_COLOR_CHANGED
#define ITEM_FONT_CHANGED
#define ITEM_RESIZESEL 22
#define ITEM_NEW_LAYER 6
#define ITEM_DELETE_LAYER 7
==layer independent interaction==
#define ITEM_NEW_BG_ONE 8
#define ITEM_NEW_BG_RESIZE 9
#define ITEM_PAPER_RESIZE 10
Difference between 9 and 10?
#define ITEM_NEW_DEFAULT_BG 11
This don't matter?
#define ITEM_NEW_PAGE 13
#define ITEM_DELETE_PAGE 14
We also have some commands that do not need to be implemented as a
network command because they don't interact with the journal
directly (as far as I understand)
==local commands==
#define ITEM_RECOGNIZER 23
Finally, we will need to add a few more commands to allow for
ownership transfer of layers
And there are some commands missing, I don't add them now, you will
see it when you start to implement this.
==new commands==
- Request layer ownership
- Ownership request response
- Assuming layer ownership
- Most recent command seen
- Join/leave commands
- client version commands
Returning layer ownership can be implemented as an ownership
request with an automatic accept (i.e. check to see if layer id
was originally owned by me, if yes: auto accept) so there's no
need for that command to be implemented
The 'most recent command seen' command could be used to sync users
who join a collaboration mid-session. (i.e. new client sends last
command seen, and other clients send every command with ID greater
than that one)
Implementing commands
Each class of commands can be handled differently.
1. Sending layer dependent interactions need to be locked when
there is no ownership of the layer.
2. Receiving layer dependent commands with a layer ID should
invoke an action on the read-only layer with matching layer ID
3. Layer independent interactions should only be issued by an
'admin' user, with exception of ITEM_NEW_PAGE.
XMPP library
I'm thinking of using gloox as the XMPP lib in question. It's a
pretty stable multi-platform (win/nix/osx/symbian) library which
implements all of XMPP core, and also most of the XMPP IM
features. It's GPL licensed, and included in the repos for Ubuntu,
Fedora, Suse, and Gentoo. Has anyone heard of this, or used it
before?
http://camaya.net/gloox/features
I'm not sure what kind of XMPP features are useful to us yet.
Multi-user chat seems promising, but I think we would want to
encode our strokes to hex first before sending.
So we can send PDF backgrounds etc;-)
Another great feature is the offline message retrieval. We can
probably use that to take care of reconnecting users.
That's it for now. Please let me know what you think.
Matt
|