Table of Contents
1. Introduction
2. Goals
3. Outline - The Command Pattern
4. Different Strategies
4.1. Using Mediator Pattern
4.2. Using Adapter Pattern
4.3. Using Publisher-Subcriber Pattern
5. API
6. Summary
1. Introduction
This document is intended to describe the construction of an Application Programming Interface (API) for engines in Magnetar. An engine is a component in Magnetar that performs a specific sub-task in a software system. For example, a system might need a graphics engine that is responsible for the graphics functions of the system and a network engine for its networking capabilities. The idea of an API for a generic engine in Magnetar is to facilitate the development of engines that can be easily plugged into a Magnetar-compliant systems. This is very much in line with the goals of component-based software development. In this document, we describe our goals for the API for engines as well as the different strategies that can be used to interface with an engine. We also provide a minimal set of API method calls that must be defined by any Magnetar engine, apart from the methods that are specific to the engine.The reader must keep in mind that this is the very first draft of the API for the engine and by no means is it the final. We do not believe in the "Waterfall model" of software development where the process of software construction is strictly sequential. Rather, we hope that the API will be revised many times before it can be finalized.
2. Goals
This section describes our general design goals for the Engine API.
- The API must be generic enough to describe arbitrary kinds of systems. For example, the API provided by networking engine must not be different from the API provided by a graphics engine, though their purposes are widely different.
- The API must allow transparent communication with an engine. That is the API must not render some useful capabilities of an engine inaccesible.
- The API must not be too complex to implement. We aim to provide a very thin API so that adapting a legacy engine to make it compatible with Magnetar will be a snap.
- The API must be independent of implementation technologies or languages.
- The API must not retard good design. In fact, it must facilitate the usage of good design.
3. Outline - The Command Pattern
After several days of discussion, we thought that a very good strategy to unify the API of widely varying engines is to use a command design pattern. To be more specific, a command object can encapsulate the information needed by an engine to perform a task. The API can be as simple as calling a single method called do with this command object. This action is generally triggered by an event in some component of the system.The command object themselves can be specific to engines. Better yet, they can be defined in the Information model of the engine. When a client wants to communicate with the engine, all it does is instantiate this command object and call the do method with that object.
At first sight, this may sound very simple. Nevertheless, there are various issues associated with it:
- We would like the engine components to be as decoupled as possible. This would greatly aid in the independent development of them. Therefore, the different engines in a system should not directly invoke the API of the other engines. This would tightly couple the engines. Rather, we need a mechanism that can deliver events and trigger commands in a loosely coupled fashion.
- The command instantiation protocol might require reading from an XML schema to decipher the structure of the command and use the information embedded in the event that triggered the protocol to flesh out the structure. That is, in object-oriented terms, the class of the command is described in the schema file, while the event that triggers the command protocol, carries the information needed to instantiate the command. In short, the command-instantiation protocol must actually dynamically instantiate a type that is read from a schema file, using event information. This might require a lot of research so that it may not end up being the bottleneck of the system.
- The command instantiation protocol might need to map the event generated by one component to a command for another component. This mapping could be provided as yet another xml file. For this, the components themselves must publish their commands and events. When a system developer assembles these components together, she has the responsibility of mapping the commands and events of the different components. We may need to research the best strategy for realizing this mapping.
- The engine components should not be tightly-coupled. Additionally, they maintain internal data that is private to them. The command creation protocol may need to map the events of one engine to commands for another engine. This protocol might also need a good way to translate the private data of one engine to command data for the other.
4. Different Strategies
This section discusses the different strategies that we have been considering for the implementation of the engine-engine interaction protocol. Each strategy has its own pros and cons. We outline them in this section.
4.1. Using Mediator Pattern
A mediator is any object that can facilitate complex protocols between a client and server object, thereby decoupling them. This object is responsible for triggering the correct command in one engine when an event happens in another engine. The way it may be implemented is illustrated in the figure below:

