Chapter 13. Tuscany runtime architecture

This chapter covers

  • The architectural building blocks that make up the Tuscany runtime
  • The complete flow of how Tuscany processes an SCA application
  • The key patterns that make the Tuscany runtime flexible and extensible

Throughout this book we’ve promised that Tuscany handles the complexity associated with integrating various technologies in a distributed environment. You focus on developing your business logic and Tuscany handles the rest. This chapter explains the details of Tuscany’s flexible architecture and reveals how the complexity is handled.

This sounds like a chapter that should appear at the start of the book. But if you’re a user of Tuscany and SCA and you just want to build SCA applications, you don’t necessarily need to know how the Tuscany runtime itself is architected. It’s useful to know, though, if you intend to embed or extend Tuscany for use in your environment. The content of this chapter gives you a high-level understanding of the architecture of the Tuscany runtime but with sufficient enough detail so that in the next chapter we can explain how to build Tuscany extensions.

To begin, we’ll talk about the architectural building blocks that make up Tuscany and that allow you to tailor the lightweight runtime to meet your application-processing requirements. We’ll then use the TuscanySCATours Payment composite from the payment-java contribution to demonstrate the various phases of application processing. This includes the initialization of the Tuscany runtime, followed by starting and managing the Payment application, and finally the shutdown process. As we go through these various phases, we’ll follow the same pattern. We’ll first provide a high-level view of the processing steps and then delve into more details.

After reading this chapter, you’ll have a good understanding of the Tuscany architecture and will be equipped with the knowledge needed to either extend Tuscany with new functionality or to extend your own software with Tuscany’s capability to develop and manage SCA applications.

13.1. An overview of the Tuscany architecture

Tuscany provides the runtime environment for deploying, running, and managing SCA composite applications. This includes the infrastructure required for handling different flavors of communication protocols used between services in a composite application. The infrastructure knows how to navigate the application and execute the services regardless of their implementation technology. Figure 13.1 illustrates the payment part of the TuscanySCATours composite application in box A. It’s deployed, executed, and managed by the Tuscany runtime shown in box B.

Figure 13.1. The high-level architecture view showing the Tuscany runtime reading an SCA composite application

Tuscany is a container for the SCA programming model. It’s architected in a modular and extensible way. It provides core functions that handle SCA composite applications and provides a mechanism for plugging in extensions to support different integration technologies such as implementation and binding types.

The Tuscany code base consists of a set of modules that can be divided into two categories: core and extension modules. These are described as boxes 1 and 2 in figure 13.1.

Think of the core modules as the foundation on which all extensions are based. The extensions are added to the core foundation to add support for various binding types, implementation types, policies, and databindings. Each extension type provides a library of choices for handling various technologies. For example, for binding types, Tuscany provides Web Services, RMI, JSON-RPC, and many other binding extensions.

Now think about the application that you’re developing. Your application may not, and in most cases won’t, need all the extensions. Therefore, to keep the footprint small, Tuscany allows you to choose only the extensions that you need for your composite application. As an example, if you have a component that’s implemented with BPEL, you’ll not need to drag in the supporting code for other implementation types. Or, if you’re using only the JMS binding, there’s no need to drag in Web 2.0—related bindings. You get the picture.

You’ll have the same flexibility in choosing what you need when you’re embedding Tuscany. You can choose to use all of the Tuscany modules or choose only the ones you need. For example, you may want to use the Tuscany modules that handle composite application assembly, but you’ll need to use your own extension to handle deployment because your hosting environment uses a specific deployment model. No problem—Tuscany is architected in such a way that enables you to do this. Out of the box Tuscany already supports many different platforms, such as Apache Tomcat, Jetty, IBM WebSphere, JBoss, and Geronimo, but you can also extend Tuscany to add support for new platforms.

You now have a fairly good picture of the kind of flexibility offered by the Tuscany architecture. In the following sections we’ll look at the Tuscany architecture from both structural and behavioral perspectives. The structural perspective describes the primary building blocks in the Tuscany runtime and how they’re glued together to achieve modularity, extensibility, and pluggability. It gives a static view of how Tuscany is organized and assembled. The behavioral perspective describes how an SCA composite application is processed by the Tuscany runtime. Let’s start with the structural perspective.

13.2. A structural perspective of the Tuscany architecture

