This post has been triggered by two interesting posts on the topic of what it is to be an application in an OSGi environment. This is something I felt I just had to get stuck in with as its exactly what we’ve been working on in the Newton project.
The way we’ve tackled this is very similar to the approach suggested by Mirko, except instead of using Spring-DM as the top level element in the model we’ve picked SCA.
SCA is a relatively new specification but it gives a vendor neutral way of describing the service dependencies and architecture of an application running in an enterprise environment.
The basic pattern is that a component which has a certain implementation (java, spring, bpel, c, etc) is able to publish a number of services (over a range of protocols – in-memory, RMI, WS, JMS, etc) and consume services from other components via references. The combined unit which is the component, it’s services and it’s references are then packaged in an entity called a composite.
In Newton we associate a composite with a top level (or root) bundle. This bundle then provides the class space for the composite to instantiate it’s implementation, services and references. Importantly the bundle does not have to contain all of the classes that it needs to function but can use OSGi tools such as Import-Package to achieve modularity at the deployment level.
When an SCA composite is installed in a Newton runtime we go through a series of steps:
- Resolve the root bundle that supplies the class space for the composite. If this is not the first time the root bundle has been resolved we increment an internal counter
- Resolve and optionally download the bundle dependencies required to satisfy the root bundle against a runtime repository (this includes ensuring that we reuse existing bundles within the runtime – if they were installed for other composites)
- Build a runtime model around the SCA document that controls the lifecycle of the component as references come and go
- Finally when all required references are satisfied (a dynamic process) we publish the services to be consumed by other components in the enterprise.
When an SCA composite is uninstalled we go through the reverse process:
- Unpublish the services and release any references.
- Shut down the implementation and discard our runtime model.
- The bundle root counter is decremented, if the bundle root counter reaches zero then it is no longer required in the runtime and is marked as garbage.
- Finally garbage collect all bundles that are no longer in use, so clearing down the environment.
This pattern then forms the building blocks of our distributed provisioning framework that is able to deploy instances of SCA composites across what we term a “fabric” of newton runtime instances.
To achieve this we group composites together into an entity known as a System. A system is a group of composites which each specify a particular replication strategy and a set of contract requirements. A replication strategy is a dynamic pattern used by Newton to decide how many instances of a Composite should be installed at any given instant. A contract is a pattern used to restrict where a composite should be installed, i.e. a contract may specify a filter such as (&(machine.cpu.count>1)(machine.memory>1G)) i.e. only install on dual cpu or greater machines with more than 1GB of memory available.
So in the Newton world an application is actually a very dynamic entity. It is built from OSGi bundles and wired together on the fly. But it never the less has rules which are defined via a model (expressed in SCA plus our system extensions) which specify:
- how implementations and interfaces are connected
- how remote services should interact via bindings
- how they should scale
- where they should install
Hope that’s been of interest,