Message Handler Programming Guide

This document provides information about the Akana Message Handler Framework. It describes the architecture of the framework, the framework API, and how to deploy extensions to the framework.

To effectively use this guide, you should have access to and a working knowledge of the concepts outlined in the Policy Manager product documentation.

Table of Contents

Message Handler Framework Architecture

Overview

The Akana API Platform includes an extensible Message Handler Framework for executing business logic on messages. The framework is similar in many respects to the JAX-RPC Message Handler Framework, but the Akana framework is not limited to SOAP messages only.

The Message Handler framework provides a set of interfaces that can be implemented by developers that would like to extend the base capabilities of an SOA Container. Many of the base capabilities in the container are also implemented using the same framework.

The handler framework is used to process incoming and outgoing messages of web services. The processing is typically constrained to binding specific logic, header processing, and minor transformations. It is not intended to provide orchestration, content based routing, or major transformations. Those capabilities should be pursued through the Virtual Service Orchestration Framework.

The Message Handler Framework is used by individual features such as the Network Director. The Network Director acts as a provider and consumer of services; in this scenario, the framework is used for processing both incoming and outgoing message exchanges.

Note: this document covers only Network Director use of the Message Handler Framework.

In all the features, the Message Handler Framework is made up of the same fundamental components:

Message Handlers

A MessageHandler is a Java class that is given a message from a message exchange to perform some business logic. The handler might or might not make changes to the message. For example, it might process a SOAP WS-Security header and remove it from the message, or it might store some metrics about the message without making any modifications. The MessageHandler can provide feedback that would dictate future processing of the message exchange, such as returning a fault to the consumer.

Handler Factories

A MessageHandler is constructed by a HandlerFactory. The framework presents context to the HandlerFactory, and the HandlerFactory uses this context to create the handler. As illustrated in upcoming sections, the context will be different for certain factories based on how they are deployed in the framework. Some examples of the context provided to factories are:

  • WSDL descriptions of a service (service-specific)
  • WSDL descriptions of a specific operation (operation-specific)
  • An Effective WS-Policy (policy enforcement/implementation)

Handler Chains

A HandlerChain is a list of MessageHandlers that are invoked in order, each being given the same message as context. The context supports the addition and retrieval of properties. This allows MessageHandlers to associate data with a context without changing the message that a subsequent handler in the chain can use in its business logic.

In two-way message exchanges, one HandlerChain is used to process both request (IN) and response (OUT) messages. The order of handler invocation for OUT messages is configurable. Handlers can be called:

  • In reverse order for OUT messages:

    OUT message in reverse order to IN messages

  • in the same order as was followed for IN messages:

    OUT message in the same order as IN messages

A handler can dictate the flow of the HandlerChain by reporting to the chain either of the following:

  • That a fault has been encountered.
  • That the handler believes all processing should stop and the exchange should be completed.

For example, if a handler encounters an error processing the SOAP WS-Security header of a message, it can relay the error to the HandlerChain, which will then relay the error to the framework, which will return a fault.

The Framework in the Network Director Feature

The Network Director acts as an intermediary, and therefore both a provider and consumer, so it includes several variations of binding and mediation support.

Framework in the Network Director feature

The Network Director supports any number of bindings for both incoming and outgoing message exchanges.

Based on routing dictated by the Virtual Service engine, messages received on one binding may or may not be forwarded on to the downstream service using the same type of binding.

Every binding implementation is different. Third parties and customers themselves can implement their own bindings. Bindings developed by Akana all incorporate the Message Handler Framework in a consistent fashion. The handlers created and invoked within the bindings may be different based on binding type, but the frameworks share a similar organization.

For IN bindings, each binding only deploys binding handlers that are specific to the matching type of binding in addition to all global handlers. In Network Director, the WS-Policy handlers are divided between the IN bindings and the Virtual Service Engine.

The Virtual Service Engine deploys all WS-Policy handlers for policies that are attached to the abstract WSDL components of a Service:

  • PortType
  • PortType Operation
  • Service

The IN bindings deploy the WS-Policy handlers that are specific to the concrete WSDL components of a service:

  • Binding
  • Binding Operation
  • Port

This enables virtual services to invoke other locally-deployed virtual services while still having policies enforced.

Message Handler Framework API

The core of the Message Handler Framework is composed of a small number of interfaces and classes, and includes an API for creating handlers. This section provides a brief description of these interfaces and classes. A detailed description of the API can be found with the API documentation installed with the Policy Manager product. A UML diagram of the primary interfaces and classes of the framework is illustrated below.

