Basically the following three techniques are used to develop Saros:
-
Java as programming language
-
XMPP as communication layer
-
Eclipse as target development environment to enhance
Since Saros is based on XMPP, it uses a server based architecture. Communication between clients is done by XMPP messages which are send over XMPP servers. Exchanging client actions (modifications, selection, project file operations,...) is done by wrapping activity descriptions in XMPP messages and sending them to other clients. An exception is the file transfer. Due to performance reasons it uses a peer to peer transfer. If this direct connection fails, Saros uses a fallback to file transfer over XMPP servers.
Saros uses a layer architecture and consists of eight major modules as you can see in the image below. The Whiteboard is an additional project which expands the functionality of the Saros plug-in.
We use the
PicoContainer library to manage our component instances using the Dependency Injection (DI) design pattern.
During Runtime
We are using two different DI containers (or "contexts"): The Plugin Context is started when the Saros IDE plugin starts, and exists as long as the Saros instance is running. A second container is created whenever a Saros session is started, and it is teared down when the session ends. Components from the Session Context can access components from the Plugin Context, but not the other way around.
At Compile Time
Since Saros comes in different variants (a plug-in for Eclipse, one for IntelliJ IDEA, and also as a stand-alone server), the components used at run-time are distributed between the common Core and the specific IDE plugin code.
The following schema shows a typical situation for the Eclipse plugin (continuing the example above).
The example illustrates the following aspects:
- IA is an Interface defined in the Core, and it is implemented by component A in the IDE plugin. Implementing IA is optional from the perspective of the IDE plugin, but it might be helpful (the AbstractContextLifecycle is a real-world example from the Saros code).
- IB is defined in the Core, and needs to be provided with an implementation by the IDE plugin as Core-component E needs it.
- IC is IDE-dependent.
- ID and IE both define Core components, of which D can be readily used, while E needs the IDE plugin to provide an IB implementation.
- Note: Interfaces ID, IC, and IE could be removed to let components A and B depend directly on D, C, and E, respectively (see image below). Many components in Saros don't have a separate Java interface for this reason.
Putting together the contexts: Creating the Mapping
The actual mapping from interfaces to concrete components is what is put into the DI container. In the case of Saros, there are two dimensions that need to be considered: First, there are two contexts (long-lived Plugin Context and short-lived Session Context); second, there are general Core components and specific IDE components. In Saros, we separate these concerns by n times m "context factories" (n=2 for 2 containers, m=4 for Core, Eclipse, IntelliJ and Server). The above example would be realized by the following four context factories: