Chapter 14. Extending Tuscany

This chapter covers

  • Building Tuscany extensions
  • A complete walk-through of developing a new implementation type
  • A complete walk-through of developing a new binding type

Apache Tuscany already supports a wide variety of technologies out of the box. But it’s still possible that the technology that you’re interested in isn’t supported yet. The beauty of Tuscany is that it can be extended easily to support new technologies because of its modularized and pluggable architecture.

This chapter is somewhat different from the previous chapters in the book because it goes beyond describing how to use the Tuscany runtime to build applications and explains how to extend the Tuscany runtime to support new technologies. The nature of this subject means that this chapter’s content is more complex than that of previous chapters. If you don’t need to extend Tuscany, you can skip this chapter. If you do want to add your own extension, we’ll present a practical step-by-step process for doing that, and we’ve based our examples on samples that come with the Tuscany distribution.

We’ll start this chapter with a high-level view of what’s required to extend Tuscany. Then we’ll walk through two examples in detail. In one example we’ll extend Tuscany to allow users to create a new Java-based implementation type called implementation.pojo. In the second example we’ll extend Tuscany to support a new communication protocol called Echo with binding.echo. We won’t cover the details of adding a databinding extension or a policy extension in this chapter. But the steps required for adding these extension types are similar to the two that we’ve chosen to demonstrate, and there are plenty of example databindings and policies in the Tuscany code base.

Let’s start with an overview of the steps required to extend Apache Tuscany.

14.1. The high-level view of developing a Tuscany extension

The first step in supporting a new technology in Tuscany is to determine how it’s relevant to building an SCA composite. Is it going to be used to build components, to handle a communication protocol, to describe a service or reference interface, to represent data, or to describe a quality of service? In other words, which extension type does it fit into? Tuscany defines extension types for each purpose, namely:

  • Implementation types
  • Binding types
  • Interface types
  • Databindings
  • Intents and policy sets

The SCA specifications define how each extension type (with the exception of databinding, which is Tuscany-specific) can be used to enable SCA to work with a new technology. This is referred to as the SCA extension model. When you want to support a new technology in Tuscany, you build a new extension for an appropriate extension. The SCA specifications themselves use this extension model to describe a series of extensions including binding.ws (Web Services), binding.jms (JMS), implementation.java (Java), implementation.spring (Spring), and implementation.bpel (BPEL). Tuscany supports these and in addition implements other extensions such as implementation.script (scripting languages) and binding.atom or binding.jsonrpc (Web 2.0).

Creating a new extension in Tuscany involves two distinct steps. First, you’ll need to develop the extension code to handle the new technology. For example, implementation.java is implemented using reflection APIs to invoke each business method. We won’t cover the details of this step in this chapter because each extension is dependent on the characteristics of the technology that it supports. In the second step, the Tuscany runtime is configured to load, invoke, and manage the new extension. You’ll provide this information to the Tuscany runtime through the Tuscany extension point mechanism.

An extension point is the place where the Tuscany runtime collects the information required for handling an extension. You’ll need to do the following:

  • Define how the extension can be used and configured in a composite file.
  • Define how to create a runtime instance of the configured extension.
  • Enable the Tuscany runtime to invoke and manage the extension.

Figure 14.1 shows the three typical extension points where you’ll need to provide information to enable a new extension. Extension points are shown as plug-in boxes numbered 1, 2, and 3 on top of the runtime.

Figure 14.1. An extension contributes various information to Tuscany extension points.

The numbered extension points from figure 14.1 are as follows:

  1. The extension point where you’ll provide the XML schema for the extension— This defines how the extension is used in the XML of an SCA composite file. Notice how it extends the schema that SCA defines for extension types. For example, implementation.java is an extension of the SCA implementation type and adds the class attribute for the name of the Java file that implements a component.
  2. The extension point where you’ll define a Java model that represents the in-memory version of the extension— You’ll also provide the code for a processor that knows how to transform the XML representation in the composite file into an in-memory Java model and vice versa.
  3. The extension point where you’ll add the code that the Tuscany runtime will use to locate, invoke, and manage the extension at runtime— This is called an extension provider. For example, the BPEL implementation extension provider delegates the handling of the BPEL process component implementation to the Apache ODE runtime.

Now that you have a general understanding of the steps required for adding a new extension, let’s look at some concrete examples. We’ll first look at what’s required to add a new implementation type.

14.2. Developing a POJO implementation type

In this section we’ll show you how to develop a new implementation type. If, say, the TuscanySCATours company wanted to implement its Payment component using Fortran, Pascal, Cobol, or any one of many programming environments that the Tuscany SCA Java runtime doesn’t support out of the box, a new implementation type would be needed.

Once the new implementation type is provided, anyone wanting to use it could exploit it by adding the appropriate <implementation/> element to their component description. One person takes the time to work out how to integrate with the new component implementation environment, and other users can benefit.

Here we’ll add support for a POJO (plain old Java object) component implementation type, which can be used in the following way.

<component name="HelloWorldComponent">
<p:implementation.pojo class="helloworld.HelloWorldImpl" />
</component>

This extension is a simplistic version of implementation.java, but we’ve chosen it because it’s provided in Tuscany as a simple example to show how to add a new implementation type. You can find the code for the implementation.pojo extension, and examples of its use, in the Tuscany source or binary distribution in the samples/ implementation-pojo-extension directory.

Let’s start by looking at how to add XML schema for the implementation.pojo element.

14.2.1. Add the implementation.pojo XML schema

The XML schema for each implementation extension defines how it will appear within the XML of an SCA composite file. This information resides in an XSD file that’s typically prefixed with the extension name. The following listing shows the contents of sample-implementation-pojo.xsd.

Listing 14.1. XML schema for implementation.pojo

The content of this .xsd file describes some key attributes of the implementation. pojo extension. It declares a global element, implementation.pojo, that belongs to the "http://pojo" namespace. The <p:implementation.pojo> element can be used to substitute <sca:implementation> as a child of the <sca:component> element. The POJOImplementation type extends <sca:Implementation> by adding a class attribute of type NCName.