UML diagram of the primary interfaces and classes of the framework

MessageHandler
The MessageHandler interface is implemented to provide the business logic to perform on a message. The framework will invoke the handleMessage() method with a MessageContext object. The same method is invoked for IN, OUT, and FAULT messages. Depending on deployment, the same instance of a MessageHandler may or may not be called. The close() method is invoked by the framework after it has completed all handler invocations for a given message. This allows handlers to de-allocate resources and perform cleanup outside the processing time of the message itself.
MessageContext
The MessageContext interface gives a MessageHandler access to all the information the framework knows about the message and exchange being processed. The message itself is provided in the context as well as properties about the message that may have been created from other handlers. The exchange is also available in the context. If processing an OUT message, the IN message will be available from the exchange. Also, properties that are associated with the exchange as a whole can also be created and retrieved.
HandlerFactory
The HandlerFactory interface is implemented to create instances of a MessageHandler implementation. It is the HandlerFactory implementations that are deployed to the framework. They in turn create the MessageHandler implementations when requested based on how they are deployed. The HandlerFactory is given a HandlerContext that provides contextual information that the factory can use to configure the instance of the MessageHandler.
HandlerContext
The HandlerContext interface gives a HandlerFactory access to deployment information. For example, if the HandlerFactory is deployed in the global group, it is given very minimal information. However if the HandlerFactory is deployed in the message specific group, it is given access to the WSDL that describes the message that will need to be processed.
ChainHandler
The ChainHandler is a MessageHandler implementation that aggregates a list of MessageHandlers and invokes them in order. An author of a MessageHandler may find it useful to aggregate other MessageHandlers, in which case the ChainHandler would be ideal.
HandlerChainFactory
The HandlerChainFactory is a HandlerFactory implementation that creates a ChainHandler. It aggregates other HandlerFactory implementations that will create the MessageHandler implementations that the ChainHandler will aggregate.

Message Handler Deployment

The Network Director uses the OSGi (Open Services Gateway initiative) framework for deploying features and extensions.

The Message Handler Framework dynamically constructs the chain of handlers by discovering message handler factories published as OSGi services by OSGi bundles.

The Message Handler Framework registers with the OSGi framework for services that implement the HandlerFactory interface. It organizes the HandlerFactory services into groups, as described earlier, through the use of attributes that the HandlerFactory services can use to describe themselves. The following are the attributes the Message Handler Framework will use to group services.

name
Names the handler. Can be used by another handler if it needs to state a direct dependency on this handler (see before and after attributes).
scope
Note: The scope attribute will have different values for WS-Policy Handlers which are not detailed in this document. See Policy Handler Deployment: scope attribute.
Indicates which organizational group the handlers from the factory should be placed in. The values are:
  • all—Deploy a MessageHandler instance for all messages.

    This is a global handler.
  • binding—Deploy a MessageHandler instance for a specific type of binding (see binding attribute for which type).
  • binding.operation—Deploy a MessageHandler instance for each operation message. This is a message handler.
binding
Indicates which binding the handlers from the factory should be deployed for (if the scope attribute value is binding).
role
Indicates whether the handlers from the factory should be used for IN messages (IN bindings) or OUT messages (OUT bindings). The values are:
  • consumer—Used for OUT messages
  • provider—Used for IN messages
before
Specifies an ordering requirement or dependency within the group of handlers it is deployed to. The value is either the name of another handler or the wildcard (*). If the wildcard (*) is specified, the handler must be placed before all other handlers in the group.
If multiple handlers have the same value, the framework orders them in the order the OSGi framework discovers them.
after
Specifies an ordering requirement or dependency within the group of handlers it is deployed to. The value is either the name of another handler or the wildcard (*). If the wildcard (*) is specified, the handler must be placed before all other handlers in the group.
If multiple handlers have the same value, the framework orders them in the order the OSGi framework discovers them. WS-Policy handlers are not included as they are described in a separate technical note(see Policy Handler Programming Guide: Policy Handler Deployment).

The following is an example of how handlers can be defined as OSGi services, and the resulting invocation order:

Definition of services:

Handler1

  • Name: Handler1
  • Scope: all
  • Role: provider

Handler2

  • Name: Handler2
  • Scope: all
  • Role: provider
  • Before: *

Handler3

  • Name: Handler3
  • Scope: binding
  • Binding: soap
  • Role: provider

