Enterprise Integration Patterns
Messaging Patterns
HOME PATTERNS RAMBLINGS ARTICLES TALKS DOWNLOAD BOOKS CONTACT
Asynchronous Implementation with TIBCO ActiveEnterpriseAsynchronous Implementation with TIBCO ActiveEnterpriseMessaging Patterns » Interlude: Composed Messaging

(By Michael J. Rettig)

The previous two implementations of the Loan Broker used integration frameworks that provided basic Message Channel functionality. For example, both Axis and MSMQ provided APIs to send messages to or receive messages from a Message Channel while the application had to take care of pretty much everything else. We chose this type of implementation intentionally to demonstrate how an integration solution can be built from the ground up, using commonly available Java or C# libraries.

Many commercial EAI product suites offer substantially more functionality to streamline the development of integration solutions. These product suites typically include visual development environments that allow for "drag-drop" configuration of Message Translators and Process Managers. Many also provide sophisticated system management and metadata management functions. We chose the TIBCO ActiveEnterprise integration suite for this example implementation. As with the previous implementations, we focus primarily on design decisions and trade-offs and only introduce as much product-specific language as is necessary to understand the solution. Therefore, this section should be useful even if you have not worked with TIBCO ActiveEnterprise before. If you are interested in detailed product or vendor information please visit www.tibco.com.

This example implementation also differs in the solution design by using an Auction-style Scatter-Gather approach. This approach uses a Publish-Subscribe Channel instead of a Recipient List so that the Loan Broker can send the quote request to any number of banks. This type of Scatter-Gather pattern performs a dynamic Request/Reply with an unknown number of listeners. Additionally, the implementation of the Loan Broker component uses the business process management functionality provided by TIBCO’s Process Manager tool.

Solution Architecture

Our application is a simple bank quote request system. Customers submit quote requests to a loan broker interface. The loan broker fulfills the request by first obtaining a credit score, then requesting quotes from a number of banks. Once the loan broker obtains quotes from the banks it selects the best quote and returns it to the customer (see picture).

Clients of the system expect a synchronous Request-Reply interface to the loan broker – the client sends a quote request and waits for a reply message from the loan broker. The loan broker in turn uses a Request-Reply interface to communicate with the credit bureau to obtain credit scores. Once the initial client request is received, the loan broker has the option to perform asynchronous operations behind the distributed, synchronous facade. This allows the loan broker to utilize an auction-style Scatter-Gather with a Publish-Subscribe Channel to acquire the bank quotes from multiple banks.


TIBCO Loan Broker Solution Architecture

The auction sequence begins by publishing the request for quotes message to the bank.loan.request Publish-Subscribe Channelso that any interested party can listen for the message and provide their own rates. The number of banks submitting replies is unknown and can vary for each quote request. The auction works by opening the auction to the banks, then waiting a predefined amount of time for response messages on the channel bank.loan.reply. Each time a bid is received the timeout is reset, giving other banks time to submit another bid if possible. In this case, the bids of other banks are public, so another bank could actually listen for other bids and place a counter bid if desired.

The loan broker broadcasts the request for a quote to an unknown number of recipients. This is quite different from a Recipient List which involves sending the request to a predefined list of banks. This is reflected in the Aggregator’s Completeness Condition. Instead of waiting for a response from each bank like the previous implementations, the Aggregator relies solely on a time-out condition to terminate the auction. The Aggregator simply ignores any responses received after the auction times out. If no bank replies within the specified interval, the Aggregator will send response message to the client indicating that no quotes were obtained.


Activity Diagram Describing the Process Manager Behavior

In this implementation, the Content Enricher and Aggregator functions are implemented within the Process Manager component. As a result, the solution architecture diagram does not describe the details of the interaction between the components because they are embedded in a single component. Instead, we need to have a look at the activity diagram that represents the Process Template definition used by the Process Manager. An initial activity diagram cleanly defines the role of the loan broker as well as providing the basis for the Process Template. From the diagram, we can see that the loan broker has several responsibilities. This translates well into a process diagram which shows graphically, the exact order of events, and decision paths.

