Skip to: Site menu | Main content

fabric3

Privisioning Architecture Print

This guide serves as an introduction to the Fabric3 provisioning architecture.

Whether Fabric3 is configured for a local or distributed domain topology, the fundamental provisioning architecture is the same. Specifically, a local domain topology is simply a degenerate case of a distributed domain. In Fabric3, components are provisioned in a series of steps where they are first contributed to the domain, processed, and then sent via a changeset (or changesets) to participant nodes. In the case of a single-VM toplogy, the controller and participant node are the same.

To best explain how this process works, it is useful to start with the end result, namely the runtime artifacts that are instantiated when a composite is activated in a domain.

Components and Wires

Given the following composite:

<composite xmlns="http://www.osoa.org/xmlns/sca/1.0" name="BazComposite">
    <component name="FooComponent">
        <implementation.java class="foo.FooImpl"/>
        <reference name="bar" target="BarComponent"/>
    </component>
    <component name="BarComponent">
        <implementation.java class="bar.BarImpl"/>
    </component>
</composite>

the provisioning process will result in the creation of two runtime artifacts of type Component. Composites have no runtime counterpart as they are logical constructs.

Components have 0..n Wires, which correspond to configured references specified in the composite. In the above example, FooCompoent will have a Wire corresponding to the "bar" reference. Wires contain a set of Interceptor chains corresponding to operations on the reference type. Since interceptors are stateless, there exists one Wire per component reference in a runtime.

In the above example, a wire will be created for the bar reference with a terminating interceptor that is responsible for dispatching invocations to an instance of the target BarComponent (for a detailed description of the relationship between components and component implementation instances, see the SCA Assembly Specification downloadable from the OSOA website.

If the previous composite were modified to include a reference configured with the Hessian binding as in:

<!-- .... --->
<component name="BarComponent">
   <implementation.java class="bar.BarImpl"/>
   <reference name="someService>
       <interface.java interface="baz.SomeService"/>
       <hessian:binding.hessian uri="http://someplace/SomeService"/>
   </reference>
</component>

One additional artifact would be provisioned to the participant: a Wire corresponding to the "someService" reference on BarComponent with a terminating interceptor responsible for flowing invocations over the Hessian transport to a remote service.

If the composite were further modified composite to include a service as in:

<!-- .... --->
<component name="FooComponent">
   <implementation.java class="foo.FooImpl"/>
   <service name="FooService">
      <interface.java interface="foo.Foo"/>
      <hessian:binding.hessian uri="/fooService" />
   </service>
   <reference name="bar" target="BarComponent"/>
</component>

An additional Wire would be provisioned to the participant with a head interceptor accesible from the binding transport and a terminating interceptor responsible for dispatching to an instance of FooComponent (how the head interceptor becomes available to the binding transport will be explained below).

Component type extensions are responsible for creating {{Component}}s and terminating interceptors for dispatching invocations to instances of their type. Binding extensions are responsible for supplying head and terminating interceptors that flow invocations from and to remote transports.

Logical versus Physical

A key concept to bear in mind is that Fabric3 makes a fundamental distinction between the logical representation of a composite (services, components, references, bindings, etc.) and their physical manifestation on a participant node (components and wires). The provisioning process essentially involves transforming the logical representation of a composite in steps to its physical manifestation on a participant node or set of nodes. These steps are detailed below.

Loading the Logical Type Model

The first step in the provisioning process after a composite has been contributed to the domain is to load its SCDL file and process its component definitions and wires. Processing may include introspection of implementation artifacts such as Java classes to build up ComponentType information. The end-product of this load process is a logical type model which represents the set of type information for all SCA artifacts in the contribution (i.e. composites, components, ComponentTypes, etc.). The logical type model is a POJO-based in-memory representation of the SCA contribution. As a type model, it is analogous to Java reflection information.

Binding and component type extensions will typically "plug-into" this process by creating their own StAXElementLoader for specific XML element types, for example, binding.XXXX or implementation.XXXX. The output of these loaders is a logical model object representing the configuration information and extending ModelObject or one of its subtypes. Loaders follow the registry pattern as described here.

Component Instantiation

After a composite has been contributed and its logical type model has been loaded, it may be activated in the domain, which may result in the creation of logical instances. Contribution and activation are analogous to loading a Java class in a classloader and instantiating an instance respectively. Activation will result in the creation of LogicalComponents, LogicalReferences, LogicalServices, and LogicalBindings, with instance-specific information such as normalized property and reference overrides. The original type information introspected from the contribution will be available on their logical counterparts.

Generating and Provisioning Physical Changesets

After logical instances are created during activation, physical changesets are generated from that information. Physical changesets contain the information necessary to instantiate runtime artifacts on a participant node. For example, a subtype of PhysicalComponentDefinition will be generated from a LogicalComponent based on its implementation type. Likewise, PhysicalWireDefinition}}s will be generated from wires and may contain binding information if the wires are bound. For the example at the outset of this guide, the physical changeset produced would contain two {{PhysicalComponentDefinition}}s and one {{PhysicalWireDefinition for to the "bar" reference on FooComponent.

Extensions provide generators for creating physical changset information. Specifically, component type extensions contribute ComponentGenerators which produce subtypes of PhysicalComponentDefinition. Binding extensions contribute BindingGenerators which produce subtype of PhysicalWireSourceDefinition and PhysicalWireTargetDefinition .

The configuration information created by the generators is used to create runtime artifacts on participant nodes. Like loaders, generators follow the registry pattern.

When generation is complete, the resulting changesets are serialized to participant nodes.

Building Components

When a changeset arrives at a node, it is deserialized and [ComponentBuilder]s are called to process PhysicalComponentDefinition}}s. {{ComponentBuilder}}s are specific to component types and are responsible for creating implementations of {{Component. They are dispatched to (following the registry pattern) based on the subtype of PhysicalComponentDefinition.

Attaching Wires

Following the creation of components, wires are created for each PhysicalWireDefinition present in the changset. WireAttachers are then called to attach head and terminating interceptors. Component extensions, for example, would attach terminating interceptors responsible for dispatching to implementation instances. Binding extensions would attach terminating interceptors responsible for flowing invocations over a remote binding. WireAttachers are dispatched to (following the registry pattern) based on the subtype of PhysicalWireSourceDefinition and PhysicalWireTargetDefinition.

Once wires have been attached, Components are started on the participant nodes.

Summary

The distinction between the logical assembly model and physical runtime artifacts is one of the central tenets of the Fabric3 architecture. Extensions are responsible for loading logical representations from configuration or application artifact metadata (e.g. Java annotations) and generating corresponding physical metadata which is sent from the Controller to participant nodes via changesets. On a participant node, extensions are responsible for creating appropriate runtime artifacts based on the physical metadata contained in the changeset.