Handler4

  • Name: Handler4
  • Scope: binding.operation
  • Binding: soap
  • Role: provider
  • After: Handler5

Handler5

  • Name: Handler5
  • Scope: binding.operation
  • Binding: soap
  • Role: provider

Resulting deployment:

Resulting deployment

In this example:

  • Handler1, Handler2, and Handler3 are in the same global/binding group and are deployed first.
  • Handler2 is given the first position in the invocation order because it specified a before attribute of *.
  • Handler1 or Handler3 could have been second since there were no ordering constraints on either one, but in this example Handler1 will be second. Handler4 and Handler5 are in the second message-specific group.
  • Handler5 will be deployed before Handler4 though because of Handler4's after attribute which referred directly to Handler5.

For OUT bindings, these same example services would be defined with the same attributes except that the role attribute would have the value of consumer. The deployment order would be the same.

Developing a Message Handler

This section describes the steps necessary to develop and deploy a Message Handler. The sample artifacts described are available in the /samples directory installed with the product.

In the example, a MessageHandler will be developed that will simply log the contents of the IN and OUT messages of a message exchange. The MessageHandler will be a global handler and can therefore be deployed to any binding.

This section includes:

Policy Assertion Schema

The assertion used in the example is defined by the XML schema shown below.

01)   <?xml version="1.0" encoding="UTF-8"?>
02)   <xs:schema targetNamespace="http://soa.com/products/policymanager/examples/policy/complex" 
elementFormDefault="qualified" attributeFormDefault="unqualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
03)    <xs:element name="Complex">
04)      <xs:complexType>
05)        <xs:sequence>
06)          <xs:element name="HeaderName" type="xs:string"></xs:element>
07)          <xs:element name="Optional" type="xs:boolean"></xs:element>
08)        </xs:sequence>
09)      </xs:complexType>
10)    </xs:element>
11)   </xs:schema>

The assertion name is Complex as defined on line 03. The assertion has two elements:

  • HeaderName on line 06 identifies the name of the header that the operation name must be in.
  • Optional on line 07 indicates whether the presence of the header is optional or required.

Source Code

The source code of the LoggingHandler (a MessageHandler implementation) is as follows:

01) package com.soa.examples.handler.logging;
02)
03) import java.io.StringWriter;
04)
05) import javax.xml.transform.Source;
06) import javax.xml.transform.Transformer;
07) import javax.xml.transform.TransformerFactory;
08) import javax.xml.transform.stream.StreamResult;
09)
10) import com.digev.fw.log.Log;
11) import com.digev.fw.log.LogLevel;
12) import com.soa.message.handler.MessageContext;
13) import com.soa.message.handler.MessageFaultException;
14) import com.soa.message.handler.MessageHandler;
15)
16) /**
17) * MessageHandler implementation that logs the content of a message.
18) */
19) public class LoggingHandler implements MessageHandler {
20)
21)    private static final Log log = Log.getLog(LoggingHandler.class);
22)
23)    public void close(MessageContext context) {
24)    // no cleanup necessary
25)    }
26)
27)    /* Logs the content of the message */
28)    public boolean handleMessage(MessageContext context)
29)    throws MessageFaultException {
30)    try {
31)    // get the message from the context
32)    Source msgContent = context.getMessage().getContent();
33)    // log the message content as an informative message
34)    log.logText(msgToString(msgContent), LogLevel.INFO);
35)    return true; // continue handler processing
36)    } catch (Exception e) {
37)    throw new MessageFaultException(e);
38)    }
39)    }
40)
41)    /* Transforms the Source content of a message to a String for
42)    * logging.
43)    */
44)    private String msgToString(Source msg) throws Exception {
45)    Transformer xformer =
46)    TransformerFactory.newInstance().newTransformer();
47)    StringWriter writer = new StringWriter();
48)    StreamResult result = new StreamResult(writer);
49)    xformer.transform(msg, result);
50)    return writer.toString();
51)    }
52)  }

In this example:

  • On line 32, the message content is retrieved from the message context as a java.xml.transform.Source object.
  • That content is converted to a String for logging using the msgToString() method on lines 44–51.
  • The Akana Logging Framework is used to log the content to the SOA Container’s log file on line 34.
  • If any exception is thrown during the processing a fault will be returned to the consumer and all remaining handlers in the chain will not be invoked. This is done by creating a MessageFaultException in the catch block on lines 36–38. The MessageFaultException supports defining fault context information such as a code and description. In this example, nothing is provided except the causing exception which will result in a default fault code being used.
  • If the logic completes without exception the handler returns true on line 35. This indicates to the framework that it should continue invoking handlers in the handler chain.
  • The close() method on lines 23–25 perform no function in this example. If the handler were to have allocated resources that should be cleaned up only after the entire handler chain had finished it’s processing, it would have been done here.

