Enterprise Service Bus: Theory in Practice

   

Chapter 4 discussed a generic data exchange architecture and introduced the concept of a Content-Based Routing (CBR) service. An ESB CBR service may support scripting. The "validate" step could be implemented as a CBR service, and written using some simple JavaScript. The JavaScript code snippet in Example 7-1 uses XPath expressions to validate whether an XML document is a PurchaseOrder.

Example 7-1. Validating that a document is a PurchaseOrder

// // Function returns "true" if this contains a PurchaseOrder document. // function isPO( ){ // Get the elements that would be at: /Envelope/Body/PurchaseOrder. nodeSet = getXPath("*/*/test:PurchaseOrder", "xmlns:test='http://www.sample.com/PO'"); if (nodeSet = = null || nodeSet.getCount( ) < 1){ return false; } return true; }

This snippet uses a JavaScript helper function, getXPath( ), to match a specific named element within the document that has the name PurchaseOrder, is the third level down from the root element, and uses the XML namespace identifier test='http://www.sample.com/PO'.

There are other approaches as well; for example, you could use the SAX interface, which would be more event-driven and positionally independent, or you could use an XML pull-parser. Other, more fundamental tests could have been used prior to this, such as simply validating that the document contains a SOAP envelope. XML schema could be used for validation, and other scripting languages or validating engines may also be plugged in. Different parts of an ESB can use different approaches, as appropriate.

This particular routing service relies on the invocation of a rule( ) method to perform the routing logic. The code snippet in Example 7-2 shows some simple JavaScript to implement the rule( ) method that invokes the isPO( ) method.

Example 7-2. A simple rule( ) function used by a CBR service