The Tuscany code is organized as a set of modules. From a developer’s point of view, a module is a project in an Eclipse IDE, or an artifact in a Maven build, that’s used to produce a JAR file. It usually takes more than one of the JARs in the Tuscany distribution to support a single function in the Tuscany runtime. As an example, look at figure 13.2. The Web Services extension shown in box 2 consists of three JAR files: binding-ws, binding-ws-xml, and binding-ws-axis2. All three are needed when using binding.ws in your composite applications. The binding-ws-axis2 module can be replaced with another Web Services provider such as Apache CXF without affecting how the user exploits the SCA <binding.ws> element. We don’t have a binding-ws-cxf yet, but if you decided to write some CXF integration code, this is where you’d put it.

Figure 13.2. Plugging the Web Services binding and Axis2 extension into the Tuscany runtime extension points

We mentioned earlier that the core modules, some of which are shown in box 1, provide a framework for extending the SCA programming model and adding extensions. The framework defines a set of common interfaces and resources. For each extension, the extension modules implement these common interfaces and provide the extension-specific resources. The places in the Tuscany framework where supporting code or resources are added are called extension points. We’ll call the extension code itself plug-in code to distinguish it from our use of the word extension to refer to implementation-agnostic extension elements, such as <binding.ws>.

Let’s look at figure 13.2 again and try to identify the extension points and the plugins. In the Tuscany core, in box 1, the Tuscany runtime defines two contribution extension points where extensions provide the plug-in code that supports the loading of the extension as it appears in the composite file. In this case, two plugins are provided to support loading of <binding.ws> elements. The extension point in the core-spi module allows the runtime to manage the lifecycle of the Web Services binding.

Let’s take a closer look at what constitutes the Tuscany core and Tuscany extensions.

13.2.1. Tuscany core functions

The Tuscany core is responsible for bootstrapping the runtime and for loading, running, and managing SCA composite applications. During the bootstrap phase, the Tuscany core discovers the available extensions and uses this information to load the composite application. The Tuscany core builds an in-memory representation of the composite application based on what it reads from the composite file. As the core reads the composite file, it identifies which extensions it needs in order to run the application. It then delegates the work to the appropriate extensions when the application is run. For example, if the composite application uses a BPEL process to implement a component, the core delegates the work of loading and executing the process to the ODE BPEL engine. The building blocks that make up the Tuscany core are illustrated in figure 13.3.

Figure 13.3. Key functional blocks in the Tuscany runtime

The code in box 1 enables extension points and plugins to be declared and discovered. The content of the other boxes defines the extension points themselves and the contract between the extension points and the plugins.

Box 2 is the XML configuration model for an SCA composite application. The default configuration model is defined by the SCA Assembly Model Specification as a set of XML schemas and can be extended to add new implementation types, binding types, and so on.

Box 3 consists of a set of interfaces and default implementations for interfaces that describe the in-memory model of an SCA composite application. For example, the Java models for Composite, Component, Implementation, Service, Reference, and Binding are in the SCA assembly module under box 3. Tuscany uses these models to represent the components of a composite application.

Box 4 consists of a set of modules that handle deployment of SCA composite applications within the SCA domain. An SCA composite application is packaged as one or more archives called SCA contributions. Tuscany provides the code to scan SCA contributions and load artifacts such as composite files, Java classes, and WSDL/ XSD documents into memory using the metadata models. It resolves references in the model across artifacts and contributions following import and export directives. After the resolution, we’ll have an in-memory graph of the composite application.

Box 5 is the supporting code for the core runtime, which handles lifecycle, invocation, instance-management, and data-transformation capabilities for SCA components. It also includes some SPIs for plugging in extensions. The core runtime collaborates with extensions to start and stop SCA services, references, and implementation instances and to drive the invocations from and to SCA components.

Box 7 represents the different extensions that can be plugged into the Tuscany runtime to support different kinds of implementations, bindings, protocol, hosts, interfaces, policies, databindings, and packaging schemes.

The Tuscany runtime can be used in different hosting environments. The supporting code for this resides in box 6. Out of the box, Tuscany provides integration with various environments such as Java SE, Tomcat, Jetty, OSGi, and Java EE so that it can be either embedded within application servers or used to deploy and run SCA composite applications in a standalone mode. This can also be extended to support new platforms.

Box 8 contains the set of Tuscany APIs and tools that the end user uses to bootstrap Tuscany and launch SCA applications.

In the next section, we’ll concentrate on box 1 and explain how Tuscany supports the plugging in of extensions.

13.2.2. Tuscany runtime extension points and plugins

