Dynamic transformations using Oracle Service Bus 10gR3
By Chris Tomkins on Nov 17, 2008
One of the new features in Oracle Service Bus 10gR3, and one commonly requested by customers, is the ability to choose the transformation to apply to a message based on runtime information - also known as dynamic transformations.
In the previous version of Oracle Service Bus you could only specify the transformation to be performed on the message at design time which may have resulted in:
- Multiple proxy services performing the same mediation logic, but applying different transformations
- A proxy service message flow containing a for loop with a number of cases each performing a different transformation
These scenarios can easily be handled when the number of transformations is small and/or doesn't change regularly but both scenarios involve modifying or creating a proxy service just to add a new transformation. Dynamic transformations allow you to define a proxy service which can handle new transformations being added at a later date.
Let's look at how we might implement a typical scenario where a proxy service accepts multiple versions of a message and applies an appropriate transformation based on the version of the message (determined by the namespace of the root element) in order to convert the message into a common format.
First create a new Oracle Service Bus project called DynamicTransformation (File>New>Oracle Service Bus Project, enter a name of DynamicTransformation and click Finish)
Then create a new schema file called Customer.xsd (File>New>Other>XML Schema) and define the common format with the target namespace http://www.oracle.com/Customer and the following structure:
Now define a CustomerApplication business service which accepts messages in this format (File>New>Business Service, enter a name of CustomerApplication and click Finish)
Now in the main editor pane, select the General tab for this service and make sure it is defined as a Messaging Service.
Now move to the Messaging tab, set the Request Type to XML and click the Browse button to select the Customer element from Customer.xsd as the message format definition:
Now move to the Transport tab:
- Select the protocol as JMS
- Click Add to accept the default URL or modify it to point to a valid JMS endpoint
(NB. You will need to ensure the connection factory and queue specified in this JMS endpoint exist. If you accept the default, and are running on the default port of 7001 you should only need to create a JMS queue with a JNDI name of CustomerApplicationRequest on the WebLogic Server instance underpinning your Service Bus instance as the connection factory is created for you).
Now move to the JMS Transport tab:
- Select the Message Type as Text and leave the other values as their defaults.
Now test this business service is working as expected by right clicking on it in the Project Explorer and selecting Run As. When the Select Tasks dialog box appears, just click Finish. This should launch the Test Console.
Enter some sample data (the Test Console should have generated the structure for you) and then click Execute.
This should succeed but without a response (this is a one way service and the message has been placed on the CustomerApplicationRequest queue). Navigate to the CustomerApplicationJMS queue and check it contains your message:
Next create a new schema file called CustomerV1.xsd and define the CustomerV1 format to be in the http://www.oracle.com/CustomerV1 target namespace and have the following structure:
And one called CustomerV2.xsd defining our CustomerV2 format to be in the http://www.oracle.com/CustomerV2 target namespace and have the following structure:
Next, lets create our transformations: Version1.xq (which converts from CustomerV1 format to the common Customer format):
And Version2.xq (which converts from Customer V2 format to the common Customer format):
NB. It is important the target namespaces and transformation filenames are named as described as this is how the correct transformation is selected.
So now we have our transformations and formats defined, the final step is to create our Customer proxy service (File>New>Proxy Service, enter name Customer and click Finish) which will actually perform the dynamic transformation.
In the General tab, select the Service Type to be Messaging Service.
In the Messaging tab, select the Request Type to be XML but don't specify a format (this is because this service can accept both V1 and V2 message formats).
In the Transport tab, select the Protocol to be JMS and accept the default endpoint (the queue and connection factory pointed at by this JMS endpoint will be created for you).
In the Message Flow tab create a message flow like the following which routes to the CustomerApplication business service:
The Assign action extracts the version number (the last digit) from the namespace URI for the Customer element and stores it in the version variable:
NB. The XPath expression here gets the namespace URI of the Customer element (fn:namespace-uri), converts it into a string (fn:string) and then extracts the version number from the end of the string (fn:substring-after).
The Replace action replaces the entire contents of the body with the result of performing the XQuery transformation, the name of which is determined, in this case, by concatenating the static string DynamicTransformation/Version with the value stored in the version variable:
In order to create the Expression line you need to have clicked in the Expression field and completed the XQuery editor as follows:
The Bind Variables field defines the variable passed to the transformation. To create this simply enter the name Customer in the Add Custom Variable field and clicked Add. The XPath expression here evaluates to the Customer element within the body, irrespective of namespace.
After completing this, save everything and then launch the Test Console on the Customer proxy service (right click on it and select Run As).
Enter a CustomerV1 format message into the payload field (you can generate a sample one of these by right clicking on CustomerV1.xsd in Workshop and selecting Generate XML).
Then click Execute.
The invocation should succeed without a response - the V1 message should have been transformed to the common Customer format, the CustomerApplication business service invoked and a common Customer format message placed on the CustomerApplicationRequest JMS queue. Check the JMS queue to see this is the case.
Then try the same, but replace the payload with a CustomerV2 format message. This should also succeed without a response - the V2 message having been also transformed to the common Customer format and placed on the CustomerApplicationRequest JMS queue. Again, check the JMS queue to see that this is the case.
That's it. You've just implemented your first dynamic transformation with Oracle Service Bus - congratulations!
NB. A few words of caution when adopting an approach like the one described above. Since your proxy service now has a weakly defined service contract rather than a strongly defined one you will need to describe to your service consumers the message structure(s) your service supports (probably via documentation) and also ensure you have some error handling to deal with unsupported message formats.
Click here for the Oracle Service Bus configuration jar, which includes the Customer proxy service, the CustomerApplication business service, the Customer XML schemas, the transformations and the sample messages.