The Tuscany runtime will use the schema for the extension to validate the implementation.pojo syntax as it reads the composite file into memory. The validation will be done only if the schema is registered with the Tuscany runtime.

You’ll want the implementation.pojo extension to be validated in this case. Alongside your new XML schema file you register the schema with the Tuscany runtime by creating a file under the META-INF/services directory as follows:

src/main/resources
META-INF/services/
org.apache.tuscany.sca.contribution.processor.ValidationSchema
sample-implementation-pojo.xsd

This ValidationSchema file will contain a single line to identify which schema to register:

sample-implementation-pojo.xsd

Schema validation is an optional step in forming an extension. It’s highly recommended, though, because it allows the Tuscany runtime to report potential problems early on.

Now that you have the XML schema, you’ll need to define how the implementation. pojo XML element is read in and written out.

14.2.2. Adding implementation.pojo XML processing

Before you read and write the implementation XML, you’ll first need to define how the implementation is modeled in the Tuscany runtime. In figure 14.2, box 1, we’ll introduce implementation.pojo to our composite application to implement the HelloWorld component. The class attribute holds the name of the business logic for the HelloWorld component, in this case helloworld.HelloWorldImpl.

Figure 14.2. The XML and Java model for the POJO implementation type

 

SCA assembly naming convention for implementation types