The Implementation Toolset

In order to explain the design of our solution, we need to introduce some basic concepts about the TIBCO product suite. Implementing TIBCO solutions often requires evaluating different tools for a particular problem. At times, a problem can be solved with several different tools. The trick is picking the best one. We limit the discussion to only those TIBCO features that are required to construct the example implementation:

TIB/RendezVous Transport

At the heart of TIBCO's messaging suite is the TIB/Rendezvous transport layer. Rendezvous provides the messaging mechanism for sending and receiving TIBCO messages on the "information bus". TIBCO supports a wide range of transports including (but not limited to) JMS, HTTP, FTP, and email. Rendezvous will provide the underlying transport for the messages in our example. The transport supports both synchronous and asynchronous messages, as well as Point-to-Point Channels and Publish-Subscribe Channels. Each channel can be configured for different service levels:

Similar to the MSMQ examples in this book, TIBCO provides an open API for creating messaging solutions using Java or C++, and includes a range of tools for simplifying the development process.

TIB/IntegrationManager Process Manager tool

IntegrationManager is a TIBCO development tool consisting of a rich user interface for designing workflow solutions, and a Process Manager engine for executing them. The GUI provides an extensive array of configuration, workflow, and implementation options that are stored in the TIBCO Repository. TIBCO uses the repository as the central configuration artifact for the system. It holds all metadata, workflow and custom code.

The workflow component of the solution is an aspect that sets it apart from code level solution. For the loan broker example, IntegrationManager provides asynchronous and synchronous messaging, as well as basic workflow including ServerSessionState [EAA] For our purposes, we can break IntegrationManager into 3 parts: the channel, job creator, and the process diagram.


TIB/IntegrationManager Components

Every process in Integration Manager creates a "job" (or Process Instance) which provides a central session object for maintaining state. This object includes a slotted environment with get and put operations for storing objects, as well as utility methods for interacting with the session. A simple GUI allows TIBCO developers to create Process Definitions (called Process Diagrams in TIBCO) that specify the sequence of tasks the job executes. Process Definitions resemble UML style activity diagrams, consisting of tasks, connected by transition lines (see diagram). However, the diagrams only provide the skeleton; behind each activity (i.e., box on the diagram) is a good amount of configuration and code. For our example, we require some code for processing the loans. Integration manager uses ECMAScript (commonly referred to as "JavaScript") as the underlying scripting language.


TIB/IntegrationManager Process Diagram Example

A typical process diagram includes a series of integration tasks. These include control tasks, such as forks, sync bars, or decision points, signal tasks to send and receive messages, and execution tasks such as data translation, routing, and system integration. The basic diagram pictured here includes two tasks: an ECMAScript task that executes some custom logic, and a signal out task that publishes a message to a channel. Task transitions can contain logic for selecting a particular route based upon message contents or other criteria.

TIBCO Repository for Metadata Management

Integration and messaging nearly always requires some form of self-describing data (see Introduction to Message Transformation). TIBCO defines metadata classes as Active Enterprise (AE) objects stored in the TIBCO repository. Every AE object sent across a message channel includes a Format Indicator to indicate the class definition that this message adheres to.

Managing message metadata is an important part of the development process. TIBCO developers can define the metadata directly within the development environment, can extract it from external systems such as relational databases (using a Metadata Adapter as described in Channel Adapter), or import XML Schemas. The metadata provides an explicit contract for objects and messages in the system. Once the classes are defined in the TIBCO repository, the objects are available for instantiation and manipulation within ECMA script:

//Instantiation of a TIBCO AE class
var bank = new aeclass.BankQuoteRequest();
bank.CorrelationID = job.generateGUID();
bank.SSN = job.request.SSN;

However, remember that this is a dynamic, scripted environment with limited compile-time type checking. Changing a metadata definition can easily break another part of your system. Without proper testing and development practices, message based systems can easily become "add only" systems, meaning metadata is only added to the system, and never changed for fear of breaking something.

The Interfaces