As is evident from the figure above, the mediator module sits between two engine components acting like a conductor that streamlines the protocol between the two. It uses a XML file for mapping the engines, which effectively makes the mediator module itself a meta-programmed module. It avoids hard-coding the protocol inside the mediator and significantly eases the design and implementation of the mediator module. In addition, a class called CommandFactory is used to instantiate engine-specific commands from command parameters at runtime. This factory can be reused through out the system irrespective of the mediator that uses it.
Thus, using a mediator pattern can address 3 of the 4 issues listed above - loose-coupling of the components, dynamic creation of commands and mapping between the commands and events of the mediated engines. The last issue, namely the translation of private data between engines is still being researched. One viable option that seems to be the solution of choice at this stage is using "object-ids" that are shared between the engines. Each object-id uniquely identifies the data that needs to be shared by the engines. The generation of object-ids is done by either the mediator or using a UUID (Universally Unique Identifier) generation mechanism. The mediator module can maintain the mapping between the ids of the different engines using efficient data structures.
It is apparent that the design and implementation of the mediator module might require enormous thought and research. Experience and literature suggest that the mediator itself can become a monolith that can quickly become hard to construct and debug. This is one notable disadvantage when using this strategy to implement engine interfacing.
4.2. Using Adapter Pattern
Using adapter pattern is very similar to using the mediator pattern. While the mediator pattern itself provides two-way communication between the interacting engines, adapter pattern allows a developer to adapt an engine for the interface of another engine. This means that instead of a single module guiding the engine-engine interaction protocol, there will be two objects, adapting each engine to the interface of the other. Thus, the usage adapter pattern essentially addresses the monolithic design issue that was described for the mediator pattern. The adapter splits the mediator into two, one for way direction of communication. The usage of adapter pattern to guide engine-engine communication is shown below:

4.3. Using Publisher-Subcriber Pattern
The publisher-subscriber pattern is an asynchronous messaging mechanism that can allow the engine components in a system to interact without knowing about each other. A publisher is a software module that publishes events to a central store. A subscriber registers its interest for particular events with this central store. The central store is responsible for routing the events from the publisher when it publishes it, to the subscriber who has interest in that event.Let us use a concrete example to show the usage of this concept for communication between engine components. Let us say that a graphics engine needs to deliver an event to a simulation engine of a game system. Using the loosely-coupled publisher-subscriber pattern utilized in COM+ event system, the graphics engine would register an GraphicsEvent event class in a central catalog ( which would be the COM+ catalog in COM+ event system). The Simulation engine would implement a particular interface of that class. If the Simulation engine is interested only in the "move" events in the GraphicsEvent class, it would define the methods of that event. The COM+ event system takes care of routing the graphics event to the simulation engine.
One advantage with the using this pattern for the mediation between engines is that one could reuse the COM+ implementation of mediation as opposed to writing specific mediator modules. It also significanly decouples the engines themselves.
However, in order to subscribe to a graphics event, the subscriber must implement a "Graphics-engine-specific" interface. This is not a viable option since the engines may be developed by independent thrid-party developers that may not knowledge of each others' interfaces. One might consider having standard interfaces for each event and requiring the engine developers to stick to them. But this seems like an unnecessary and undesirable demand.
It might be possible to develop wrappers around independently developed engine components to make them listen to specific events of the colloborating engines. That is a simulation engine might originally not know anything about the GraphicsEvent class or its constituent interfaces. But a system developer who assembles these engines together might
adapt it to implement the interesting interfaces of GraphicsEvent. Though, this is a plausible solution, it would involve some coding on the part of the system developer.
The following diagram illustrates the usage of publisher-subscriber pattern for engine-engine communication in Magnetar.

5. API
The previous section discussed the implementation strategies for engine-engine communication protocol. While the engine-engine communication is the difficult part to realize, the actual engine API itself is simple. Thus, engine developers do not have the responsibility of figuring out how to interface with other engines or implementing hundreds of methods for their components. Each engine must expose a mandatory set of API listed below:
- public Initialize(), to initialize an engine
- public UnInitialize(), to uninitialize an engine
- public Do (Command c), where Command encapsulates the parameters needed to perform a command. The engines must expose the commands they can handle and optionally a command factory for creating commands at runtime using the corresponding parameters.
- public Load(), to load an engine in a system and
- public Unload(), to unload and engine.
One must note that the API might vary while using the publisher-subscriber pattern for engine-engine communication. The API in that case will closely resemble the COM+ event service specifications.
6. Summary
This document summarized the API for engines in a Magnetar system. The system/engine developers might find it useful to investigate the different implementation strategies listed here and pick the one that best suits their needs.