The SCA assembly spec has a naming convention for the XML element of an implementation type. The local name uses the “implementation” prefix, for example, implementation.pojo. The target namespace for elements not defined by the SCA specifications must be defined in namespaces other than the SCA namespace (http://www.osoa.org/xmlns/sca/1.0). We’ll use http://pojo for implementation.pojo.

 

Figure 14.2, box 2 holds the XML schema for implementation.pojo that we defined earlier. You’ll use this to validate the XML configuration.

The next step is box 3, where you’ll define the in-memory Java model of implementation.pojo. The <implementation.pojo> element is modeled as a pure Java interface and extends the SCA Implementation interface. The following code snippet shows the key methods we defined:

import org.apache.tuscany.sca.assembly.Implementation;

public interface POJOImplementation extends Implementation {
public String getPOJOName();
public void setPOJOName(String pojoName);
public Class<?> getPOJOClass();
public void setPOJOClass(Class<?> pojoClass);
public Map<String, Method> getMethods();
}

The POJOName attribute will be set to the name from the class attribute, helloworld. HelloWorldImpl in our example. This will later on be linked to the helloworld.Hello-WorldImpl class (shown as box 4) during the model-resolution phase.

To create instances of the implementation.pojo model you’ll need a factory, as shown in the following snippet:

public interface POJOImplementationFactory {
POJOImplementation createPOJOImplementation();
}

The Java model factory for implementation.pojo must be registered with the Tuscany runtime. Add the model class and factory and the META-INF/services POJOImplementationFactory file to your list of files:

src/main/java/
pojo/
impl/
POJOImplementationFactoryImpl.java
POJOImplementationImpl.java
POJOImplementation.java
POJOImplementationFactory.java

src/main/resources/
META-INF/services/
pojo.POJOImplementationFactory

The POJOImplementationFactory file contains a single line as follows:

pojo.impl.POJOImplementationFactoryImpl

Let’s check our to-do list. By now we have the XML model, the Java model, and the capability to create implementation.pojo models through the factory. The next step is to define a StAX-based processor that can read the XML and map it to the Java model and vice versa. This is shown in figure 14.2, box 7.

 

StAX: the Streaming API for XML

The Streaming API for XML (StAX) is a Java-based API for pull-parsing XML. It’s defined by JSR 173: http://jcp.org/en/jsr/detail?id=173.

 

The following code snippet shows the detail of the StAX processor for implementation.pojo. It extends the org.apache.tuscany.sca.contribution.processor.StAXArtifactProcessor interface. Two methods provide the keys for looking up the processor. The getArtifactType() method returns the XML QName for <implementation.pojo>. The getModelType() method returns the POJOImplementation Java model class. Using these keys, Tuscany can find the right processor when it comes across <implementation.pojo/> when reading a composite file and when it comes across POJOImplementation when writing out the in-memory model to XML.

public class POJOImplementationProcessor implements
StAXArtifactProcessor<POJOImplementation> {
private static final QName IMPLEMENTATION_POJO
= new QName("http://pojo", "implementation.pojo");
...
public QName getArtifactType() {
return IMPLEMENTATION_POJO;
}

public Class<POJOImplementation> getModelType() {
return POJOImplementation.class;
}
...
}

The POJOImplementationProcessor needs to access other classes in the Tuscany extension point registry such as the POJOImplementationFactory. The constructor of the POJOImplementationProcessor includes the ModelFactoryExtensionPoint as an argument to make this possible. This is shown in the following code snippet. The Tuscany runtime will inject an instance of the model factory extension point from the extension registry.

public POJOImplementationProcessor(ModelFactoryExtensionPoint
modelFactories, Monitor monitor) {
...
pojoImplementationFactory =
modelFactories.getFactory(POJOImplementationFactory.class);
...
}

Now you’re ready to provide the code to read the XML model and to create its corresponding Java model. The code sample for the read() method of implementation.pojo is shown here:

public POJOImplementation read(XMLStreamReader reader)
throws ContributionReadException, XMLStreamException {
POJOImplementation implementation =
pojoImplementationFactory.createPOJOImplementation();
...
String className = reader.getAttributeValue(null, "class");
implementation.setPOJOName(className);

...
return implementation;
}

This code reads the class attribute from the <implementation.pojo/> element.

You’ll also need to provide a write() method whose purpose is to create XML output based on a Java model. The code snippet for implementation.pojo is as follows:

public void write(POJOImplementation implementation,
XMLStreamWriter writer)
throws ContributionWriteException, XMLStreamException {
writer.writeStartElement(IMPLEMENTATION_POJO.getNamespaceURI(),
IMPLEMENTATION_POJO.getLocalPart());

if (implementation.getPOJOName() != null) {
writer.writeAttribute("class", implementation.getPOJOName());
}

writer.writeEndElement();
}

You’ve finished implementing the StAX processor for implementation.pojo. Again, you’ll need to register it with a Tuscany extension point. Add a POJOImplementation Processor and a StAXArtifactProcessor file to your collection of files under META-INF/ services as follows:

src/main/java/pojo/impl/
POJOImplementationProcessor.java
src/main/resources/
META-INF/services/
org.apache.tuscany.sca.contribution.processor.StAXArtifactProcessor

This StAXArtifactProcessor file contains a single line as follows:

pojo.impl.POJOImplementationProcessor;qname=http://pojo#implementation.pojo,
model=pojo.POJOImplementation

We’ve now explained how to develop the XML model and the Java model and provided a StAX processor that transforms one form to another. Let’s now look into how this information is used to create a component.

14.2.3. Determining the component type for implementation.pojo

Tuscany needs to be able to understand how to map the POJO, specified by implementation.pojo, to an SCA component’s services, references, and properties.

So far the Tuscany runtime knows only the name of the class associated with implementation.pojo. In order to access the class object, the class name needs to be linked to a real POJO object. Although this seems like a simple step for POJO, the resolution of artifact names to real objects can be different and more complicated for each implementation type. Therefore, for each implementation type you’ll need to provide code in the resolve() method of the model processor to enable the Tuscany runtime to find the necessary artifacts. The following listing shows how the class attribute for implementation.pojo is resolved to the class object through the ModelResolver.

Listing 14.2. The first half of the implementation.pojo processor resolve() method
public void resolve(POJOImplementation implementation,
ModelResolver resolver)
throws ContributionResolveException {
ClassReference classReference = new
ClassReference(implementation.getPOJOName());
classReference = resolver.resolveModel(ClassReference.class,
classReference);
Class<?> pojoClass = classReference.getJavaClass();
if (pojoClass == null) {
throw new ContributionResolveException("Class not resolved: " +
implementation.getPOJOName());
}
implementation.setPOJOClass(pojoClass);
...
}

The resolve() method creates a ClassReference object from the name of the POJO and passes it to the model resolver. The model resolver finds a ClassReference object from the contribution that matches the name of the POJO. The returned ClassReference already has the Java class loaded. The method finally sets the POJO-Class on the POJOImplementation model object.

Next, you’ll need to create the component and find the constructs that represent services, references, and properties. The names for these attributes are either available in the componentType file or, depending on the underlying technology, can be introspected from the implementation itself. Let’s see how this can be done for a POJO.

Listing 14.3 shows the second half of the implementation.pojo resolve() method. It uses the POJOImplementation model to determine if a componentType file exists. If so, you’ll use the information available in the componentType file to create the component. Otherwise, you’ll introspect the POJO implementation class and create a component based on the information found.

Listing 14.3. The second half of the implementation.pojo processor resolve() method
public void resolve(POJOImplementation implementation,
ModelResolver resolver) throws ContributionResolveException {
...
ComponentType componentType = assemblyFactory.createComponentType();
componentType.setUnresolved(true);
componentType.setURI(implementation.getURI() + ".componentType");
componentType = resolver.resolveModel(ComponentType.class, componentType);

if (!componentType.isUnresolved()) {
implementation.getServices().addAll(componentType.getServices());
implementation.getReferences().addAll(componentType.getReferences());
implementation.getProperties().addAll(componentType.getProperties());
} else {
Service service = assemblyFactory.createService();
service.setName(pojoClass.getSimpleName());
JavaInterface javaInterface;
try {
javaInterface = javaFactory.createJavaInterface(pojoClass);
} catch (InvalidInterfaceException e) {
throw new ContributionResolveException(e);
}
JavaInterfaceContract interfaceContract =
javaFactory.createJavaInterfaceContract();
interfaceContract.setInterface(javaInterface);
service.setInterfaceContract(interfaceContract);
implementation.getServices().add(service);
}

implementation.setUnresolved(false);
}

Notice that we’re using the implementation URI to locate the component type file. It’s expected to be found at helloworld/HelloWorldImpl.componentType. This URI is formed from the location of the class file by replacing the .class suffix .componentType.

You now have an in-memory representation of the component type for implementation.pojo. The next step is to provide a hook for the implementation type so that the Tuscany runtime can invoke, start, and stop implementation instances.

14.2.4. Controlling implementation.pojo invocation and lifecycle

Each component implementation type has an associated lifecycle that allows it to be started, invoked, and stopped. Tuscany defines an ImplementationProvider interface for this purpose as well as an ImplementationProviderFactory interface to create instances of the implementation provider. Each extension provides its own implementation of the ImplementationProvider and ImplementationProviderFactory interfaces. The class that implements the ImplementationProviderFactory interface for the given extension is registered with Tuscany.

The following code shows the POJOImplementationProviderFactory class, which creates POJOImplementation providers:

public class POJOImplementationProviderFactory implements
ImplementationProviderFactory<POJOImplementation> {

public
POJOImplementationProviderFactory(ExtensionPointRegistry registry){
}

public Class<POJOImplementation> getModelType() {
return POJOImplementation.class;
}

public ImplementationProvider createImplementationProvider(
RuntimeComponent component,
POJOImplementation implementation) {
return new POJOImplementationProvider(component, implementation);
}
}

Notice that the constructor of this class has Tuscany ExtensionPointRegistry as a parameter. This is the source for finding any extension point, and therefore extension, that’s registered with the Tuscany runtime. Extension types are indexed by their model class name in the registry. Therefore, the getModelType() method returns the POJOImplementation model class that’s used by the Tuscany runtime to locate the provider factory.

You’ll add the provider factory and register it by adding an Implementation-ProviderFactory file to the list of files in META-INF/services:

src/main/java/
pojo/provider/
POJOImplementationProviderFactory.java
src/main/resources/
META-INF/services/
org.apache.tuscany.sca.provider.ImplementationProviderFactory

The content of the ImplementationProviderFactory file is a single line, as follows:

pojo.provider.POJOImplementationProviderFactory;
model=pojo.POJOImplementation

You’ve now enabled the Tuscany runtime to create components using implementation.pojo. Next, we’ll provide the code that handles the invocation, start, and stop of the component instances by implementing the ImplementationProvider interface, as shown here.

Listing 14.4. The implementation provider for implementation.pojo

In this case the provider constructor creates a single instance of the implementation class. Because there’s one provider instance per component, a single instance of the POJO class is created for each component. Alternative implementation creation algorithms can be created as required.

The start() method prepares the implementation artifacts for the extension; for example, compile the BPEL script for implementation.bpel, or handle dependency injection of references and properties for implementation.java.

The stop() method is called when a component is stopped. It provides the code to destroy the implementation instance and release its resources.

An instance of an invoker is required for each service operation. The Tuscany runtime calls the createInvoker() method on the POJOImplementationProvider to create an instance of the invoker that’s shared for all requests to the given operation. The invoker takes the incoming message from the service binding and passes it on to the appropriate operation in the implementation instance.

As you’ve seen, an SCA service can offer multiple operations. The invoke method in the Invoker interface, shown in the following snippet, is responsible for dispatching requests to the appropriate operation. The handling of the invoke operation is dependent on the technology used. As you can see, you’ll need to provide an invoker specific to POJO.

class POJOImplementationInvoker implements Invoker {
...
public Message invoke(Message msg) {
try {
// Use java reflection call to invoke the method
// and set the result to the message body
msg.setBody(method.invoke(pojoInstance,
(Object[])msg.getBody()));
} catch (InvocationTargetException e) {
msg.setFaultBody(e.getCause()));
} catch (Exception e) {
throw new ServiceRuntimeException(e);
}
return msg;
}
}