The Solution Architecture diagram shows us that the solution requires the following services.

Loan Broker

Credit Service

Bank(s)

Each service is accessible through an external interface. For each such interface we have to make the following design decisions:

The conversation styles for the interfaces have been predetermined by the solution architecture. The loan broker and credit service will both need synchronous interfaces, while the communication with the banks will be purely asynchronous.

Service levels for messaging solutions can become quite complicated especially given failover scenarios. Fortunately, the loan example service level resolves to a simple solution. In event of failure (timeout, dropped messages, down systems), the original request can be resubmitted (the loan broker as an Idempotent Receiver). Remember that we are only obtaining a quote at this point. It is not yet a legally binding agreement. Of course, this type of assumption needs to be documented in the system and understood by all parties involved. For example, the banks need to know that a quote may be resubmitted a second time. If the banks track customer loan requests for fraud detection purposes we may have to change the solution to avoid sending duplicate requests, for example by using Guaranteed Delivery.

Implementing the Synchronous Services

The loan broker system has 2 synchronous interfaces – between the customer and the loan broker and between the loan broker and the credit bureau. TIBCO implements RPC style messaging with operations using Request-Reply and Command Message. Essentially, this is a synchronous wrapper to the underlying TIBCO messaging engine. During the invocation of TIBCO operations, you can listen to the request and reply messages passing across the bus. The request message is published on the specified channel (for example customer.loan.request). The message includes a Return Address that specifies the address of a so-called INBOX channel for the reply. TIBCO hides the asynchronous details behind an RPC-style programming model. In Integration Manager, the domain operations such as obtain credit score, etc. are defined as part of the AE classes and can be invoked from the process modeling tool.

For example, to expose the loan broker as a synchronous interface we must perform several implementation steps. The implementation of the credit bureau service follows closely to these same steps.

Define the AE Class Definitions

AE classes define the data format for messages sent across TIB/RendezVous channels. Defining a class in Integration Manager is quite different from creating a class in your favorite IDE. AE classes are defined through a series of dialog boxes (see picture) from the TIB/IntegrationManager IDE. The dialog boxes allow you to select a name for your class, and then designate fields for the class. The fields can be typed as integers, floats, doubles, or composed of other AE classes.


Defining an AE Class with Attributes and Operations

Define the AE Operation Definitions

Similar to adding interface methods, operations can be added to an AE class. The parameters and the return types can be specified in the definition. True to an interface, no implementation is specified. The implementation is bound later when we use the job creator to bind a channel to a process instance.

Create a Process Diagram

A process diagram provides the implementation for the operation. The operations implemented in this example require a return parameter. Unlike method implemented in code, a process diagram doesn't have a "return" value. Instead, we specify the slot in the job where the return value will be placed. We designate this in the job creator, and we must remember to properly assign the value to the job slot in our process diagram. The actual implementation of the process diagram will be discussed in detail in a later section.

Create a Client/Server Channel to Receive the Requests

The channel allows us to define our transport, message definition, service, and subject. For our synchronous operations we need a client/server channel. We can specify the AE classes that we created in step 1. From our initial interface definitions, we chose Rendezvous with reliable messaging. Configuring these options is just a matter of clicking and selecting the proper options.


Defining Channel Properties

Configure a Job Creator to Instantiate the Process Diagram

The job creator retrieves values from the channel and passes them to the process diagram by creating a job and initializing its environment slots. Once the job is created, the process diagram is instantiated. Execution will follow the path defined by the activity diagram. Once the process finishes, the job creator returns the reply back to the channel. We can see in the job creator dialog the name of our operation.


Configuring the Job Creator

The Loan Broker Process

With our synchronous services in place, we can implement the loan broker to tie everything together. The included diagram represents the loan broker process. This process defines the behavior of the Loan Broker component. In our prior steps, we defined the AE operation, channel, and job creator to instantiate the process diagram when a client submits a message to the CreditRequest channel.

Design Considerations

