Welcome, Guest! Log In | Create Account

Everything Just Works Together

One of the hardest tasks facing a development team is getting all the features/services/sub-systems of a product to work well together. So it is most remarkable to claim to be able to assemble any number of plugins which just work together.

When developing a product there are any number of libraries we can use, and development proceeds by building a set of components to implement the required sub-systems, services and features using those those libraries. These components are not reusable, as they are carefully designed to work together in the context of a particular product. This contrasts dramatically with the idea of developing reusable plugins which can then be assembled to create a program. So we are really making the claim that programming can be reduced to writing libraries and plugins, and that once you have a rich set of plugins you can create whatever product you need by just including the appropriate plugins. Reuse becomes a given.

OK, so how did we achieve such reusability? How indeed do we define plugins so that they all just work together? It is too easy to get lost in the details of the code. Some things are obvious requirements, like a common thread/lock model. Some things are helpful, like an asynchronous framework for commands. But there are three things which are central: Rolons, Life Cycle and An Implicit Extensible Binding Schema.

1. Rolons

Rolons are simple DOMish tree structures, and everything is implemented as a Rolon, including all sub-systems, services and user objects. Each element in a Rolon has an instance name, so Rolons are easy to navigate using pathnames.

Rolons then are the universal data structure used and defined by plugins. And because their structure is well known, it is easy for services to manipulate each other's Rolons. This is key to interoperability.

Every Rolon has a number of synchronous operations, which are implemented by the elements of a Rolon or by its child Rolons. Rolons also inherit operations from their parent Rolons. Operations for a Rolon are like methods for an object. And like a method (which can be inherited from a parent class), when you invoke an operation on a Rolon you don't worry about where the operation was implemented. This provides an added degree of decoupling, as a Rolon does not need to address the implementation details or structure of another Rolon to make use of its services.

The persistence model of a Rolon is also constrained, allowing Rolons to be easily expressed as XML documents. Only the attributes and content of an element are persisted, and these are accessible via a common API. This means that Rolons can easily operate on the persistent data of other Rolons without regard to the implementation details of that Rolon. (The threads of a service must take care then to only access its persistent data under a lock, and can only update its persistent data as part of a transaction.)

2. Life Cycle

The life cycle of all elements and all Rolons is well defined and fine-grained. Application logic which does not use the entire life cycle in all its detail can simply ignore the life cycle events which do not apply. Initialization of an element or Rolon is not a simple affair, but being standardized means that we have standard methods for handling it.

Having a standard and detailed life cycle for elements and Rolons makes it possible for Rolons to instantiate and destroy other Rolons, and modify their structures. Again, this is key to the interoperability of services.

3. An Implicit Extensible Binding Schema

Now everything is implemented as a Rolon including the Ark itself, which is a persistent repository of Rolons. And Rolons are nothing more than simple trees of elements. Every element, in addition to having an instance name, has an element type name--which corrisponds directly to the name of an XML element. And to create an element, you need only 3 things: the element type, the instance name and the container element.

For every type of element there is a factory instance, which is responsible for creating instances of that type of element and which participates in the initialization of the element instances as well. Each factory instance holds the names of the classes used to instantiate the objects comprising an instance of an element type, as well as information about what subordinate elements (and child Rolons) are to be created when an element is initialized. The element factory objects collectively define an implicit binding schema.

Now when an Ark is first created, the process begins with the creation of the root element of the Ark Rolon. Initializing the Ark's root element then results in the creation of various subordinate elements and child Rolons. This process cascades, culminating in the creation of the initial configuration of the Ark, simply as an expression of the implicit binding schema.

Plugins make this entire process open and extensible. A plugin does two things:

  1. A plugin defines new element types, by configuring new factory objects.
  2. A plugin extends the configuration of existing element types, defining additional subordinate elements (and child Rolons) to be created when an instance of that element type is initialized.

Note in particular that plugins do not remove or replace any configuration data. Only they define new element types and append to the configuration data of existing element types. This helps reduce potential conflicts between plugins.

The End of Programming As We Know It?

Plugins are entirely reusable, with everything just working together. Programs, be they stand-alone, Swing clients, back-end servers, P2P or web servers, are simply built from lists of plugins, combining whatever services and features you want. And if you need a feature, it only needs to be programmed once.