Add the provider and invoker files to your list of files in the implementation.pojo module:

src/main/java/
pojo/provider/
POJOImplementationInvoker.java
POJOImplementationProvider.java

You’ve now completed the work of extending Tuscany to support the POJO implementation type. In the next section, let’s look at how all the pieces that we talked about so far come together in an end-to-end picture.

14.2.5. The end-to-end picture for the POJO implementation type

Figure 14.3 demonstrates the relationship among the classes that together enable the implementation.pojo extension in the Tuscany runtime.

Figure 14.3. The classes that describe the extension point and extensions that collaborate to define the implementation.pojo extension

During the contribution-processing phase, the StAX processor sees the <implementation.pojo> element in the composite file (1). Tuscany looks up the POJOImplementationProcessor (3) from the StAXArtifactProcessorExtensionPoint (D) using the QName of implementation.pojo. The POJOImplementationProcessor gets POJOImplementationFactory (5) from ModelFactoryExtensionPoint (B) and uses it to create an instance of POJOImplementation (2). The processor then parses the XML to populate POJOImplementation. If schema validation is enabled, the implementation-pojo.xsd file (4) is loaded by ValidationSchemaExtensionPoint (C), and it validates the implementation.pojo element as StAX parsing continues.

After the composite file is loaded, the POJOImplementationProcessor resolves the POJOImplementation object to get the Java class (6) and the accompanying componentType side file (7). Through the parsing of (7) and/or introspection of (6), it builds a component type (8) for the POJO implementation.

When the component is activated by the Tuscany runtime, the ProviderFactoryExtensionPoint (E) is queried using the model class (2), and POJOImplementationProviderFactory (9) is found. The Tuscany runtime calls the POJOImplementationProviderFactory to create an instance of the POJOImplementationProvider (10) for each component implementation instance. The POJOImplementationProvider’s start() method is invoked to set up the implementation instance when the component is started. The Tuscany runtime also calls the POJOImplementationProvider’s createInvoker() method to create an Invoker (11) for each service operation and add it to the invocation chain (12). When the request comes in, the POJOImplementationInvoker will be called to dispatch the request to the POJO instance and get the response back to the caller. When the component is stopped, the POJOImplementationProvider’s stop() method is triggered to clean up the resources associated with the implementation instance.

Now that you have a good understanding of how all the pieces work together, let’s package implementation.pojo and make it available as an implementation type.

14.2.6. Packaging the POJO implementation type

In order to make implementation.pojo available to Tuscany users, we’ll need to package the extension into a JAR file and include it in the lib folder of the Tuscany distribution. The final package will contain the resources and Java classes summarized in table 14.1.

Table 14.1. The artifacts required to implement our new implementation.pojo extension

Resource or Java file

Description

sample-implementation-pojo.xsd The XML schema for <implementation.pojo>
META-INF/services/pojo.POJOImplementationFactory Service provider file to register POJOImplementationFactoryImpl
META-INF/services/org.apache.tuscany.sca.contribution.processor.StAXArtifactProcessor Service provider file to register POJOImplementationProcessor
META-INF/services/org.apache.tuscany.sca.contribution.processor.ValidationSchema Service provider file to register sample-implementation-pojo.xsd for schema validation
META-INF/services/org.apache.tuscany.sca.provider.Implementation ProviderFactory Service provider file to register POJOImplementationProviderFactory
pojo/POJOImplementation.java Java interface for POJO implementation model
pojo/POJOImplementationFactory.java Java interface for the model factory
pojo/impl/POJOImplementationFactoryImpl.java Implementation class for the factory
pojo/impl/POJOImplementationImpl.java Implementation class for the model
pojo/impl/POJOImplementationProcessor.java StAX processor for <implementation.pojo>
pojo/provider/POJOImplementationProviderFactory.java Implementation provider factory for implementation.pojo
pojo/provider/POJOImplementationProvider.java Implementation provider for implementation.pojo
pojo/provider/POJOImplementationInvoker.java Implementation invoker for implementation.pojo

You may now wonder whether you can follow the same steps to add other extension types to Tuscany. The answer is yes, with some minor differences. Next, let’s look at how you can add a new binding type to Tuscany.

14.3. Developing a new binding type

In this section we’re going to show you how to develop a new binding type. If the TuscanySCATours company wanted to connect its Payment component to the CreditCardPayment component using SMTP, FTP, DCE, XML-RPC, or any one of many communication protocols that the Tuscany SCA Java runtime doesn’t support out of the box, they’d have to write a new binding type. As with implementation types, the advantage of creating a new binding type is that anyone else who needs to use the same protocol can do so by configuring their SCA services or references with the new <binding/> element.