As you already know, the SCA programming model allows different technologies to be used in an SCA assembly through its extensible programming model. It defines extension types such as implementation, binding, interfaces, and policies. This creates an interesting challenge for Tuscany. It must take care of the anomalies of each technology while going through the various phases of invocation, execution, and management of a composite application.

Figure 13.4 shows an example of how the Tuscany runtime handles various packaging schemes for an SCA contribution, such as a filesystem directory, an OSGi bundle, a compressed directory (zip, gzip, and so on) or a JAR file (or its variants—WAR, EAR, and the like).

Figure 13.4. The contribution scanner extension point and contribution scanner extensions

Tuscany meets the extensibility challenge by defining a consistent and simple model for adding extensions through its extension point mechanism. An extension point is a bridge between the core and extensions that are not known to the core upfront. It functions as a container for a given extension type and collects all the extensions of that type that plugs into the Tuscany runtime. An extension point usually comes with a Java interface or base class to define the behavior of the extensions.

In figure 13.4 Tuscany defines a generic ContributionScanner interface that’s implemented for each packaging scheme. Each ContributionScanner scans the specific packaging format to build a list of artifacts. Each new ContributionScanner implementation is registered with the Tuscany runtime via the Contribution-ScannerExtensionPoint extension point. This is a known location where the Tuscany runtime looks for information about supported contribution packaging formats. During contribution processing, the Tuscany runtime uses the ContributionScannerExtensionPoint to find the contribution scanner that knows how to handle the required packaging format. The runtime delegates handling of the contribution to the scanner.

At the heart of the Tuscany runtime is an extension point registry that holds a list of all the extension points. This is shown on the left side of figure 13.5.

Figure 13.5. The ExtensionPointRegistry contains pointers to extension points that support the various functional requirements of the extensions.

The StAXArtifactProcessorExtensionPoint is used to register the XML and Java configuration model for each extension. As you can see in figure 13.5, the Java implementation extension and Web Services binding extension both register plug-in code with StaxArtifactProcesssorExtensionPoint.

The extension point registry will be used to find information about extension points and find extension plug-in code. The following piece of code shows how the registry lookup works.

ModelFactoryExtensionPoint factories =
registry.getExtensionPoint(ModelFactoryExtensionPoint.class);

assemblyFactory = new RuntimeAssemblyFactory();
factories.addFactory(assemblyFactory);

MessageFactory messageFactory =
factories.getFactory(MessageFactory.class);

First, the extension point is looked up in the registry using the extension point’s interface, in this case ModelFactoryExtensionPoint. Then a new plug-in is added into this factory extension point. The plug-in here is RuntimeAssemblyFactory. Finally the extension point is used to look up a factory plug-in based on the type of thing that the factory can create, in this case MessageFactory-based classes.

Now that you understand the idea of extension points and plug-in code, we’ll look at how the Tuscany runtime loads the extension points and the plug-in code associated with them and utilizes the information to process composite applications.

13.2.3. Defining extension points and plugins

In Tuscany, extension points and plug-in code are modeled using Java interfaces or abstract classes. To allow extension points and plug-in code to be declarative and pluggable, Tuscany adopts the service provider pattern defined by the JAR file specification.

 

JAR file service provider

The JAR file specification defines a mechanism to declare service providers using configuration files under the META-INF/services directory. For more details, please see the following site:

http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html#Service Provider

 

In the context of this pattern, implementations of extension points and plugins are service providers. They identify themselves to the Tuscany runtime by providing a configuration file that resides in the resource directory META-INF/services. The name of the configuration file should be the name of the fully qualified Java class name representing the extension point or plug-in. The file should contain a newline-separated list of unique and concrete provider-class names and must be encoded in UTF-8. Each line declares a provider for the named extension point.

An example is shown in figure 13.6. The AssemblyFactory is the extension point responsible for creating Java models for the SCA assembly entities such as Composite or Component. AssemblyFactory is the service provider interface and Default-AssemblyFactory is the service provider that contains the concrete implementation for the AssemblyFactory interface.

Figure 13.6. The service provider interface, implementation, and declaration

The DefaultAssemblyFactory class is declared to Tuscany using a plain-text file named META-INF/services/org.apache.tuscany.sca.assembly.AssemblyFactory. The content of the file is as follows:

org.apache.tuscany.sca.assembly.DefaultAssemblyFactory

Some plugins can be declared with additional attributes so that multiple instances can be identified by the owning extension point. For example, the Composite-Processor can be registered in the StAXArtifactProcessorExtensionPoint using a services file called META-INF/services/org.apache.tuscany.sca.contribution.processor. StAXArtifactProcessor:

org.apache.tuscany.sca.assembly.xml.CompositeProcessor;qname=
http://www.osoa.org/xmlns/sca/1.0#composite,model=
org.apache.tuscany.sca.assembly.Composite

The qname attribute allows the StAXArtifactProcessor extension point to find a CompositeProcessor using the XML element name of the extension being processed.

The model attribute associates the StAX processor, CompositeProcessor in this case, with the Java model interface it can handle.

When the runtime realizes the need for a given extension, it looks it up by using the Tuscany ServiceDiscovery SPI. The corresponding code is shown in the next snippet:

ServiceDeclaration factoryDeclaration =
ServiceDiscovery.getInstance().
getFirstServiceDeclaration(factoryInterface.getName());
if (factoryDeclaration != null) {
Class<?> factoryClass = factoryDeclaration.loadClass();
...
}

Figure 13.7 shows how the Tuscany runtime discovers plugins. Tuscany calls the Tuscany ServiceDiscovery SPI to discover, load, and instantiate the implementation class for a given plug-in interface.

Figure 13.7. The Tuscany runtime’s service discovery and plug-in instantiation flow. In this flow the service provider refers to the plug-in code providing a service in the context of the JAR file service provider pattern.

You now understand how the Tuscany runtime provides a modular, extensible, and pluggable infrastructure. Next, let’s focus on how the various functional pieces collaborate with one another to process an SCA composite application. We’ll walk you through the steps that the Tuscany runtime takes to process an SCA composite application. You’ll learn how the building blocks we’ve discussed in this subsection work with each other.

13.3. A behavioral perspective of the Tuscany architecture

As a user of the Tuscany runtime, you’ll configure an execution node with a list of contributions, either on the command line or via the domain manager. Once the node is started, all you’ll see is that component services are made available. Under the covers, a sequence of steps is performed in order to load the provided contributions, read any composites that they contain, and process the components of the composites in order to create and expose the component services.

The steps are shown in figure 13.8. The object of these steps is to create an in-memory model of the SCA components described in the contribution’s deployable composite files. The components in this model communicate with one another and collaborate to perform the task that the composite application is designated to handle.

Figure 13.8. The Tuscany runtime steps for starting and stopping an SCA composite application

As shown in figure 13.8, the first step of processing bootstraps the Tuscany kernel in the host environment. In this step, Tuscany creates an extension point registry, which will be used to discover and load extension points and plugins on demand. At this point, the Tuscany runtime is ready to process composite applications.

In the second step, Tuscany loads the composite application using a list of SCA contributions. The Tuscany runtime uses ContributionScanner plugins to build a list of artifacts from the contributions. Examples of artifacts include SCA composite files, WSDL/XSD files, Java classes, and BPEL processes. These artifacts are represented in memory as a series of Java models. The model of the composite files and the components they contain is referred to as the assembly model because it models the SCA assembly.

Tuscany has now populated the artifact models. A model, such as an SCA composite file, can reference other models by name or location. In the third step, the Tuscany runtime finds such by-name references and replaces them with references to concrete model objects. This step is called resolution. For example, a Java class name gets linked to its implementation class, or the XML-qualified name of an included composite is replaced with the real composite model. All references need to be mapped to real objects before the Tuscany runtime proceeds to the next step.

The SCA programming model is quite flexible, and it enables components to inherit settings or to override them. In step 4, the Tuscany runtime refines, or builds, the assembly model and applies the inheritance and scope rules. At the end of this step, you’ll have a complete view of the SCA composition, including service endpoints, references to service endpoints, service implementations, nested composite resolution, and policy settings.

In the fifth step, the Tuscany runtime activates the SCA composites by creating runtime provider artifacts using the provider plug-in code from implementation, binding, and policy extensions. Providers handle lifecycle events and add extra functions to the invocation path on behalf of the extensions. As a result of this step, the invocation path between each SCA reference and service is set up.

In the sixth step, the Tuscany runtime starts the SCA composites by delegating the start event to the provider code associated with component implementations, reference bindings, service bindings, and policies. By the end of this step, SCA components in the composite are ready to accept incoming requests, call the implementation instances, and invoke other services.

The seventh step handles the interaction between components. The inbound path starts when an SCA component is accessed from the service binding, and the outbound path starts when an SCA component invokes another service via the reference binding. In step 7, a chain of runtime message handlers is utilized to transform data, process business logic, enforce policies, and dispatch outbound calls to the protocol stack.

