Friday, December 14, 2007

Dealing With Promotion, Or How To Change Which Web Service Gets Called Without Altering The Code

On of the difficulties of working with web services is how to deal with external dependencies as you promote code from development to QA to production. Take this JAX-WS code below as an example. Here was have a SMS service from VoodoPhone that makes use of an external translation service from Noodle.com. (Names has been changed to protect the innocent)

The problem comes from that fact that whilst using @WebServiceRef make everything really quite tidy is that the location of the WSDL, and therefore the service endpoint, is hard coded in the annotations and constants on the TranslatorService class. This makes it harder to replace the translation service with an internal or "mock" implementation during development time. You probably wouldn't want to rely on an external server for your internal regression tests for example.

Previously in JAX-RPC it was relatively obvious that you could create the proxy class and then change the endpoint. Indeed in the JAX-WS SE case you can do the same but it requires you to write code to externalize the wsdl location and this is not very JEE. Another way around the problem is to regenerate the proxy for each target system; but this is a pain and it probably takes you away from the pleasant land of the IDE into the wilds of ant tasks and complex build scripts.

I turns out that in the EE case, when using @WebServiceRef, that each injection point can be modified at deployment time with a new WSDL location. This is all done using JNDI behind the scenes; but it is suitably abstracted away from the developer.

In the next screen grab you can see the JSR-88 "Deployment Plan" dialog that you will see when you try to deploy using JDeveloper 11. (Normal disclaimers as regards to this not being production software) As you can see it has picked up the the fact that there is an injection point as part of the deployment process. There is no "service-ref" entry in my web.xml but the project the deployment process has worked out that one might be needed.

As you can see you can load and save deployment plans so you can have different configurations depending on whether you are doing development or even promoting to a production system.

You can of course do this by just editing the web.xml file yourself to alter the enviroment. The JNDI name is the name of the class and the name of the field with a slash between then. This is the "com.voodophone.SMSService/translationService" node in the tree on the left of the panel. There is no reason why you cannot type this yourself into web.xml as required or add in later using the deployment tool of your choice.

<web-app>
  ...
  <service-ref>
    <service-ref-name>com.voodophone.SMSService/translationService</service-ref-namae>
    <wsdl-file>http://localhost:8099/mockTranslation</wsdl-file>
  </service-ref>
  ...
</web-app>

The main thing to take away from this is that whilst a lot of the new annotation stuff appears to work like magic, underneath there is a JNDI assistant doing all of the dirty work. You might not have to use JNDI directly but understanding how it works under the covers allows you to leverage the indirection in some really quite powerful ways. Just remember to make sure that the different WSDL define the same interface.....

Update: Sadly the deployment plan is broken in this specific area in JDeveloper TP3, hopefully it should be working in the next release. Or you will find similar features in other tools/servers that support JSR-88.