Here we’re going to explain how to build a new binding extension to wrap protocol-specific code. We’ll do this through the implementation of a simple binding that handles a protocol of our own invention called the echo protocol. For a component running in isolation, the echo binding returns the first parameter of any outgoing message. If a reference is wired to a service, then the echo binding will forward the message, and the service is free to return whatever it likes.

We’re using the echo binding because the code for the binding.echo extension, and examples of its use, can be found in the Tuscany source or binary distribution in the samples/binding-echo-extension directory.

There are two distinct steps for adding a new binding type. First, you’ll find some code that handles the network protocol for the new binding, which, to Tuscany, is a black box. For example, for binding.ws, Tuscany uses Apache Axis to handle the SOAP Web Services protocol. Then you’ll enable the binding through Tuscany extension points.

We won’t talk through the details of the protocol-specific code here, but there are some key questions that you’ll need to think through when integrating the protocol-specific code with the binding extension. Think of the binding as a pipe that handles communication between SCA and non-SCA components. At one end it receives the message (inbound invocation request), and on the other end it delivers it (outbound invocation request). The following are the types of capabilities that the binding should be able to provide:

  • Dispatching an outbound SCA invocation request to the underlying protocol handler— The dispatching can be done in different styles depending on what the protocol requires. You might use RPC APIs to invoke remote services or use messaging APIs to send the request and receive the response. For example, the Web Services reference binding maps an SCA request to a SOAP envelope, connects to the HTTP endpoint, sends the SOAP message, receives a SOAP message as response, extracts the data from the SOAP response, and gives it back to SCA.
  • Handling inbound invocations that are calls from the protocol stack to the service— An SCA binding usually registers a listener with the underlying stack that’s called to route a request to the SCA component. Examples of listeners include Servlet, for the HTTP binding, and JMSServiceListener, for the JMS binding.
  • Mapping the data format that’s understood by an implementation type to the wire format required by the protocol stack and vice versa— Each protocol has its own requirement for handling data on the wire; for example, RMI and EJB bindings require the data to be Java serializable. Some protocols already provide a well-defined wire format; for example, Web Services with SOAP binding uses SOAP envelopes. The Tuscany runtime handles data format transformation through its databinding extension type, as discussed in detail in chapter 9. The interface for the binding and component implementation type defines the data format for each side of the data transformation. The Tuscany runtime reads these interfaces to determine the data format required and uses the appropriate databinding to handle the transformation seamlessly. Tuscany provides a rich set of databindings and can be easily extended to support additional ones if needed.

Now you have a rough idea of what we’re trying to achieve in creating a new binding extension. Let’s get started with the first step of creating the binding schema.

14.3.1. Adding the binding.echo XML schema

When defining the XML model for a binding, you’ll need to think about how the binding will be used. Is it going to be configured for references only, services only, or both? A binding configured for references defines how an SCA component communicates with a service provider via an SCA reference. For example, binding.ws enables an SCA component to call a web service over SOAP/HTTP. A binding configured for service defines how an SCA service exposes its business function to other services over a particular protocol. For example, an SCA service with binding.ws will be published as a web service, and it can then be accessed by a JAX-WS client, a .NET client, or an SCA component using the Web Services protocol.

In our example, binding.echo will be configured for references and services. We define the schema for binding.echo in a file called sample-binding-echo.xsd. The schema is optional, but we’ll include it in this example so that the Tuscany runtime can use it to validate the syntax of binding.echo as it appears in the composite file. The following listing shows the XML schema for the <binding.echo> element.

Listing 14.5. XML schema for binding.echo defined in sample-binding-echo.xsd
<schema xmlns="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://echo"
xmlns:sca="http://www.osoa.org/xmlns/sca/1.0"
xmlns:e="http://echo"
elementFormDefault="qualified">
<import namespace="http://www.osoa.org/xmlns/sca/1.0"/>

<element name="binding.echo" type="e:EchoBinding"/>

<complexType name="EchoBinding">
<complexContent>
<extension base="sca:Binding">
... <!-- binding specific elements or attributes can go here -->
</extension>
</complexContent>
</complexType>
</schema>

Because binding.echo is a simple binding, you’ll only need to extend SCA’s programming model to define a name and type for this binding. Add the schema file and register it with Tuscany’s schema validation extension point, in the same way as you did with implementation.pojo, by adding a line to a ValidationSchema file in the META-INF/services directory. The two files to add are these:

src/main/resources/
sample-binding-echo.xsd
META-INF/services/
org.apache.tuscany.sca.contribution.processor.ValidationSchema

The ValidationSchema file contains a single line, as follows:

sample-binding-echo.xsd

Now that you have the XML schema for binding.echo, you’ll need to define how it can be configured in the composite file and used at runtime.

14.3.2. Adding the binding.echo XML processor

The XML processor is used to read, write, and resolve <binding.echo> elements found in a composite file. In figure 14.4, box 1, we’ll extend the SCA programming model for binding types and introduce the binding.echo extension. The schema for the Echo binding that we defined in the previous section is shown in box 2. It will be used by the Tuscany runtime to validate the syntax used in box 1. Two Java interfaces are implemented for the Echo binding type. The first is the model interface in box 3. This interface extends the base Binding interface that’s marked as box 5. The second is the factory interface shown in box 4. This is used to create instances of Echo-Binding. Unlike implementation.pojo, where we constructed specific XML processors, here we’ll use the Tuscany generic StAX processor shown in box 6 to handle the XML/Java conversions.

Figure 14.4. The artifacts that together are used to describe and populate an in-memory model of the binding.echo extension

Notice the uri attribute in box 1. All binding types have a uri attribute that represents the address of the target service endpoint. In the case of binding.echo the uri is used in a local in-memory cache to locate the target service.

A more complicated binding will have other attributes. For example, binding.ws supports an attribute called wsdlElement to point to WSDL-based configuration for the binding.