The stop flow shown in figure 13.8 is simpler. Tuscany first asks the providers of SCA components to stop SCA services, references, and implementations and release any resources associated with these objects. The second step disassociates the provider code from the assembly model. The third step shuts down the Tuscany kernel. All the modules are stopped. Extension points and plugins have a chance to perform cleanup here.

Now you have a high-level view of how Tuscany processes composite applications. We’ll look in more detail at the key steps in the processing.

13.3.1. Starting and stopping the Tuscany runtime

A Tuscany node is the runtime environment in which the SCA composite application is run. Before the node loads the application, it needs to create the extension point registry so that extension points and plug-in code can be discovered, loaded, and called. This is step 1, as shown in figure 13.9.

Figure 13.9. Tuscany’s bootstrap sequence

The second step discovers the list of module activators that help Tuscany to handle the lifecycle of core and extension code. Both core and extensions can have one or more module activators that are implementations of the following interface:

public interface ModuleActivator {
void start(ExtensionPointRegistry registry);
void stop(ExtensionPointRegistry registry);
}

You can register the plug-in code for an extension’s module activator by adding a line to the configuration file named META-INF/services/org.apache.tuscany.sca.core. ModuleActivator and providing the full path of the implementation class that handles activation. For example, the following line declares a module activator for implementation.java:

org.apache.tuscany.sca.implementation.java.JavaImplementationActivator

The Tuscany runtime runs the module activators in step 3. In steps 4–7, it creates utilities to load contributions, build composites, and activate and start composites. Tuscany loads the system-level SCA policy definitions in step 8. After that, Tuscany is ready to load an SCA application.

The Tuscany runtime can be shut down once the application is stopped and deactivated. This step releases resources that are allocated by the extension points and plug-in code and calls the stop() method of all the module activators.

13.3.2. Loading SCA applications

The Tuscany runtime reads contributions to form an in-memory representation of the SCA composite application. We’ll again use the payment composite application to help you understand the process. The payment application is an SCA contribution packaged as a JAR file. The contribution contains Java classes, composite files, and other artifacts such as WSDL documents. The Tuscany runtime goes about processing the application in the order discussed in the next section.

SCA Contribution Processing Overview

Figure 13.10 illustrates how the Tuscany runtime processes the payment contribution.

Figure 13.10. Tuscany’s bootstrap sequence

In step 1, the payment.jar file is scanned to build a list of artifacts, including the composite file. Step 2 finds artifacts of interest and loads them into memory as Java models. As shown in step 3, for composite files like payment.composite, the Tuscany runtime delegates the loading of the XML elements, such as <composite> or <implementation.java>, to a set of XML processors. Tuscany also introspects component implementations and interfaces to discover more metadata. After all the contribution artifacts have been read, references between them can be resolved. Let’s look more closely these steps.

Scanning an SCA Contribution

In figure 13.10, the left side shows the ContributionScanner interface. This is plug-in code that’s implemented to handle specific packaging formats, such as filesystem directory, JAR, and zip. Scanning a contribution produces a list of artifacts such as Java classes, SCA composite files, WSDLs, and XSDs. Each implementation of the ContributionScanner interface is registered with the ContributionScannerExtensionPoint. This was described in detail in section 13.2.2.

Loading Artifacts

The Tuscany runtime uses URLArtifactProcessorExtensionPoint plugins to parse the artifacts found in the contribution. As shown in figure 13.10, the Composite-DocumentProcessor is registered with the runtime and knows how to parse composite documents such as the payment.composite file.

Processing XML Artifacts Using Stax

If the artifact is an XML document, such as an SCA composite file, the URLArtifact-Processor will locate a relevant StAXArtifactProcessor using the QName of each XML element. The parsed information is used to create a Java model. In our payment example, distinct processors are used to process the <composite> and <implementation.java> elements. The interactions between various extension points and plugins are illustrated in figure 13.11.

Figure 13.11. Collaboration between extension points and plugins. The arrows with numbers show the sequence of lookups and delegations.

The <composite> and <component> elements are handled by the CompositeProcessor and AssemblyFactory, whereas the <implementation.java> element is processed by the JavaImplementationProcessor and JavaImplementationFactory.

Introspecting Java Classes and Interfaces

