newton
Newton
 

Custom Bindings

The Newton framework is designed to be an extensible system that allows implementors to provide implementations of bindings to a range of other service orientated protocols.

Binding Architecture

The starting point for a developer wishing to build a new binding for Newton are the interfaces:

org.cauldron.newton.component.factory.api.ReferenceBindingFactory
org.cauldron.newton.component.factory.api.ServiceBindingFactory

These two factories provide the entry point used by the Newton framework to bind POJO's within the JVM to external services. The ReferenceBindingFactory is called on by the Newton framework to import existing external services into the Newton world. Conversely the ServiceBindingFactory is used to export components from Newton as external services to the outside world.

When an SCA composite is installed in the Newton container the binding layer introspects each service and reference binding in the composite document - it looks for a corresponding factory that knows how to create that binding. Using the corresponding factory an instance of a ReferenceBinding or ServiceBinding is created that handles communicating the state of the service to the Newton framework.

A ReferenceBindingFactory's primary job is to create a ReferenceBinding object which provides two functions. Firstly it communicates the state of an external service to the Newton infrastructure and secondly it provides a mechanism to grab a proxy object (or the object itself if the SOA is an internal jvm model) to the service the reference is bound.

A ServiceBindingFactory's primary job is to create a ServiceBinding which listens for lifecycle events from the Newton infrastructure and publishes an external interface to the component that the service is bound to when the Newton infrastructure prompts.

Installing A Binding Factory

There are two parts to installing a binding factory.

  1. Install the XML parser that knows how to interpret the XML elements

  2. Install a reference or service binding factory that interprets the parsed elements and constructs the actual binding.

There are two parts to this process as we wished to disconnect the model for parsing XML elements into a programmatic model of the composite from the process of parsing that model into a real implementation. This allows us to do interesting things like programatically manipulate a model after it has been read in from XML but prior to constructing a real implementation.

XML To Model

The first stage then is to publish a service to the osgi registry that implements either:

org.cauldron.Newton.descriptor.xml.api.ElementParser
org.cauldron.Newton.descriptor.xml.api.ElementParserGroup

This can be achieved in a composite using the following pattern:

<composite name="binding-parser">
<service name="parser-export">
<interface.java interface="org.cauldron.Newton.descriptor.xml.api.ElementParser" />
<binding.osgi />
</service>

<component name="parser-impl">
<implementation.java.callback impl="com.example.parser.MyBindingParser" />
</component>

<wire>
<target>parser-impl</target>
<source>parser-export</source>
</wire>
</composite>

This publishes a component that is able to interpret xml elements and construct an object model that represents the binding. Here is a simple example of a binding xml parser:

public class MyBindingParser extends CompoundDescriptorParser {

public QName getName() {
return new Qname("binding.example");
}

public void parse(XMLStreamReader reader, DescriptorParsingContext context) throws XMLStreamException {
CompoundDescriptor parent = (CompoundDescriptor) context.peek();

BindingDetails fragment;
if (parent instanceof Service) {
fragment = new BindingDetails("com.example.binding.service");
}
else if (parent instanceof Reference) {
fragment = new BindingDetails("com.example.binding.reference");
}
else {
throw new XMLStreamException("unexpected parent: " + parent.getClass().getCanonicalName());
}

if (parent.accept(fragment)) {
context.push(fragment);
parseCompound(reader, context);
context.pop();
}
else {
throw new XMLStreamException("Parent does not accept " + getName(), reader.getLocation());
}
}
}

When this service is installed when an xml element is found that matches <binding.example /> the parse element will be called. The parser creates a different binding implementation depending on the parent element that encloses it. For example if the composite contained:

<service name="foo">
<binding.example />
</service>

then the binding details created would be new BindingDetails("com.example.binding.service");

On it's own this example is fairly limited but it is possible to extend the parsing to attach org.cauldron.newton.descriptor.component.impl.BindingFacet 's to BindingDetails which can contain specific details about the binding. For example if we extended the element to contain an async attribute: <binding.example asyc="true" /> then the parser could attach a BindingFacet that encapsulates this info which can then be parsed to the relevant binding factory at deployment time.