Typically, the same model can be applied to both references and services. But it’s possible for the model to be different depending on the type of the protocol supported by each. For example, binding.jms can use connectionFactory for references and activationSpec for services. These two attributes are mutually exclusive.

Let’s look at the code from figure 14.4 in a little more detail starting with box 3. We’ll provide a new Java interface specific to the Echo binding type that extends an existing Tuscany interface called org.apache.tuscany.sca.assembly.Binding. The code is shown in the following snippet:

package echo;
import org.apache.tuscany.sca.assembly.Binding;
public interface EchoBinding extends Binding {

}

We’ll then provide a factory to create instances of the Echo binding type. This is shown in the following snippet. Notice that both the model and factory interfaces can be implemented using simple JavaBeans.

public interface EchoBindingFactory {
EchoBinding createEchoBinding();
}

The Java model factory for binding.echo must be registered with the Tuscany runtime. Add the model class and factory and the META-INF/services echo.EchoBinding-Factory file to your list of files:

src/main/java/
echo/
EchoBinding.java
EchoBindingFactory.java
impl/
EchoBindingFactoryImpl.java
EchoBindingImpl.java
src/main/resources/
META-INF/services/
echo.EchoBindingFactory

The META-INF/service echo.EchoBindingFactory file contains a single line as follows:

echo.impl.EchoBindingFactoryImpl

So far, we’ve created the XML and Java model for the Echo binding. We’ll need to provide a StAX-based processor that bridges the two models. This is shown in box 6 of figure 14.4. The good news is that we don’t have to write a new processor because the Echo binding is modeled as a JavaBean. Instead, we can use the DefaultBeanModel-Processor that Tuscany provides.

 

Using Tuscany’s generic StAX processor

You can use Tuscany’s generic StAX processor if the implementation type’s XML model uses attributes of simple types and its Java model follows the JavaBeans pattern. Tuscany’s generic StAX processor uses JavaBean property accessors to read/write the properties from/to the XML document. You can register the generic StAX processor with the following text (using your qname/model/factory names instead of CRUD):

org.apache.tuscany.sca.assembly.xml.DefaultBeanModelProcessor;qname=
http://
crud#implementation.crud,model=crud.CRUDImplementation,factory=crud.
CRUDImplementationFactory

 

We’ll register the StAX artifact processor with the Tuscany runtime to enable it to handle the transformation between Java and XML models for the Echo binding. This is done by including a StAXArtifactProcessor file under META-INF/services as follows:

src/main/resources/
META-INF/services/
org.apache.tuscany.sca.contribution.processor.StAXArtifactProcessor

This file configures the generic Tuscany StAX processor using the following line:

org.apache.tuscany.sca.assembly.xml.DefaultBeanModelProcessor;qname=http://
echo#binding.echo,model=echo.EchoBinding,factory=echo.EchoBindingFactory

Notice that the content of the file includes the processor class name called Default-BeanModelProcessor. The qname attribute defines the XML QName of the binding element, http://echo#binding.echo in this case. The model attribute points to the class name of the binding’s Java interface. The factory attribute is used to locate the Echo-BindingFactory that’s used to create instances of the configured Echo binding.

You’ve now completed the steps for defining the XML and Java model and providing a processor to handle the transformation. The next step is to enable the runtime to invoke and manage binding.echo instances.

14.3.3. Controlling binding.echo invocation and lifecycle

Each binding extension requires a provider factory that’s responsible for creating a ReferenceBindingProvider to handle each outbound request and for creating a ServiceBindingProvider for handling each inbound request. Listing 14.6 shows the implementation of EchoBindingProviderFactory, where we’ll provide the code for creating both reference and service providers. The Java model for the binding.echo extension is used to look up the provider factory in Tuscany’s general Provider-FactoryExtensionPoint.

Listing 14.6. EchoBindingProviderFactory
public class EchoBindingProviderFactory implements
BindingProviderFactory<EchoBinding> {

private MessageFactory messageFactory;

public EchoBindingProviderFactory(ExtensionPointRegistry
extensionPoints){
ModelFactoryExtensionPoint factories =
extensionPoints.getExtensionPoint(ModelFactoryExtensionPoint.class);
this.messageFactory = factories.getFactory(MessageFactory.class);
}

public ReferenceBindingProvider createReferenceBindingProvider(
RuntimeComponent component,
RuntimeComponentReference reference,
EchoBinding binding) {
return new EchoReferenceBindingProvider(component,
reference,
binding);
}

public ServiceBindingProvider createServiceBindingProvider(
RuntimeComponent component,
RuntimeComponentService service,
EchoBinding binding) {
return new EchoServiceBindingProvider(component,
service,
binding,
messageFactory);
}
public Class<EchoBinding> getModelType() {
return EchoBinding.class;
}
}

The constructor method takes the ExtensionPointRegistry as a parameter. This gives the factory access to other extension points and therefore other extensions, such as the factory instance required to create a Tuscany Message.

You’ll need to add the EchoBindingProviderFactory and register it with the Tuscany runtime. The new files you’ll need are as follows:

src/main/java/
echo/provider/
EchoBindingProviderFactory.java
src/main/resources/
META-INF/services/
org.apache.tuscany.sca.provider.BindingProviderFactory

The BindingProviderFactory file in META-INF/service contains the following line:

echo.provider.EchoBindingProviderFactory;model=echo.EchoBinding

By now the Tuscany runtime knows how to create instances of binding.echo and has a provider factory for creating service and reference providers. Let’s look at the providers in more detail.

Coding the Service Binding Provider for Binding.Echo

The next step is to implement the code that handles service requests for binding.echo. The two files you’ll add are as follows:

src/main/java/
echo/server/
EchoServiceListener.java
echo/provider/
EchoServiceBindingProvider.java

The EchoServiceBindingProvider class is shown in the following listing.