Java interfaces and implementation classes can contain Java annotations that relay information about the assembly of the application, such as remotability of a Java interface, intents, policy sets, properties, reference, services, and WSDL/XSD mappings. Tuscany defines an extension point that collects plugins implementing the JavaInterface-Visitor interface. For an SCA component implemented in Java, the implementation class is introspected using a set of annotation processors that implement the JavaClass-Visitor interface.

Resolving References in Java Models

The SCA assembly model is a graph of related artifacts that can reference each other by name, location, and other symbolic links. Tuscany uses the proxy pattern to seamlessly connect the model objects. Figure 13.12 shows an example where the CreditCardPayment service is configured with a WSDL interface by the QName of the WSDL portType.

Figure 13.12. Tuscany uses the ModelResolverExtensionPoint and specific ModelResolver plug-in code to connect the WSDLInterface to a concrete WSDL portType object by QName.

Let’s look at how Tuscany handles such object references. Creditcard.composite and CreditCardPayment.wsdl are two files in the contribution. In step 1 of figure 13.12, the composite file is loaded into memory, and a WSDLInterface object (A) is created to hold the QName of the WSDL portType. Please note that the portType itself isn’t set. Step 2 loads the WSDL file and creates a WSDL portType object (B), which contains the definitions. In step 2, the WSDL portType object is added to the WSDLModelResolver, which is the plug-in code that knows how to find WSDL elements by QName.

When Tuscany asks the StAX processor to resolve the CreditCardPayment component’s references to other objects, the processor looks up the WSDLModelResolver from the ModelResolverExtensionPoint. The WSDLModelResolver returns the WSDL portType, matching the QName provided, to the processor, and the processor sets it onto the port-Type field of the WSDLInterface object. Now the WSDLInterface is resolved, and it contains the complete information of the WSDL portType.

After the resolution, you’ll have a complete in-memory representation of the SCA composites and all their referenced artifacts such as Java interfaces and classes, WSDLs, XSDs, BPEL processes, binding configurations, intents, and policy sets.

13.3.3. Building SCA composites

In the hierarchy of an SCA composition, some configuration is inherited. Figure 13.13 shows two types of inheritance: structural inheritance and implementation inheritance.

Figure 13.13. Configuration inheritances following the structural and implementation hierarchies

Implementation inheritance occurs when a component receives configuration from its component type. For example, when using implementation.composite, the component type is built through the promotion of services and references from the named composite.

Structural inheritance describes the relationship between elements of the XML tree in the composite file. In the structural inheritance hierarchy, configuration at a higher-level element of the XML tree is applicable to its descendant elements. For example, a required intent at a composite element applies to the component elements and their descendants.

Tuscany uses builders to walk through the composite model and apply inheritance rules to and finalize the true shape of the composite application. This is illustrated in figure 13.14.

Figure 13.14. The composite model runs through a set of builders to reconcile the configurations.

The builders, such as CompositeIncludeBuilder and CompositeCloneBuilder, collaborate with one another to determine the final configuration of the composite application with all inheritance rules applied. The output of each builder is fed into the next one in the chain.

At the end of build phase, the final configuration is available for component implementations, service bindings, and reference bindings. Tuscany will use this information to drive component lifecycles, invocations, and policy enforcements.

13.3.4. Augmenting the composite with runtime artifacts

An SCA component’s implementation instance can invoke another service via a reference binding or respond to requests via a service binding. Such invocations can be intercepted to perform tasks such as data transformation, user authentication and authorization, or transaction demarcation. The interceptors used to perform these functions are typically contributed to the runtime as a set of providers by various extensions. The Tuscany core associates these providers with the SCA in-memory assembly model and sets up the chains of interceptors that are called during the invocation process. Figure 13.15 illustrates a chain of interceptors for the invocation process.

Figure 13.15. Runtime providers enable the Tuscany runtime to control the lifecycle of an SCA component, interact with communication protocols, invoke the business logic, transform data, and force quality of services. They connect the SCA programming model constructs to the plumbing technologies.

Various types of providers connect an SCA component to the underlying technologies on which it’s built. They do this by responding to lifecycle events and adding functions to the invocation path:

  • Implementation providers interact with the programming language or framework that’s used to implement the business logic.
  • Binding providers handle outbound communications from SCA references and inbound communications to SCA services over the binding protocols.
  • Policy providers handle the infrastructure-specific policy associated with the quality of service handling.
  • Data transformation providers handle different representations of data and transform data from one format to the other.

Let’s look at how Tuscany handles component interactions using a chain of interceptors.

Runtime Constructs for SCA Component Interactions

