Who needs a Service Bus anyway

Who needs a Service Bus anyway

Gepubliceerd: Categorie: Oracle

Some time ago I was having a discussion about the setup of a SOA project on which a former colleague was about to work on. The purpose of this project was to implement a synchronous service API for a third party systems to integrate with. This API needed to conform to a B2B standard (written some ten years ago) and in their particular case, it would need to provide data from their legacy ERP system.

The API description consists of several different ‘business operations’ where their request messages come in over HTTP in a variety of predefined formats, e.g. as an HTTP GET operation where the parameters are encoded in the URL string or sending an HTTP POST operation where the request message is contained in the body: in both cases, the response is handed back as an XML message. In order not to exclude third party systems, both flavours of operations would need to be implemented.

One of the other requirements for the API was that it needed to implement an operation to list all operations that it currently supports, together with the endpoints on which these operations need to be invoked.

The API description consists of several different ‘business operations’ where their request messages come in over HTTP in a variety of predefined formats, e.g. as an HTTP GET operation where the parameters are encoded in the URL string or sending an HTTP POST operation where the request message is contained in the body: in both cases, the response is handed back as an XML message. In order not to exclude third party systems, both flavours of operations would need to be implemented.

One of the other requirements for the API was that it needed to implement an operation to list all operations that it currently supports, together with the endpoints on which these operations need to be invoked.

Restrictions

One of the current implementation of the third party systems they were looking to integrate with was quite quirky in that it required all operations to have the exact same endpoint. So, it was a tough choice to either exclude this system from the possible clients and create a ‘proper’ implementation where all operations have their own endpoints, or to provide an implementation where there’s only one endpoint that basically functions as a dispatcher for the different operations.

Summarizing:

  • XML POST interface, request = XML, response = XML for all operations
  • HTTP GET interface, request = None, response = XML for all operations
  • Provide a single endpoint accepting all these requests at the same address

Service Bus to the rescue

As we were discussing the project and its requirements, it turned out that their internal IT department had already started development using Oracle SOA Suite, as their skills in BPEL were ‘the hammer that made this problem look like a nail’. However, my feeling was that this project would actually be much better off by the introduction of Oracle Service Bus to transform different message formats into a generic XML representation (and depending on the complexity: implement the message flows entirely in Service Bus or offload the more complicated ones to SOA Suite).

Scenario

As I am somewhat branded by my background in chemistry, I will show my proposed implementation using some ‘chemical’ webservices from WebServiceX as an example. For the backend implementation, invariably the SOAP implementation of the service will be used. For my convenience, I am reusing the XML structures that are provided by the WebserviceX implementations, saving myself the hassle of transforming the messages structurally or with respect to their namespaces.

In the following scenario, I am exposing two operations (GetAtomicNumber and GetAtomicWeight) in two different message formats:

HTTP GET: http://server:host/HttpGetAtomicNumber?RequestName=GetAtomicNumber&elementName=boron

XML POST: http://server:host/GetAtomicNumber

Furthermore, all operations will also be available at a consolidated endpoint for both request message formats, at http://server:host/OneProxyForAll

Schematic implementation

The following diagram shows a schematic representation of the desired setup; on the left hand side, you can see the exposed proxies (HTTP GET, XML POST and Generic Gateway), connected through some Service Bus flow logic with a Service Bus Business Service, exposing the actual implementation logic:

Environment

The sample project was built using Oracle’s 12.1.3 Virtual Image, downloadable from Oracle Technology Network.

Implementation

To start off, I created a plain Service Bus Application named ChemicalWebServiceApp, containing a single project (XMLPostOperations). For the service contract of the backend service, I have downloaded the service provider’s WSDL  and stored this inside my project. To facilitate the creation of the example, I extracted the embedded schema definitions in this WSDL into Schema/MessageStructures.xsd.

Now, simply create a business service based on the WSDL inside the Service Bus project. Next, create a proxy service (and pipeline) to expose a local interface. This proxy service needs to refer to the GetAtomicWeight’s request and response elements and be created to listen at /GetAtomicWeight:

Definition of XML Post proxy for ‘GetAtomicWeight’ operation

Finally, it’s simple to hook the pipeline to the business service on the Service Bus overview:

Wiring it together

This will generate a RouteNode and a Routing Action in the pipeline, which you only need to check/modify to call the correct operation on the business service (note that the business service implements multiple operations whereas the local interface is created per operation):

Deploy and test your local service using the Service Bus console!

The proof of the pudding is in the testing…