From a design perspective, we need to be very careful as to what is included in the process diagram. Large, complex diagram quickly become unmanageable. It is critical to create an effective separation of concerns within the diagrams, avoiding an ugly mixture of business logic and process logic inside a single process definition. Defining what is process logic and what is business logic is hard. A good rule of thumb is to think of process logic as external system interaction. What system do I connect to next? What do I do if the system is not available? Business logic will typically involve more domain specific language. How do I activate an order? How do I calculate a credit score? How do I create a quote for a bank loan? Given the complex nature of business code, a full featured development language is often the best choice for implementation. Most process management tools, including Integration Manager, will allow you to integrate directly with Java or another language.

For those familiar with the MVC (Model, View, Controller) concept (see, for example, [POSA], the implementation can be viewed in a similar context. By simply renaming view to workflow, our implementation falls into a concise definition.

  1. Workflow - A visualized model of the workflow
  2. Controller - The process engine that receives events from the message bus, executing the proper component of the process workflow.
  3. Model - The Underlying business code (ECMAScript, JavaScript, Java, J2EE, etc)

The Process Model Implementation


The Loan Broker Process Definition

Our diagram contains a mix of script execution boxes and custom tasks that perform integration actions. One of the big advantages of modeling processes using a visual process modeling tool is the fact that the running "code" looks very similar to the UML activity diagram that we used to design the solution.

Since each task icon can contain actual code or important configuration parameters, we'll walk through each task and describe it in more detail.

The first box represents an ECMAScript to instantiate an AE object and assign values to the fields.

var credit = new aeclass.CreditBureauRequest();
credit.SSN = job.request.SSN;
job.creditRequest = credit;

The credit request created is needed as a parameter in the next activity which invokes the synchronous operations call to the credit bureau. The credit bureau is implemented as a separate process diagram consisting of a single ECMA task that receives the message and returns a credit score. The synchronous operation implies that the Loan Broker process will wait until a reply message from the credit bureau arrives.

Once the loan broker receives a reply from the credit bureau, we can see in the diagram that another AE object needs to be created to publish a quote request to any participating banks. To implement this function, we'll use the Mapper task that allows us to graphically map data items from multiple sources. The Mapper is a visual implementation of the Message Translator pattern. The Mapper task demonstrates one of the advantages of managing metadata. Because IntegrationManager has access to the metadata defining the structure of each object, the mapper displays the structure of the source and target objects and allows us to visually map the fields with a few mouse clicks. A new object is instantiated with the values from the input object. We use the job object’s generateGUID method to generate a unique Correlation Identifier and assign it to the Correlation ID field of the bankRequest object. As we will see later, the Correlation ID field is important to allow us to process more than one loan request at the same time.


The Visual Mapper Task Creates the Bank Request Message

Instead of using the visual Mapper we could have used an ECMAScript task to accomplish the same function. The equivalent code would look like the following. You can see how we create a new object of type BankQuoteRequest and assign each field from the source objects:

var bank = new aeclass.BankQuoteRequest();
//Create ID to uniquely identify this transaction.
// We will need this later to filter replies
bank.CorrelationID = job.generateGUID();
bank.SSN = job.request.SSN;
bank.CreditScore = job.creditReply.CreditScore;
bank.HistoryLength = job.creditReply.HistoryLength;
bank.LoanAmount = job.request.LoanAmount;
bank.LoanTerm = job.request.LoanTerm;

job.bankRequest = bank;

Whether to use the visual Mapper Task or an ECMAScript Task to implement a Message Translator function depends on the type and complexity of a mapping. The Mapper Task can give us a nice visual view of the connections between source and target object, but can become hard to read when the objects have many fields. On the other hand, the Mapper includes special functions that allow us to map repeating fields (i.e. Arrays) with a single line instead of having to code a loop.

Next, a very simple ECMAScript task instantiates a bid array that will hold the incoming bank responses. This script contains only a single line:

job.bids = new Array();

The next task is a Signal Out task that publishes the bankRequest object to the bank.loan.request channel. This is an asynchronous action, so the process immediately transitions to the next step without waiting for a reply.

The following tasks waits for incoming quote reply messages on the bank.loan.reply channel. If a message is received within the specified timeout interval, the script task adds the received message to the bids array:

job.bids[job.bids.length] = job.loanReply;

Once the auction period ends, the Signal In task times out and transitions the process to the final task. This ECMAScript task implements the Aggregation Algorithm of the Aggregator, selecting the best quote from all the bids. The code creates a new instance of the LoanQuoteReply, transfers the SSN and LoanAmount fields from the request object to this reply object, and loops through the bids array to find the best quote.

var loanReply = new aeclass.LoanQuoteReply();
loanReply.SSN = job.request.SSN;
loanReply.LoanAmount = job.request.LoanAmount;

var bids = job.bids;
for(var i = 0; i < bids.length; i++){
    var item = bids[i];
    if(i == 0 || (item.InterestRate < loanReply.InterestRate)){
        loanReply.InterestRate = item.InterestRate;
        loanReply.QuoteID = item.QuoteID;
    }
}

job.loanReply = loanReply;

The last line of the ECMA script places the loan reply object to be returned to customer into a job slot . We configured the loan broker job creator to return the loanReply attribute of the job to the customer as the reply message (see picture). When the process finishes, the job creator pulls the message found in the loanReply attribute of the job, and returns the value to the client.


Configuring the Job Creator’s Rule Set to Return the Best Quote

Managing Concurrent Auctions

The implementation of the auction provided a few development hurdles. Concurrency adds a degree of complexity to the problem. For the auction to work, we need to publish an asynchronous message then wait a specified amount of time for replies. However, if multiple auctions occur at once, we must ensure that each loan broker only receives the replies they are concerned about, and not all the replies.

This functionality was implemented using a Correlation Identifier and a Selective Consumer to discard unrelated messages. Ideally, we would want this filtering to occur at the channel level and not at the process instance. However, that would require the dynamic creation of channel subjects at runtime, one for each pending auction. Presently, there are implementation constraints within Integration Manager that prevent us from using this approach. When a process diagram is instantiated at runtime, it needs to register all subjects it is listening to. This allows the process engine to listen for any messages on those subjects and queue them as needed. Queuing prevents timing bugs due to process diagrams that publish on one subject then transition to another state to listen for a reply. In an asynchronous world, if the transition takes too long, the reply could be received before the process diagram gets a chance to subscribe. Therefore, the process diagram conveniently queues inbound messages, but at the cost of disallowing dynamic subjects. Given the requirements of the present example, filtering at the process level works perfectly well. The Correlation Identifier is a unique identifier assigned to each process that is then passed to each bank process and is included in each reply. A single line of ECMAScript provides the filtering in the SignalIn task:

(event.msg.CorrelationID == job.bankRequest.CorrelationID)

Execution

With the example in place, we can now run the solution. Running the solution involves starting the Integration Manager engine. To test the solution, a simple test client is included in the repository that submits loan requests every 5 seconds. Simple stubs are used for the implementations of the credit service and the banks. One of the advantages of Publish-Subscribe Channels is that we can easily listen to all messages on the message bus to inspect the message flow. We used a simple tool to log all messages to the console. For clarity, we truncated the message content to show only relevant fields and eliminated the format identification and tracking information included in each message. We can see from the logs, the times, subjects, and inboxes of messages. The inboxes are the return addresses for the synchronous services.

tibrvlisten: Listening to subject >

2003-07-12 16:42:30 (2003-07-12 21:42:30.032000000Z):
subject=customer.loan.request,
reply=_INBOX.C0A80164.1743F10809898B4B60.3,
SSN=1234567890 LoanAmount=100000.000000 LoanTerm=360

2003-07-12 16:42:30 (2003-07-12 21:42:30.052000000Z):
subject=credit.loan.request,
reply=_INBOX.C0A80164.1743F10809898B4B60.4,
SSN=1234567890

2003-07-12 16:42:30 (2003-07-12 21:42:30.092000000Z):
subject=bank.loan.request,
SSN=1234567890 CreditScore=345 HistoryLength=456 LoanAmount=100000.000000
CorrelationID="pUQI3GEWK5Q3d-QiuLzzwGM-zzw" LoanTerm=360

2003-07-12 16:42:30 (2003-07-12 21:42:30.112000000Z):
subject=bank.loan.reply,
InterestRate=5.017751 QuoteID="5E0x1K_dK5Q3i-QiuMzzwGM-zzw" ErrorCode=0 CorrelationID="pUQI3GEWK5Q3d-QiuLzzwGM-zzw"

2003-07-12 16:42:30 (2003-07-12 21:42:30.112000000Z):
subject=bank.loan.reply,
InterestRate=5.897514 QuoteID="S9iIAXqgK5Q3n-QiuNzzwGM-zzw" ErrorCode=0 CorrelationID="pUQI3GEWK5Q3d-QiuLzzwGM-zzw"

We see the request message from the test client on the channel customer.loan.request. The message includes the social security number of the customer as well as the desired loan amount and term (100,000 Dollars for 360 months). The message specifies the test client's private reply channel _INBOX.C0A80164.8CC3F0E4FAA97E1EA8.123 as the Return Address so that the loan broker knows where to send the reply message.

The next message represents the request from the loan broker to the credit service, carrying the private inbox of the loan broker as the Return Address. The credit service only requires the social security number. The reply message is not captured by our logging tool because _INBOX channels are private channels.

After the loan broker receives the credit score, it publishes a message to the bank.loan.request channel. The two bank stubs in our example immediately reply with one interest rate each. Each bank also assigns a unique QuoteID to the reply so that the customer can refer back to it later. The auction period times out in a few seconds. Because we cannot see the reply message from the loan broker process to the test client we can look in the debug log for the process manager engine to verify that our test client received the lower interest rate. Within this message we can see the CorrelationID and the Format Indicator for the class.

reply= class/LoanQuoteReply {
    SSN=1234567890
    InterestRate=5.017751017038945
    LoanAmount=100000.0
    QuoteID=5E0x1K_dK5Q3i-QiuMzzwGM-zzw
}

Conclusion

The solution provides some interesting points for comparison. Visual Workflows can be easy to view and understand, but just as in code, this relative simplicity can turn into a maze of confusion as implementations become more complex. The dynamic nature of the Scatter-Gather allows for a clean decoupling of the pub/sub interface. The loan broker can publish bank loan requests without any knowledge of the subscribing banks. Similarly, the Aggregator for the replies doesn't need to know the number of expected replies. The potential downside of this flexibility is that it can hide errors resulting from incorrect subject naming, developer error, or dropped messages.

The visual, GUI driven implementation with TIBCO provides a corollary approach to the code level implementations. While the sheer power and flexibility of code cannot be argued against, a more graphical environment can greatly simplify very complex tasks. Configuration is a very large part of integration, and graphical environments work well for this. At times, it may seem that developers have to choose between writing code and using a vendor development tool. However, they can often coexist effectively. For instance, you could choose to use Integration Manager to model integration workflows, and use J2EE session beans to implement domain logic.

For the sake of brevity, our example was relatively simple, leading to an easy to understand scenario. Real world implementations are rarely this simple. Rapid development tools can facilitate faster initial development, but can limit your development options. Process management tools can help hide the complexities of messaging, allowing developers to focus on integration tasks without worrying about what happens behind the scenes. However, ignorance of messaging infrastructure has led to the death of more than one project.

Enterprise Integration Patterns book cover

Enterprise Integration Patterns
The de-facto language for designing asynchronous, distributed systems. Over 100,000 copies sold.

Software Architect Elevator book cover

The Software Architect Elevator
Rethink the role of architects as a connecting element across organizational layers. Acquire the technical, communication, and organizational skills to succeed in this new role.

Cloud Strategy book cover

Cloud Strategy
Make your cloud migration a success by translating high-level goals into conscious decisions with well-understood trade-offs.

Platform Strategy book cover

Platform Strategy
Platforms can boost innovation through harmonization, but they aren't easy to build. Learn from over a decade of designing and rolling out IT platforms.