Model To Implementation

Having parsed the xml and built up a BindingDetails object with associated BindingFacets the installer parses the model onto the CompositeFactory. This delegates creation of bindings to services discovered in the osgi registry which implement the ReferenceBindingFactory and ServiceBindingFactory.

These services each provide a method getName(). The composite factory uses this name property to match the factory to the name of the BindingDetails object for a particular binding.

Therefore to continue our example to implement the ExampleReferenceBindingFactory we would first create a composite like so:

<composite name="binding-factory">
<service name="factory-export">
<interface.java interface="org.cauldron.newton.component.factory.api.ReferenceBindingFactory" />
<binding.osgi />
</service>

<component name="factory-impl">
<implementation.java.callback impl="com.example.binding.MyBindingFactory" />
</component>

<wire>
<target>factory-impl</target>
<source>factory-export</source>
</wire>
</composite>

This publishes our binding factory to the osgi registry where it will be discovered by the composite factory and called when an composite is installed with a BindingDetails object that matches the name of our binding factory.

Here is a simple example of how the factory could be implemented:

public class MyBindingFactory implements ReferenceBindingFactory {

public MyBindingFactory() {
}

public String getName() {
return "com.example.binding.reference";
}

public ReferenceBinding getReferenceBinding(Reference ref,
BindingDetails provider, ComponentContext ctx)
throws CompositeFactoryException {

return new MyReferenceBinding()
}

public void releaseReferenceBinding(ReferenceBinding binding) {
}
}

This binding factory creates a new MyReferenceBinding which will handle life-cycle events from the outside world and pass them onto the Newton framework.

Creating a Reference Binding

The reference binding interface provides the following methods:

public interface ReferenceBinding {
String getName();
Reference getReference();
void startBinding();
void stopBinding();
}

The first method getName() should match the name of the binding factory that created it, i.e. in the example given above "com.example.binding.reference".

The second method getReference() returns an object that implements the Reference interface. It is recommended that impementors simply return a ReferenceImpl for the current release though this may be improved in future releases based on feedback.

The final two methods start/stop binding are used to signal to the binding that it should make/drop external connections to the implemention service layer.

When the service layer detects that the service that is bound to this reference is found/lost it should call the methods

void connect(Object serviceObject, Map<String, Object> attrs);
void disconnect(Object serviceObject, Map<String, Object> attrs);

On the Reference object returned from getReference() this informs the Newton infrastructure that a service implementation matching the criteria in the reference has been found or is lost.

Creating a Service Binding

A service binding provides the following methods:

public interface ServiceBinding {
String getName();
Service getService();
void publish();
void unpublish();
void onCompositeBound();
void onCompositeUnbound();
}

The first method getName() should match the name of the binding factory that created it, i.e. in the example given above "com.example.binding.service".

The second method getService() returns an object that implements the Service interface. It is recommended that implementors simply return a ServiceImpl for the current release though this may be improved in future releases based on feedback.

The following methods publish/unpublish and onComposite Bound/Unbound have similar semantics but may be used by implementing bindings in different ways.

Publish/Unpublish are called at the beginning and end of a service's lifecycle, they are called once and can be used to set up any long running resources.

onComposite Bound/Unbound is called as references are bound to the underlying component.

These two different lifecycles are useful if your service layer is able to differentiate between “available” and “active”. In the “available” case publish is called but not setCompositeBound – this means semantically “here is a service, though it's not fully functional yet”. In the active case this means the service is visible and all it's dependencies are satisfied so this means “here is a service, come us it”

Jars & API's

The main jars that are of interest to the binding developer are found in the directory sdk/lib of a Newton install, they are:

model.jar – contains classes that represent the model of components, services and references

descriptor-parsing.jar – contains classes that parse xml documents into the model

component-api.jar – contains classes that represent the runtime view of components, services and references

component-factory-api.jar – contains interface classes for building extension service/reference bindings or component implementations.