Listing 14.7. EchoServiceBindingProvider
class EchoServiceBindingProvider implements ServiceBindingProvider {

private RuntimeComponent component;
private RuntimeComponentService service;
private EchoBinding binding;
private MessageFactory messageFactory;

...

public InterfaceContract getBindingInterfaceContract() {
return service.getInterfaceContract();
}

public boolean supportsOneWayInvocation() {
return false;
}
public void start() {
RuntimeComponentService componentService =
(RuntimeComponentService)service;
RuntimeWire wire = componentService.getRuntimeWire(binding);
InvocationChain chain = wire.getInvocationChains().get(0);


// Register with the hosting server
String uri = binding.getURI();
EchoServer.getServer().register(uri,
new EchoServiceListener(chain.getHeadInvoker(),
messageFactory));
}


public void stop() {
String uri = component.getURI() + "/" + binding.getName();
EchoServer.getServer().unregister(uri);
}


}

The EchoServiceBindingProvider class implements a few methods that are essential for handling service requests for the Echo binding. These methods will be accessed by the Tuscany runtime.

  • The start() method is invoked when a component service gets started. It creates and publishes the service endpoint to the underlying protocol stack. Typically the service binding provider registers a listener to the underlying protocol stack to receive the requests from the transport. Examples include Servlet for HTTP-based protocols or a JMSServiceListener for JMS. The listener is then responsible for dispatching the inbound call to the SCA service that’s configured with the binding type. We don’t reproduce the EchoServiceListener here because it’s specific to the Echo protocol, but you can see it by looking at the sample in the Tuscany distribution.
  • The stop() method is invoked when a component service is stopped. It’ll ask the underlying stack to remove the endpoint and release resources associated with the service.
  • The getBindingInterfaceContract() method returns the interface that represents the type of data used by the binding extension type. Based on this information and the target destination data format, the Tuscany runtime decides what kind of databinding to use to handle the transformation of data as it travels between the two points. For example, a Web Services binding based on Axis2 uses WSDL as the binding interface with an AXIOM databinding, whereas the corresponding component reference or service can use a Java interface with a JAXB or SDO databinding. This frees the binding code from complicated transformation code and allows it to handle data uniformly. Please refer to chapter 9 for more details on databinding.
  • The supportsOneWayInvocation() method tells the Tuscany runtime whether the binding can support one-way invocation natively or needs the Tuscany runtime to handle asynchronous invocation. If help is needed, the Tuscany runtime will use a thread pool to schedule one-way operations.

If you recall, our Echo binding handles both service and references. We just explained how the service provider works for binding.echo. Let’s look at the code for the reference provider.

Coding Reference Binding Provider for Binding.Echo

The Echo binding needs a reference provider to handle outbound invocation requests. You’ll add two more new files as follows:

src/main/java/
echo/provider/
EchoBindingInvoker.java
EchoReferenceBindingProvider.java

The EchoReferenceBindingProvider class is shown here.

Listing 14.8. EchoReferenceBindingProvider
class EchoReferenceBindingProvider implements ReferenceBindingProvider {

private RuntimeComponentReference reference;
private EchoBinding binding;

EchoReferenceBindingProvider(RuntimeComponent component,
RuntimeComponentReference reference,
EchoBinding binding) {
this.reference = reference;
this.binding = binding;
}

public Invoker createInvoker(Operation operation) {
...
return new EchoBindingInvoker(binding.getURI());
}

public boolean supportsOneWayInvocation() {
return false;
}

public InterfaceContract getBindingInterfaceContract() {
return reference.getInterfaceContract();
}

public void start() {
}

public void stop() {
}
}

The following methods in the EchoReferenceBindingProvider class will provide the supporting code to handle the outbound invocation. This code is invoked by the Tuscany runtime.

  • The start() method is invoked by the Tuscany runtime when a component reference is started. This method allocates resources such as a connection pool needed for the outbound invocation. No extra resources are required by the Echo binding, and this method does nothing here.
  • The stop() method is invoked by the Tuscany runtime when a component reference is stopped by the Tuscany runtime. It releases resources associated with the reference.
  • The createInvoker() method is used by the Tuscany runtime to create an Invoker for the reference binding. This Invoker is added to the invocation chain of the SCA reference. More detail is provided in the next section.
  • The getBindingInterfaceContract() method returns the interface that represents the data types supported by the binding. In the Echo binding case, it can return either null or the interface contract from the component reference because it doesn’t require data transformations.
  • The supportsOneWayInvocation() method tells the Tuscany runtime whether the binding can support one-way invocation natively without the Tuscany runtime’s built-in asynchronous facility.

The Invoker is responsible for dispatching the outbound invocation requests for the given SCA reference to the protocol stack. It can be viewed as a bridge between the SCA world and the protocol handler. It receives the input parameters from the calling component reference in the form of an SCA message. It then uses protocol-specific APIs to dispatch the request and the input data to the protocol handler. Once the response comes back from the protocol layer, the Invoker extracts the output data into an SCA message that gets sent to the component implementation.

If you recall, a service can consist of multiple operations. In Tuscany, an instance of the binding Invoker is associated with each operation. The code for EchoBindingInvoker is shown in the following snippet:

class EchoBindingInvoker implements Invoker {
private String uri;

EchoBindingInvoker(String uri) {
this.uri = uri;
}

public Message invoke(Message msg) {
try {
System.out.println("Passing thro invoker...");
Object[] args = msg.getBody();
Object result = EchoServer.getServer().call(uri, args);
msg.setBody(result);
} catch (Exception e) {
msg.setFaultBody(e);
}
return msg;
}
}

In the case of the Echo binding samples, the Invoker doesn’t do anything particularly interesting. It invokes the call() operation on a service retrieved from the EchoServer. This has the effect of sending the message to the target service or, if there’s no target service, of returning the first input parameter. In a real binding you’re free to add any code you need here to interact with the protocol stack.

You’ve now completed the steps required to add the Echo binding. Now that you understand the basic building blocks, let’s look at the other bindings in the Tuscany distribution to see how integration with real protocol stacks is performed.

In the next section, we’ll look at the end-to-end picture of how all the pieces that we talked about relate to one another.

14.3.4. The end-to-end picture for the Echo binding type

