Monday, November 5, 2007

Dynamic Partner Links and the UDDI BC

I recently posted a basic example of Dynamic Partner Links in OpenESB here. In this post, I'll show how to use the UDDI BC to dynamically resolve a service URL at runtime and invoke it.

Without dynamic partner links and the UDDI BC, what would you do if a service you're invoking from within your process was moved? Well, you'd have to modify, rebuild, and re-deploy your Composite Application. Using what I'm about to show you, your process can react to that change automatically.

Below is the tutorial (really just some high level steps). I've also uploaded my NetBeans modules and a copy of the tutorial here.

Prerequisites:



Tutorial:


  • Step 1: Create a New BPEL Module


  • Step 2: Create a New SOAP WSDL with one request-response operation, and a simple BPEL process for the backend . We'll invoke this service dynamically later on.




  • Step 3: Create a New UDDI WSDL with one request-resposne operation. (UDDI BC Demos can be found here)


  • Step 4: Add an External WSDL File from http://schemas.xmlsoap.org/ws/2004/08/addressing. The WS-Addressing WSDL defines the EndpointReference element that we'll need.
    • GOTCHA: NetBeans may add a character to the beginning of the wsdl when it imports. Make sure to remove this character. In some cases, this may be a hidden character that will only show up in an editor like VI.




  • Step 5: Create another SOAP WSDL with one request-response operation. This service will provide the dynamic partner link functionality.


  • Step 6: Create another BPEL Process. This process should lookup a service endpoint in UDDI and invoke the service using a dynamic partner link.




  • Step 7: Clean and Build the BPEL Module.


  • Step 8: Create a Composite Application and add your BPEL Module to it.


  • Step 9: Clean and Build the Composite Application.


  • Step 10: Correct the Composite Application's configuration using the CASA Editor.
    • Generated:





    • Corrected:





  • Step 11: Clean, Build, and Deploy the Composite Application Again


  • Step 12: Create a test case to invoke the service we've created.


  • Step 13: Create another service to invoke, just as in Step 2. Make the BPEL process do something a little different so we will be able to tell which service was invoked.
    • This WSDL must have the same target namespace and operation to invoke as the WSDL in step 2.



  • Step 14: Update the service endpoint in UDDI to point to the new service you created.


  • Step 15: Run the testcase again. The output will show the new service was invoked.




9 comments:

Anonymous said...

In this example, the web service that can be invoked are deployed within JBI. Moreover, in the CASA editor, a binding component for each echo service appears.

In the case I query a UDDI server to obtain the endpoint of a service, it is expected that this service is not part of the JBI environment, and moreover I would not like to deploy a binding component for this service (think the UDDI server may potentially return, say, 50 distinct access points).

How would you modify the example in this case?

Chad Sturtz said...

Let me start by saying that it doesn't matter that my echo services live in the JBI Container. They could just as easily be sitting on a Tomcat server somewhere else in the world.

Having that said, I want to be clear about the use case(s) that this example fulfills. This example allows your business process to automatically react to a service being moved (to a different URL). It would also fulfill the use case of having 2 different instances of the same service available at 2 different endpoints (say a primary and backup). It does not allow you to consume multiple, completely different services. In my example, even though the 2 echo services are "different", their WSDLs are basically identical so that building a partner link from either WSDL allows us to invoke either service. Think of it more like 2 instances of the same service. This allows me to simulate the echo service being moved by simply changing the URL in UDDI.

When you talk about 50 distinct endpoints, if you’re talking about 50 instances of the same service (or similar enough one WSDL can be used to invoke any of them), here’s what you’d do:
1) Remove the echo and echo2 BPEL Processes and WSDLs.
2) Create or import a WSDL that describes the service you’re invoking.
3) Modify the DPL BPEL Process (dpl.bpel) to invoke a partner link built from the WSDL you’ve imported/created (replacing the current partner link that invokes one of the echo services).

If you’re talking about 50 completely different services with different operations, you’re in for quite a bit of work. You’d have to create/import a WSDL for each service. You’d then have to create a partner link for each one inside your DPL BPEL Process (dpl.bpel). After all that, you’d still have to put some logic inside of your BPEL Process to choose which partner link to invoke. At that point, you’re not even using dynamic partner links. You’d simply be invoking one of 50 different partner links depending on what response you got from UDDI.

Hopefully this all makes sense and answers your questions.

- Chad -

Anonymous said...