The source code of the LoggingHandlerFactory (a HandlerFactory implementation) is as follows:

01) package com.soa.examples.handler.logging;
02)
03) import com.digev.fw.exception.GException;
04) import com.soa.message.handler.HandlerContext;
05) import com.soa.message.handler.HandlerFactory;
06) import com.soa.message.handler.HandlerRole;
07) import com.soa.message.handler.MessageHandler;
08)
09) /**
10) * Creates a LoggingHandler.
11) */
12) public class LoggingHandlerFactory implements HandlerFactory {
13)
14)    public MessageHandler create(HandlerContext context,
15)    HandlerRole    role)
16)    throws GException {
17)    return new LoggingHandler();
18)    }
19) }

Since there is no configuration needed, the only step for the factory is to construct a new LoggingHandler with the default constructor.

Bundle

The LoggingHandler and LoggingHandlerFactory classes need to be packaged in an OSGi Bundle so that they can be deployed. The LoggingHandlerFactory needs to be published as an OSGi service so that the Message Handler Framework can load it. In this example, Spring DM (Distributed Modules for OSGi) is used to construct and publish the LoggingHandlerFactory OSGi service. Spring DM is not a requirement but is used here for simplicity.

01) <?xml version="1.0" encoding="UTF-8"?>
02)
03) <!--
04)	Spring definition for the logging handler OSGi integration.
05) -->
06) <beans xmlns="http://www.springframework.org/schema/beans"
07)	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
08)	xmlns:osgi="http://www.springframework.org/schema/osgi"
09)	xsi:schemaLocation="http://www.springframework.org/schema/beans
10) http://www.springframework.org/schema/beans/spring-beans.xsd
11) http://www.springframework.org/schema/osgi
12) http://www.springframework.org/schema/osgi/spring-osgi.xsd">
13)
14)	<osgi:service interface="com.soa.message.handler.HandlerFactory">
15)	<osgi:service-properties>
16)	<entry key="name" value="examples.logging.factory"/>
17)	<entry key="scope" value="all"/>
18)	<entry key="role" value="provider"/>
19)	</osgi:service-properties>
20)	<bean class="com.soa.examples.handler.logging.LoggingHandlerFactory"/>
21)	</osgi:service>
22)
23) </beans>

In this example:

  • The OSGi service is defined on lines 14–21. The service is published as an implementation of the HandlerFactory interface using the interface attribute.
  • The service has three attributes defined on lines 15–19. The scope is all which indicates to the framework to deploy this handler as a global handler to all bindings of the container. The role is provider which indicates to the framework that the handler will process incoming messages to the container.
  • The construction of the LoggingHandlerFactory is specified on line 20. Since only the default constructor is needed to create the factory, there is nothing more than the identification of the class to instantiate required here.

An OSGi Bundle must have a Manifest to define its dependencies. The following is the Manifest for this example.

01) Manifest-Version: 1.0
02) Bundle-ManifestVersion: 2
03) Bundle-Name: Logging MessageHandler Example
04) Bundle-SymbolicName: com.soa.examples.handler.logging
05) Bundle-Version: 1.0.0
06) Bundle-Vendor: Akana
07) Import-Package: com.digev.fw.exception;version="6.0.0",
08) com.digev.fw.log;version="6.0.0",
09) com.soa.message;version="6.0.0",
10) com.soa.message.handler;version="6.0.0",
11) javax.xml.transform,
12) javax.xml.transform.stream

In this example:

  • Lines 01–06 hold general information about the Bundle.
  • Lines 07–12 hold the package dependencies for the Bundle. All packages not defined within the bundle that are imported by code in the Bundle must be listed here. The only exceptions to this are packages that are in the global classpath of the SOA Container such as the Java JRE and Spring packages.

Deployment

An Akana container will have a folder on the file system with a name that matches the key of the container seen in the Policy Manager Management Console. Under that folder is a sub-folder named deploy.

Bundles that provide extensions to the container, such as additional message handlers, are placed in the deploy folder.

When the container is restarted, the services published within any Bundles in the deploy folder are imported into the container, and all published handler factories are deployed by the Message Handler Framework.