Vaadin recently released Collaboration Engine, a new feature that allows you to easily create real-time multiuser apps. In this post, we take a look under the hood to see how it works.
You can try a collaborative demo app here.
While it's not immediately obvious when using the high-level
CollaborationAvatarGroup classes, there are a whole lot of interesting things going on in the low-level APIs that these classes use. You can use these underlying concepts to build your own custom collaborative user experience. It is also useful to know something about the internals in case you need to debug anything related to Collaboration Engine.
Figure 1: The internals of the Collaboration Engine.
The bottom-most layer is all about data. In a way it’s a simple in-memory database. While building a real full-featured database is a massive undertaking, we have been able to simplify things considerably based on the unique aspects of how Collaboration Engine is intended to be used. The data is structured into "topics" that can be thought of as channels or rooms in a chat app. All data changes made to one topic are seen by all users who are currently subscribed to that topic.
Single topics are intended to correspond to what a user sees in a specific part of the UI, for instance, a particular form. This allows us to simplify concurrent updates: mapping directly to the UI means that no topic will ever receive updates more quickly than a human user can process them. This in turn means that we can use simple, exclusive locks to protect the data in a topic from concurrent access by different/other users. Because each topic has its own lock, the system can scale to accommodate a lot of users, provided that you don’t need a single chat room in which all users can post messages simultaneously, i.e. faster than other users can read them.
The next layer is about subscribing to data changes in a topic. Its foundation is a list of listeners in each topic that are notified whenever data in the topic changes. This in itself is very inconvenient, since it leaves it up to the application developer to manage listeners, update their Vaadin components based on data changes (using UI access), and take care of removing listeners when they are no longer needed to avoid leaking memory.
Because of this, we designed the
TopicConnection class as the only way of accessing the data in the topics. A connection is always tied to a component or custom context. The component lets the connection know when it is attached or detached and it also takes care of using UI.access to make updates to the part of the UI that the component belongs to. Finally, the connection helps do the cleanup in the opposite direction so that any listeners added to UI components can be configured to be removed when the connection is deactivated.
The final layer is the high-level
CollaborationBinder classes that help you implement specific use cases for collaboration with only a few lines of code. These classes work as controllers for the regular AvatarGroup and Binder classes. They take care of opening a topic connection and synchronizing the data with all other users who are using the same topic id. There's some complexity here, especially if the binder scenario needs to use various integration points provided by Binder to react when bindings are updated. Finally,
CollaborationBinder also needs to potentially change field values into a representation that can safely be shared between multiple users without the risk of race conditions.
That, in brief, is how Collaboration Engine works. If you are already using it, I hope this overview helps you stay productive and get the most out of it. If not, I hope I’ve piqued your interest and you’re intrigued enough to give it a try!