Tuesday, 13 January 2009

JAX-WS Provider API based endpoints in JDev 11g

This post constitutes my notes on implementing JAX-WS Provider APIs for web service endpoints in JDeveloper 11g.  Usual caveat: your mileage may vary.  In particular I may have screwed up the terminology, so please do your own reading to back up my findings.

My understanding is JDeveloper 11g has no inherit GUI facilities that support the JAX-WS Provider API, the JDev productivity features focus on the Service Endpoint Interface (SEI) JAX-WS approach, which is probably fine for 99% of the JAX-WS implementations.

As readers know the standard JAX-WS solution inheritly uses the JAXB XML binding style.  My understanding is JAXB binding is ideal in most uses of JAX-WS, particularly for small well defined XML payloads.  However for an upcoming project we're not sure of the size of our incoming payloads (we may have to use XBRL) so I wanted to explore other options over JAXB.  If you have a large and variable XML payload structure JAXB may be less than ideal thanks to the Java object structures JAXB creates, which at design time can be (dependent on your web service XML payload) large and complex to maintain, and at runtime possibly a large memory footprint.  The JAX-WS Provider API instead allows alternative binding styles such as DOM, SAX or StAX that have different advantages and disadvantages in parsing XML documents and retrieving results, which may be more suitable depending on your defined web services XML payload structures.

The following example demonstrates creating a Provider endpoint through JAX-WS in JDeveloper 11g.  The steps assume a JDeveloper 11g application "JaxWsProviderExample" based on the Generic Application template, and a single project "ProviderProject" with no project technologies initially.

1. Via the ProviderProject project properties, select Libraries and Classpath, and add the JAX-WS RI Web Services library:



2. For your web service define the XML schema with the incoming and outgoing payloads.  Create the XML schema in the project subdirectory /public_html/WEB-INF/wsdl.  In this example the XML schema is named sage.xsd:



The demonstrated XML schema in this example is nothing spectacular, just an incoming payload called Event demonstrating an embedded complexType BookingType, and a simple outgoing string payload named Response.

3. Create a WSDL for the web service and place it in the same /public_html/WEB-INF/wsdl subdirectory.

Refer to this previous blog entry for creating a WSDL step by step with JDeveloper 11g's GUI WSDL builder.  In addition check out the following viewlet from Oracle that not only shows you creating the WSDL via the JDev 11g builder, but uses drag n drop, and generates a JAX-WS SEI implementation with little hassle.

In this example the WSDL is named sageWsdl.wsdl:



Note from this WSDL example we're using SOAP v1.2.  Please, no laughing at my sophisticated service/port/porttype/message names! ;-)

4. Create our Java implementation of the Provider API.  In the following example I've created a class au.com.sagecomputing.ProviderImpl:



Note:
  • The getXMLFromSource method is not pivotal to this example (credit to Rahul Biswas for the source)
  • The @WebServiceProvider annotation maps back to port/service/namespace names in WSDL.
  • The @BindingType annotation is only necessary as we're using SOAP v1.2.  If you forget this line and you've specified SOAP v1.2 in your WSDL you'll receive a runtime exception about the wrong content-type header as follows (solution documented here):
SEVERE: Unsupported Content-Type: application/soap+xml; charset=UTF-8 Supported ones are: [text/xml]
com.sun.xml.ws.server.UnsupportedMediaException: Unsupported Content-Type: application/soap+xml; charset=UTF-8 Supported ones are: [text/xml]

  • Regardless of the number of operations defined in the WSDL, all will be passed to the chokepoint invoke method.  This means you need to interpret the incoming request using whichever binding style you've chosen (in this example javax.xml.transform.Source) and determine which operation the XML payload represents.
5. You'll note on creating the above class the @WebServiceProvider line will have a lightbulb code-insight hint in the JDeveloper Java editor.  Select the lightbulb and then select the single option to configure the web.xml file for your project.  This will result in a web.xml file as follows:

 

6. The JAX-WS RI implementation requires the file sun-jaxws.xml in your /public_html directory.  Create this file; example content based on the implementation shown in this post is as follows:



A handy reference for the sun-jaxws.xml file can be found here.

By careful to ensure the port name and url-pattern attributes match those specified in your WSDL and web.xml files respectively.

7. At the end of the above 6 steps you're application should look as follows:



8. Running the ProviderImpl via the JDeveloper web service tester tool (right click the ProviderImpl.java, select Test Web Service) shows the following results:

(Yes Gerard, I'm back to using HTTP Analyzer, not SoapUI! ;-)



5 comments:

Gerard Davison said...

Well,

That gave me a shock today, the internet talked back to me.

Seriously though you do expose a bug in your blog, the quick fix should have generated the correct sun-jaxws.xml entry as it does for normal web services.

Also did you need to select the RI library? This would work find with the normal JAX-WS library, indeed this is how normally would work and wouldn't required the extra sun deployment descriptor. I would be interested to hear if the vanilla version wouldn't work.

Finally for really big SOAP messages have you considered the "Streaming MTOMs". I only mention it because it sounds like the name of a band, or a kind of food poisoning.

:-)

Gerard

Chris Muir said...

Gerard's right, it's not necessary to select the JAX-WS RI library, instead the JAX-WS library will do. However at this stage deploying with either library to a standalone WLS throws an exception if the sunjax-ws.xml file isn't included. I'll have a chat to Gerard about this and see if we can resolve why the file is still needed for the JAX-WS library.

One anomaly I've detected when deploying to a standalone WLS server, the deployed app under the WLS console doesn't show the Provider as a web service. However the web service may still be invoked.

CM.

Chris Muir said...

Readers can download a demo JDev application here.

CM.

Andrew said...

"If you have a large and variable XML payload structure JAXB may be less than ideal thanks to the Java object structures JAXB creates, which at design time can be (dependent on your web service XML payload) large and complex to maintain, and at runtime possibly a large memory footprint. The JAX-WS Provider API instead allows alternative binding styles such as DOM, SAX or StAX that have different advantages and disadvantages in parsing XML documents and retrieving results, which may be more suitable depending on your defined web services XML payload structures."
Can you geve more info how to do this?

Chris Muir said...

Sorry, specifically what are you asking for an example of?

CM.