Very good post.
I just had another question for you. In the '50 services scenario' what kind of input / output specification be for the dpl bpel?

I mean, how can the dpl bpel be made generic enough to invoke appropriate external web-service and propagate responses back to the caller?

Chad Sturtz said...

@anonymous

I'm assuming you're talking about the case in which you have 50 completely different services. If you don't want to go to all the work of importing a WSDL for each service and you want to make the bpel process generic enough to hit any of them, I have 2 ideas. Let me say though that I have not tested these and would, in 99.99% of cases, not recommend them.

1) Make the input to the process be a single element of type XSD:ANY. I believe you could then put your entire soap request body (everying inside the body tags) into this XSD:ANY element and transfer it to the SOAP body of the request sent to the target service. Note: you would probably need another element for any headers that need set.

2) Switch your dpl service from a SOAP binding to an HTTP binding. You could then provide the entire SOAP request to your dpl service and manipulate it as necessary before sending it to the target service.

The biggest reason I wouldn't recommend these is because you'd be forcing the client of your dpl service to have intimate knowledge of the soap request that needs sent to the target service. And at that point, the only reason you should be using JBI is if another part of your process offers a huge benefit.

Anonymous said...

Chad, thanks for you response.
Actually, let me explain you the scenario.

One of our internal business process (that we are modeling using bpel) requires us to simultaneously reach out to our partners (for this example say there are 3 partners) by invoking the web-services that they provision. The bpel would then consolidate the responses.

Obviously, we want to use uddi to dynamically extract the endpoints. And having a wrapper service (similar to your post) is desirable for proper decoupling.

So I was thinking of two options
1) having a wrapper service for each partner. The wrapper service would have inputs and outputs similar to the actual web-service.

2) having a generic wrapper service that would be invoked from the bpel. The service would take in a soap data as input along with a partnerName as parameters and then invoke the appropriate partner web-service and propagate back the response to the calling bpel process. What I am not sure is that if there is a way from the bpel process to invoke such a generic service.

Your thoughts please.

Thanks

Chad Sturtz said...

@anonymous

As far as option #1 goes, the obvious drawback is for every partner, you have to create a new wrapper service. So, as your list of partners grows, you have to come back and build a new wrapper service for each one.

As far as option #2 goes, there are a couple of drawbacks I see. The first is that in order to have a single generic service, you can't put any knowledge of the SOAP request/response elements into your process. Since every request/response could be a different service, you'd probably end up dealing with the request/response data at a high level, maybe by just passing around the soap message body contents as a single element instead of knowing what elements are actually included in the body. This will hurt if you need to do something like dissect a response to send pieces of it to another service (not impossible, but definitely more work). The second thing here is that you'd have to pass more data into the generic service to tell it what service to invoke. For example, just as you mentioned passing in the partner name. This would also have to include the appropriate business name and service name to perform your UDDI query if you really want it to be generic. If we were talking option 1, since the wrapper service would be specific to an individual partner, it could 'know' the business name and service name for the UDDI query and all you'd have to pass in is the data to send in the soap request.

When it comes to invoking such a service as in option #2, I believe it is possible (just haven't done it). I think your options would be the 2 things I laid out in my previous comment: using an HTTP binding instead of SOAP binding and passing in the entire soap envelope to post to the target service, or pass in the soap request data as a generic XSD:ANY element and put that in the outgoing soap body. The more I think about it, I believe that for the latter option to work, you’d have to create a generic wsdl with one operation that sent and received a single XSD:ANY element to be compatible with any service. No matter which way you go, a consumer would have to have intimate knowledge of the target service to be sure that the data he/she passes in will be accepted at the target service. It’s quite a big risk.

At the end of the day it just comes down to what fits your unique situation. If you'll only ever deal with a handful of partners, option 1 may be the way to go. But if you're list of partners could expand to a huge number, it may be better in the long run to investigate option 2.

Option #2 is no doubt going to be more work upfront than #1. It will also be much messier to build something that can handle invoking any service, as those target services become more complex themselves. However, if you get it to work, it’s much more extensible in terms of adding new partners.

Wong said...
This comment has been removed by the author.
Wong said...

Hello Chad,

I am trying to implement a service as you said. It is a wrapper service have a Object(xsd:anyType) as input, and also a Object(xsd:anyType) as output.

I able only able get a xml string for the input. Could you give a little detail on how to cast the xml string to Object, please.

Thank you.

Steve said...

This is such a nice blog! Also have a look at the one which is interesting - epharmarx