The Tuscany runtime adopts the chain of responsibilities pattern and introduces InvocationChain, Invoker, Interceptor, and Message concepts to allow a list of functions to be called in sequence to process a message. This is illustrated in figure 13.16.

Figure 13.16. Interceptors and Invokers are ordered by phases in the invocation chain.

Tuscany defines a Message interface to hold the data to be exchanged. A Message contains a body and a list of headers. The body is used to carry the business data, whereas headers are used to pass extra information such as security subjects and endpoint descriptions. Tuscany also defines a MessageFactory interface to create instances of Message.

An Invoker is the basic unit that can process a request message and produce a response message. The interface is shown in the following snippet:

public interface Invoker {
Message invoke(Message msg);
}

If a function needs to know the next Invoker in the pipeline, it needs to implement the Interceptor interface, which adds two methods to Invoker so that the next Invoker can be set and retrieved. The following snippet shows the Interceptor interface:

public interface Interceptor extends Invoker {
void setNext(Invoker next);
Invoker getNext();
}

The Tuscany runtime creates an InvocationChain for invoking an operation related to a reference or service binding. The InvocationChain links all the Invokers and Interceptors and exposes the head Invoker of the chain:

public interface InvocationChain {
...
void addInterceptor(Interceptor interceptor);
void addInvoker(Invoker invoker);
Invoker getHeadInvoker();
void addInterceptor(String phase, Interceptor interceptor);
}

Within the same invocation chain, some interceptors are required to be executed before or after other ones. For example, on the service-binding side, the data needs to be transformed by the DataTransformationInterceptor before control is handed to the ImplementationInvoker that interacts with an implementation instance. Tuscany uses the concept of a phase to order the invokers in the invocation chain. A phase represents a group of Invokers, and each phase has a position in the chain. Tuscany has built-in phases that can be extended through the PhaseExtensionPoint. Invokers are added by phase and are ordered by the phase. Invokers within the same phase can’t assume any order.

Building the Invocation Chains for References and Services

The Tuscany core works with extension providers to set up the invocation chain and handle the lifecycle of SCA components. The ProviderFactoryExtensionPoint is shown in figure 13.17.

Figure 13.17. Implementation, Binding, and Policy extensions contribute Invokers to the invocation chains.

The runtime utility called CompositeActivator is responsible for finding the provider factories for implementation, binding, and policy types and associating them with component implementations, references, and services. CompositeActivator calls the implementation provider for the service side to add an Invoker to dispatch invocation requests to the implementation instances. It calls the reference-binding provider to contribute an Invoker to the reference side to handle outbound invocations through the underlying protocol stack. Other interceptors can also be inserted into the invocation chain by policy providers to add logic to enforce quality of services by intercepting the invocations.

Implementation and reference-binding providers have callback methods to handle the lifecycle events when the corresponding SCA component implementation, reference binding, and service binding are started or stopped by Tuscany runtime. Let’s see what happens when an SCA component is started or stopped.

13.3.5. Starting and stopping an SCA component

An SCA component needs to be started before it can interact with other services. Starting a component will start the component reference bindings, service bindings, its implementation, and associated policies. This allows the binding, implementation, and policy providers to set up the environment to handle invocations and policies.

Starting and Stopping an SCA Component Implementation

Each implementation provider sets up the environment required for instantiating and invoking implementation instances. For example, the provider for implementation.java handles dependency injection. Figure 13.18 shows how this is done.

Figure 13.18. Dependency injection and proxy creation performed by JavaImplementationProvider for Java implementation classes

SCA Java components can use Java annotations to define references, properties, and context. When a Java component is started, the JavaImplementationProvider finds the corresponding fields, setter methods, or parameters of a constructor, creates injectors, and associates them with ObjectFactory instances that can be used to create values or proxies for the injection.

When a Java implementation instance is instantiated, the runtime creates a value for a Java property using the JavaPropertyValueObjectFactory. It creates a proxy for SCA references and callbacks using the ProxyFactoryExtensionPoint, which holds JDKProxyFactory and CglibProxyFactory instances. Such values or proxies are then injected into the component implementation instance.

Stopping a component implementation destroys all the implementation instances and related resources.

Starting and Stopping SCA Services and References