Figure 14.5 provides an end-to-end view of all the artifacts required to create the Echo binding and the relationship of those artifacts with one another. The boxes identified with a letter represent a Tuscany extension point. The numbered boxes represent the code required to enable the extension points for a given binding type.

Figure 14.5. The different extension classes that collaborate and cooperate to support the our new binding type, binding.echo

As shown in figure 14.5, the Tuscany extension point registry (A) maintains a list of all the extension points. The Echo binding type contributes code to all extension points (B, C, and D). Let’s start with box 1 in the upper-left-hand corner.

During the contribution processing phase, the StAX processor sees the <binding.echo> element in the composite file (1). Tuscany looks up the DefaultBeanModelProcessor (3) from the StAXArtifactProcessorExtensionPoint (D) using the QName of binding.echo. The processor gets EchoBindingFactory (5) from ModelFactoryExtensionPoint (B) and uses it to create an instance of EchoBinding (2). The processor then parses the XML to populate EchoBinding. If schema validation is enabled, the sample-binding-echo.xsd file (4) is loaded by the ValidationSchemaExtensionPoint (C), and it validates the binding.echo element as StAX parsing continues.

When the component is activated by the Tuscany runtime, the ProviderFactoryExtensionPoint (E) is queried using the model class (2) and EchoBindingProviderFactory (7) is found.

If binding.echo is used to handle a reference, the Tuscany runtime calls the EchoBindingProviderFactory to create an instance of EchoReferenceBindingProvider (10) for the reference binding. The EchoReferenceBindingProvider’s start() method is invoked to set up the reference binding provider instance when the component is started. The Tuscany runtime also calls the EchoReferenceBindingProvider’s createInvoker() method to create an Invoker (11) for each operation and adds it to the invocation chain (13) for the reference. When the request comes in, the Echo-BindingInvoker will be called to dispatch the request to the “echo” protocol layer and get the response back to the caller. When the component is stopped, the Echo-ReferenceBindingProvider’s stop() method is triggered to clean up the resources associated with the reference instance.

If binding.echo is used to handle a service, the Tuscany runtime calls the EchoBindingProviderFactory to create an instance of EchoServiceBindingProvider (8) for the service binding. The EchoServiceBindingProvider’s start() method is invoked to create an EchoServiceListener (9) when the component is started. When a request comes from the “echo” protocol layer, the EchoServiceListener is triggered. It extracts the input data from the underlying message and dispatches to the head invoker of the invocation chain (12) for the service. When the component is stopped, the EchoServiceBindingProvider’s stop() method is triggered to clean up the resources associated with the service instance, including removing the Echo-ServiceListener from the “echo” protocol layer.

Now that you have a good understanding of how all the pieces work together to enable Echo binding in Tuscany, let’s package binding.echo and make it available as a binding type.

14.3.5. Packaging the echo binding type

Package the Java classes and resource files for the binding.echo extension into a JAR file and include it in the lib folder of the Tuscany distribution. The final package is shown in table 14.2.

Table 14.2. The artifacts required to implement your new binding.echo extension

Resource or Java file

Description

META-INF/services/echo.EchoBindingFactory Service provider file to register the model factory for EchoBinding
META-INF/services/org.apache.tuscany.sca.contribution.processor. StAXArtifactProcessor Service provider file to register the StAX processor for <binding.echo>
META-INF/services/org.apache.tuscany.sca.contribution.processor. ValidationSchema Service provider file to register sample-binding-echo.xsd for schema validation
META-INF/services/org.apache.tuscany.sca.provider.BindingProviderFactory Service provider file to register the binding provider factory for binding.echo
sample-binding-echo.xsd XML schema for <binding.echo>
echo/EchoBindingFactory.java Java interface for the factory to create EchoBinding
echo/EchoBinding.java Java interface for the binding.echo model
echo/impl/EchoBindingImpl.java Implementation class for EchoBinding
echo/impl/EchoBindingFactoryImpl.java Implementation class for the factory
echo/provider/EchoBindingInvoker.java Binding Invoker
echo/provider/EchoReferenceBindingProvider.java Reference binding provider
echo/provider/EchoServiceBindingProvider.java Service binding provider
echo/provider/EchoBindingProviderFactory.java Binding provider factory for binding.echo
echo/server/EchoServer.java Mocked Echo server
echo/server/EchoServiceListener.java Mocked Echo listener

In this section, we showed you how to add a new binding type to Tuscany by using a simple Echo sample. Any user who has access to this new binding.echo extension can extend their composite application descriptions to include the <binding.echo/> element.

You can use the techniques shown here of defining a model and creating processors and providers to build your own binding extensions. Tuscany has plenty of binding examples that you can refer to for inspiration, for example, Web Services, JMS, EJB, RMI, CORBA, JSON-RPC, and more.

We encourage you to think about contributing any new binding, or implementation, extensions back to the Tuscany community. In that way the Tuscany SCA runtime increases its coverage of supported binding and implementation technologies and becomes more attractive to a wider group of users.

14.4. Summary

SCA’s extensible programming model offers the freedom of choice for selecting any technology for developing components, handling data, and handling communication protocols and policies.

In this chapter, you learned that Apache Tuscany’s implementation of SCA is backed by a flexible, modular architecture that can be extended to support any technology. Adding support for a new technology requires you to determine how the technology is used to form SCA composites. This tells you which one of the supported extension types, such as implementation type and binding type, the new extension belongs to. You also learned that whereas the SCA assembly specification describes a specific extension configuration for implementations, interfaces, policy, and bindings, Apache Tuscany provides additional extension types that enable you to add support for new databindings.

We looked, at a high level, at what is required to add a new extension, and we studied the steps required to create a POJO implementation type and an Echo binding type. In doing so we presented the key interfaces and methods required for enabling an extension type through Tuscany’s extension point mechanism. You’re now ready to create new bindings or implementation types yourself.

Tuscany already supports many different extension types. But it’s always possible that some technology isn’t supported. As you go through the process of adding a new extension type, we encourage you to share your thoughts and to contribute the extension to the Tuscany project so that other members of the Tuscany community can benefit.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.188.109.205