As you can see from the above invocation, the content in the GetAtomicWeightResult element is actually no XML but simply a serialized XML string. Violating the actual contract, I decided to convert this serialized XML into actual XML because it looks nicer and all it takes is a simple replace operation on the response path:

Manipulating the response pipeline to return proper, deserialized XML
Manipulating the response pipeline to return proper, deserialized XML

Mutatis mutandis the service GetAtomicNumber is created, reusing the same business service but mapping to another operation. Perform a quick test to make sure it actually works (and now XML returns ‘all the way’).

Likewise for GetAtomicNumber
Likewise for GetAtomicNumber

For the HTTP GET operations, I have created a separate Service Bus project: it is debatable whether this is actually necessary, as these artefacts will tap into the same functionality. And since my HTTP GET artefacts are in another pipeline, this will require me to route the messages to the XML proxies (instead of being able to directly route to the XML pipelines, which are only available inside the same project). However, separating the different interfaces for demonstration purposes made me decide to create separate projects.

Creating the HTTP GET GetAtomicNumber

Inside the newly created HTTP GET operation Service Bus project, a new proxy and pipeline are created using the HTTP transport for a messaging service to return XML. The request type should be set to ‘None’, since we will not use it but instead obtain the parameter value(s) from the query string of the URL. The response type will of course be XML:

The pipeline for this service consists just of a Route Node to the corresponding XML proxy, but we need to do some work to assemble the request. The inbound request does not carry any payload, but instead its parameters are passed as part of the query string on the URL. These will need to be transformed into the required XML message. For this purpose, a replace action in the Route Node’s request pipeline is sufficient to transform the contents of the message ($body) to hold the proper contents for the XML proxy:

Transforming the HTTP GET query parameters into an XML message
Transforming the HTTP GET query parameters into an XML message

The contents of the replace expression are:

The contents of the replace expression are:

For the response, there is no need to transform: the XML proxy’s result is already in the correct format (albeit that it has already deserialized the XML to return ‘proper XML’ as explained).

This proxy service can be tested using the Service Bus testconsole, but you would have to enter the query parameters manually as well. It’s much more fun to test the actual URL directly using the curl tool from the UNIX command line (you need to escape the URL here as the request contains an ampersand character. If you fail to escape it, the UNIX shell will try to start the first part of the command as a background job and ignore the elementName altogether):

Command line testing HTTP GET using curl
Command line testing HTTP GET using curl

As you can see, performing an HTTP GET operation on the proxy service with the elementName as a parameter returns proper XML containing the expected result! The final project for this Service Bus application is to create a generic endpoint, for those (restricted) service consumers that send all their requests to a single endpoint – so we must figure out where to dispatch the requests ourselves.

As always, there are multiple ways to do this: the cleanest solution in my opinion is first to branch off, based on the HTTP operation invoked (GET for parameters passed through the URL query parameters, POST for the XML request message) and to take it from there to distinguish between all the different types of requests.

The requests are simple and I could have created XML request messages for all types straight away, removing the necessity of invoking the HTTP Get proxy services altogether. But this would require me to implement the same logic of assembling the XML request message both in the pipelines for the HTTP GET artefacts and in this generic ‘dispatcher’.

Besides, in a real life scenario there may be additional validations and functions implemented in the HTTP GET pipelines that we would miss by shortcutting. So, I will set up a routing table in my dispatcher to hook up to the four existing proxy services and set up some conditional logic to determine: to which one should I send the request?

When the HTTP GET request entered on the Generic ‘dispatcher’ proxy, the difficulty was initially in propagating the query parameters. Fortunately, Mr Service Bus (Jeff Davies) Blog helped me out:

Propagating the Query Parameters on the Generic Gateway
Propagating the Query Parameters on the Generic Gateway

Conclusion

With Service Bus you can expose different interfaces (message formats, transports) for the same business logic very easily. Any additional service interfaces can be created very quickly using Service Bus, for these kind of cases Service Bus is almost a perfect fit!

The code has been stored in Github.

The contents of the replace expression are:

Milco Numan
Over auteur Milco Numan

Technical Consultant, specialized in integration and design/development of extensions to Oracle e-Business Suite using Developer (Forms/Reports), Workflow, PL/SQL, Java (OAF) and SOA Suite 10g/11g/12c. Certified in Java (SCJP for Java 5), Oracle SOA Suite 11g, Oracle SOA Suite 12c, Oracle BPM Suite 11g, Oracle IT Architecture SOA 2013 Essentials & Oracle Enterprise Linux 6, SOA Architect & SOA Security Specialist (SOASchool.com). View my LinkedIn profile: nl.linkedin.com/in/milconuman/

Meer posts van Milco Numan
Reacties
Reactie plaatsen