Services and references of an SCA component are started using the configured bindings. The binding provider that’s contributed by the binding extension is responsible for setting up the connections to the underlying protocol. The Tuscany core delegates the start or stop call to the binding provider. The providers typically respond to these lifecycle events by doing the following:

  • Starting the service binding— Publish the SCA service to the underlying protocol as an endpoint to listen for requests from the protocol layer. This makes the SCA service available to clients that support the binding protocol.
  • Starting the reference binding— Prepare the SCA reference to make outbound calls to its service providers via the binding protocol.
  • Stopping the service binding— Remove the corresponding endpoint from the protocol stack and release associated resources.
  • Stopping the reference binding— Release connections to the protocol stack’s associated resources.

We’ll further discuss the responsibility of binding providers and illustrate how binding providers implement the lifecycle methods in chapter 14.

13.3.6. Invoking SCA references and services

Each binding configures the access method to be used when invoking a reference or a service. For example, the Web Services binding dictates that messages will be formatted as XML-based SOAP envelopes passing over HTTP or the JMS protocol. Let’s look at how invocations are handled, starting with references.

Invoking a Service Through an SCA Reference

SCA components can invoke other services by calling the service proxy of the corresponding reference binding. The service proxy can be looked up from the context or injected by the component implementation provider. The proxy is a façade of the component reference invocation chain. This is demonstrated in figure 13.19.

Figure 13.19. The invocation chain for an SCA reference binding

When a method of the reference proxy is invoked, a Tuscany message is created to hold the input parameters. The interceptors in the invocation chain will be called one by one to process the message. At the end of the invocation chain, the binding Invoker calls the APIs from the binding protocol stack to invoke the service provider using messaging or RPC technologies.

Responding to Incoming Invocations on SCA Services

SCA and non-SCA clients can invoke an SCA service using the access method configured by the binding. The request is picked up by a listener that the binding registers with the underlying protocol stack at start time, for example, a servlet listener or JMS MessageListener. Figure 13.20 shows the invocation chains that connect the service binding to the component implementation.

Figure 13.20. The invocation chains for an SCA service binding

When the binding listener is called by the protocol stack, it creates a Tuscany Message from the protocol-specific message. The Tuscany Message is then passed to interceptors in the binding invocation chain. Among them is the wire format interceptor that’ll decode and extract the input parameters from the protocol message. The operation selector will map the request to an operation (method request) in the service interface. Interceptors are invoked one by one to perform tasks such as transforming data or enforcing policies. At the end of the invocation chain, the implementation Invoker is called to dispatch the invocation to an instance of the component implementation to handle the business logic. The Invoker from the implementation extension will decide how to locate or instantiate an implementation instance so that the business logic can be invoked.

The response from the business logic, or any exceptions, will be passed back to the interceptors and Invokers within the invocation chain. When it reaches the binding listener, the response is given to the binding protocol stack, which in turn sends it back to the client.

Component Implementation Instance Management

SCA applications can choose whether implementation instances (such as a Java object) are shared across multiple invocations in a boundary called the component scope. For example, the Java implementation type supports STATELESS, COMPOSITE, and CONVERSATION scopes. The implementation Invoker dispatches an invocation to an implementation instance within the scope. When required, an implementation provider is able to create an implementation instance for a component, appropriately initialize it before any request is accepted, and destroy it after it’s no longer used. Tuscany uses the ScopeContainer to help manage this component instance lifecycle.

When a component implementation is activated, the CompositeActivator checks to see if it needs instance management. If it does, the CompositeActivator creates a ScopeContainer for the given component. The ScopeContainer holds the implementation instances and manages their creation and destruction depending on the scope of the component in question.

In this section we’ve talked in a fair amount of detail about the sequence of steps the Tuscany runtime performs in order to process a contribution’s composite files. The loading, resolving, building, activating, and starting of a composite gives rise to an in-memory executable model of the composite’s components. Incoming messages arriving at the Tuscany runtime, or indeed passing between components within the Tuscany runtime, are transformed and directed to the correct component using this in-memory model. When it comes time to shut down the runtime, the stop-and-deactivate step reverses the process and releases all of the resources held by the runtime model.

13.4. Summary

You now have a good understanding of the Tuscany architecture and are equipped with the knowledge to either extend Tuscany with new functionality or extend your own software with Tuscany’s capability to process SCA applications. This chapter provided an in-depth view of Tuscany’s modular architecture and the characteristics that enable Tuscany to be easily extended. We also discussed how Tuscany’s lightweight runtime can be tailored to address specific application-processing requirements and therefore keep a small footprint.

A major portion of this chapter focused on demonstrating how the extensible infrastructure enables you to add support for new technologies that may not be already in Tuscany. You can leverage this knowledge now and add new implementation types or binding types in the next chapter.

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

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