function rule( ) { // Test if SOAP if (!isSoap( )) { java.lang.System.out.println(" <Message NOT Soap>"); throw new java.lang.Exception("[SoapRouterService] Non-SOAP message."); } java.lang.System.out.println(" <Message is Soap>"); // Is this a SOAP Fault? if (isSoapFault( )){ java.lang.System.out.println(" <Message is Soap Fault>"); return getEndpoint("ESBFaultEndpoint"); } // Is this a PO? if (isPO( )){ java.lang.System.out.println(" <Message is a PO>"); return getService("POXformService"); } java.lang.System.out.println(" <Message is not a PO>"); // All other messages will be recorded in the audit file //(i.e. routed to the sample AuditService.) return getService("AuditService"); }

Note the implications of what happens when this function succeeds or fails. Returning a named endpoint from the service invocation causes the next destination in the itinerary to be automatically invoked by the surrounding ESB routing framework (Figure 7-4). A destination may be another service, another process itinerary, a MOM endpoint, or an external web service. The CBR rule may even return a valid endpoint destination that is outside of an itinerary, in which case the message exits the ESB process and is on its own.

Figure 7-4. Decision criteria of sample CBR service

In addition to having the scripting code explicitly throw an exception, some other processing component such as the XPath parser could throw an exception at any point during the execution of the CBR service. In this case the surrounding ESB process will be aborted, and the message will be routed to a special destination known as a Rejected Message Endpoint (RME). The RME itself can be handled by an error-handling service; it can be incorporated into an auditing and logging scheme, or have custom logic applied to the message based on more business rules.

There are many other ways a CBR service could be implemented. For example, decision criteria could be configured using a GUI interface that specifies declarative rules that act on message properties, as illustrated in Figure 7-5.

Figure 7-5. Declarative rules in content-based routing

Or you could use a GUI drag-and-drop interface, as illustrated in Figure 7-6.

Figure 7-6. Routing rules using a GUI drag-and-drop interface

An alternative to coding JavaScript is to use XML to express decision criteria, as shown in Example 7-3.

Example 7-3. XML representation of declarative routing rules

<?xml version="1.0"?> <rule:ruleSpec xmlns:rule="http://www.sonicsw.com/rule"> <rule:helperFile url="dsdata:///jsHelperFunctions.js"/> <rule:declarations>defaultCount = 100;</rule:declarations> <rule:declarations>defaultCount = 100;</rule:declarations> <rule:declarations>Description = "Custom Item";</rule:declarations> <rule:routingRule> <rule:condition>GetProperty("itemDesc") = Description</rule:condition> <rule:process endpoint_ref="CallWebServiceProcess" type="PROCESS"/> </rule:routingRule> <rule:routingRule> <rule:condition>GetProperty("orderCount") &gt;= defaultCount</rule:condition> <rule:endpoint endpoint_ref="CheckAvailabilityJCAEndpoint" type="ENDPOINT"/> </rule:routingRule> <rule:routingRule> <rule:condition>GetProperty("orderCount") &lt; defaultCount</rule:condition> <rule:process endpoint_ref="ApprovalProcess" type="PROCESS"/ </rule:routingRule> </rule:ruleSpec>

Note the use of the <rule:routingRule> element, which declares that a GetProperty( ) method be used to access a named message property at runtime. The following excerpt specifies that the CheckAvailabilityEndpoint value be used based on the test of whether the value of the orderCount property of the message is greater than or equal to the value of defaultCount:

<rule:routingRule> <rule:condition>GetProperty("orderCount") &gt;= defaultCount</rule:condition> <rule:endpoint endpoint_ref="CheckAvailabilityJCAEndpoint" type="ENDPOINT"/> </rule:routingRule>

The method of specifying the branches of the routing and the means for defining the decision rules are details that will vary between vendor implementations. The important point is that the metadata describing the possible branches and the rules for the CBR decisions are evaluated at the remote container, not by a centralized rules engine. That is one of the discriminating qualifiers for whether something can be categorized as an ESB as opposed to being a centralized hub-and-spoke EAI broker. In an ESB, there is no centralized rules engine that can become a performance bottleneck or a single point of failure. The CBR service, like any other ESB service, holds its own operating instructions in its service container, and is completely capable of executing those instructions without needing to refer to anything else externally. The implication is that any part of the ESB network may shut down and become temporarily unavailable without affecting the integration network as a whole.

The service container may obtain its configuration information from a centralized or regionalized place, such as a directory service. If the service container is capable of caching its configuration data locally, it can continue to operate should the directory service become unavailable.

Other characteristics about this CBR service that are distinguishing traits of an ESB are its selective use of only the processing technology (XML parser, JavaScript interpreter) that is needed to get its task accomplished, and the ability to support multiple processing technologies transparently.

7.4.1 Conditional Routing Using BPEL4WS

The same branching conditions that were described using JavaScript code or an XML rules file could also be described using BPEL4WS, as shown in Example 7-4.

Example 7-4. CBR rules described using BPEL4WS

... <variables> <variable name="purchaseOrder" messageType="sns:POMessage"> </variables> ... <!-- wait for and receive a PO submission use createInstance to start the process --> <receive partnerLink="customer" portType="sns:purchasingServicePT" operation="submitPO" variable="purchaseOrder" createInstance="yes"> </receive> <!-- use a 'flow' element to enable concurrent execution of each rule --> <flow> <switch> <case condition= "bpws:getVariableProperty(purchaseOrder, 'itemDesc') = 'Custom Item'"> <!-- Found a custom item, so invoke the processOrder operation of the process identified by the "process" partner link --> <invoke partnerLink="process" portType="sns:processPT" operation="processOrder" inputVariable="purchaseOrder" </invoke> </case> </switch> <switch> <case condition= "bpws:getVariableProperty(purchaseOrder, 'orderCount') > 100 "> <!-- Order count is > 100, so check the availability using the availability partner link -->n <invoke partnerLink="availability" portType="sns:availabilityServicePT" operation="checkAvailability" inputVariable="purchaseOrder" </invoke> </case> </switch> <switch> <case condition= "bpws:getVariableProperty(purchaseOrder, 'orderCount') < 100 "> <!-- order count is less than 100, so process this order --> <invoke partnerLink="approval" portType="sns:approvalServicePT" operation="processOrder" inputVariable="purchaseOrder" </invoke> </case> </switch> </flow> ...

BPEL4WS allows scripting of abstract processes that can handle more complex situations than itineraries can. Processing these complex relationships requires a specialized orchestration service, as described later in Section 7.6.

Категории

© amp.flylib.com,