Using JAX-WS and JAXB with WebLogic Server 10: JAX-WS Customization Binding
Using a JAX-WS binding declaration to specify the SEI provider interface class name
The class binding declaration is used to specify the provider interface class for an SEI (Service Endpoint Implementation). Here's an extract from the etc/server-jaxws.xbd file, which shows the class binding declaration being used for this purpose:
<bindings
wsdlLocation="DataStagingService2.wsdl"
>
...
<bindings
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
node="wsdl:definitions/wsdl:portType[@name = 'DataStaging']"
>
<jxb:javadoc>
<![CDATA[<body>Name of Provider interface class for JAX-WS
DataStagingService</body>.]]>
</jxb:javadoc>
<class name="DataStagingService2"/>
</bindings>
...
</bindings>
Here, I'm telling the wsimport Ant task that I want the name of the SEI interface class it produces to be DataStagingService2. The wsdlLocation attribute gives the location of the WSDL file to process. This location is relative to where the binding declaration file is. I have it in the same directory as the WSDL file, so there is no path preceding it. The XPath expression selects the portType element in the WSDL, which is where the name of the generated interface class comes from, by default.
You'll find the resulting services.datastaging.DataStaging2.java file in the WebContent/WEB-INF/lib/DataStagingService2_wsdl.jar file.
Using a JAX-WS binding declaration to specify the skeleton SEI class name
You can also use the class binding declaration to control the name of the skeleton Java source file, which is generated by the wsimport Ant task. This happens when the wsdlc Ant task is executed. This is the same class binding declaration that is used for other things, so don't be confused by that.
I put the class binding declaration to specify the name of the skeleton Java source file in the etc/server-jaxws.xbd file. Here's an extract showing what it looks like:
<bindings
wsdlLocation="DataStagingService2.wsdl"
>
...
<bindings
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
node="wsdl:definitions/wsdl:service[@name = 'DataStagingService']"
>
<jxb:javadoc>
<![CDATA[<body>Name of skeleton SEI class for JAX-WS
DataStagingService</body>.]]>
</jxb:javadoc>
<class name="DataStagingPortTypeImpl"/>
</bindings>
...
</bindings>
The bold typeface denotes the salient points. They tell the wsimport Ant task that you want the name of the skeleton class to be DataStagingPortTypeImpl. If you didn't have this binding declaration, the name of the generated skeleton class would come from the value assigned to the name attribute of the <service> element in the WSDL.
Unfortunately, doing this didn't produce the desired outcome. The problems were:
-
The Java package name of the generated
DataStagingPortTypeImplclass came out ascom.acmeworld.irad.services.datastaging, notservices.datastaging. I tried using a "<package>services.datastaging</package>binding declaration, as well as one tied to a<jaxws:bindings>element with anode="wsdl:definitions"attribute. Neither provided the desired results. DataStagingPortTypeImplalso becomes the name of the JAX-WS class that extendsjavax.xml.ws.Service. This is a class used by the service consumer, so I "let it slide" because I was on the service provider side.- If you also specify a
methodbinding declaration to change Web service operation names, they are not propagated to theDataStagingPortTypeImplclass that gets generated in #1 above. It looks like changes made with theparameterbinding declaration don't get propagated either.
I think all of these anomalies are being caused by a bug in a class used with either the wsdlc or wsimport Ant tasks. There ended up being quite a few "side effects" to these anomalies, so as a workaround I chose to use Ant <replace> tasks on the skeleton SEI Java source that was generated.
Note: BEA support cases were filed to get the above issues "permanently resolved" in a WebLogic Server 10 patch or a future service pack.
Using a JAX-WS binding declaration to specify method names
The method binding declaration lets you control method names in code that is generated. One of the method names you can change is the getter for the proxy class in the generated Java source that extends javax.xml.ws.Service. Here's the associated extract from the etc/client-jaxws.xbd file. It appears in the etc/server-jaxws.xbd file as well:
<bindings
wsdlLocation="DataStagingService2.wsdl"
>
...
<bindings
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
node="wsdl:definitions/wsdl:service[@name = 'DataStagingService']/wsdl:port[@name = 'DataStagingServicePort']"
>
<jxb:javadoc>
<![CDATA[<body>Returns proxy object used as the
Dynamic Proxy client API when invoking Web service operations on the
DataStagingService</body> Web service.]]>
</jxb:javadoc>
<method name="getDataStagingPort"/>
</bindings>
... </bindings>
Here I change the getter method name from getDataStagingServicePort (the default name) to getDataStagingPort. You'll find the results in the test.consumers.datastaging.standalone.jaxws.DataStagingService.java file, which is in the lib/UC-01Client.jar file.
You can also change the method names used for Web service operations:
<bindings
wsdlLocation="DataStagingService2.wsdl"
>
...
<bindings
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
node="wsdl:definitions/wsdl:portType[@name='DataStaging']/wsdl:operation[@name='dataStaging']"
>
<jxb:javadoc>
<![CDATA[<body>Sends URIs of resources to be staged</body>]]>
</jxb:javadoc>
<method name="sendInputURIs"/>
</bindings>
...
</bindings>
Here I use a method binding declaration to change the name of the dataStaging Web service operation to sendInputURIs. You'll find the results in the test.consumers.datastaging.standalone.jaxws.DataStaging.java file, which is in the lib/UC-01Client.jar file.
Using a JAX-WS binding declaration to specify method parameter names
The parameter binding declaration allows you to control the parameter names used in the methods of the skeleton SEI class. It uses:
- A
partattribute containing the XPath expression used to locate the parameter name to change. - The
elementattribute for the<part>element selected in the WSDL. - A
nameattribute to specify the new name for the parameter.
Here's the associated extract from the etc/server-jaxws.xbd file:
<bindings
wsdlLocation="DataStagingService2.wsdl"
>
...
<bindings
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:tns="http://services.irad.acmeworld.com/datastaging"
node="wsdl:definitions/wsdl:portType[@name='DataStaging']/wsdl:operation[@name='dataStaging']"
>
<jxb:javadoc>
<![CDATA[<body>Input parameter for the
dataStaging</body> method.]]>
</jxb:javadoc>
<parameter part="wsdl:definitions/wsdl:message[@name='dataStaging']/wsdl:part[@name='parameters']" element="tns:dataStaging" name="inputURIs" />
</bindings>
...
</bindings>
Earlier, I used a class binding declaration to change the method name from dataStaging to sendInputURIs. Here, I'm trying to use a parameter binding declaration to change the parameter name from parameters to inputURIs. It didn't work, and the reason why appears to be because of the following important discovery:
The parameter binding declaration doesn't work if you also have a <enableWrapperStyle>true</enableWrapperStyle> binding declaration in the file.
It turns out that using the <enableWrapperStyle>true< enableWrapperStyle> binding declaration "automagically" results in the parameter name being changed to inputURIs. I decided to use <enableWrapperStyle>false</enableWrapperStyle> on the service consumer side instead. That way I could at least demonstrate this parameters binding declaration.
Using a JAX-WS binding declaration to specify exception class names
The class binding declaration has yet another use: allowing you to control the name of exception classes used on methods in the skeleton SEI class. Here's the associated extract from the etc/server-jaxws.xbd file:
<bindings
wsdlLocation="DataStagingService2.wsdl"
>
...
<bindings
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
node="wsdl:definitions/wsdl:portType[@name='DataStaging']/wsdl:operation[@name='dataStaging']/wsdl:fault[@name='JAXWSArticleException']"
>
<jxb:javadoc>
<![CDATA[<body>General purpose exception for JAX-WS
DataStagingService</body>.]]>
</jxb:javadoc>
<class name="DataStagingServiceException"/>
</bindings>
...
</bindings>
This will cause a DataStagingServiceException class to be generated. It will be placed in the services.datastaging directory inside the WebContent/WEB-INF/lib/DataStagingService2_wsdl.jar file.
Using a JAX-WS binding declaration to specify asynchrony
The JAX-WS specification defines two paradigms for writing asynchronous clients:
- Polling This paradigm uses polling to determine if the response is available. The return type from the invocation of the
operation Asyncmethod is an instance of thejavax.xml.ws.Responseobject. Thisjavax.xml.ws.Responseobject contains methods that allow you to:- Cancel the invocation.
- Block indefinitely, waiting on the server response.
- Block for a given time interval, waiting on the server response.
- Determine if the invocation was cancelled.
- Determine if the invocation completed, so you can subsequently get the response.
- Callback In this paradigm, the client provides a callback handler to accept and process the inbound response object. This callback handler needs to implement
javax.xml.ws.AsyncHandler, which has a singlevoid handleResponse(Response)callback method.javax.xml.ws.AsyncHandlerextendsjavax.xml.ws.Response, but you really only need to use theget()method.
Both paradigms allow the JAX-WS client to continue doing things while the request is being processed at the service provider. Here's an extract from our etc/client-jaxws.xbd file, which shows how to specify the <enableAsyncMapping> binding declaration:
<bindings
wsdlLocation="DataStagingService2.wsdl"
>
...
<bindings
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
node="wsdl:definitions/wsdl:portType[@name='DataStaging']/wsdl:operation[@name='dataStaging']"
>
<enableAsyncMapping>true</enableAsyncMapping>
</bindings>
...
</bindings>
This generates only an asynchronous version of the dataStaging method. If you want all the methods to have asynchronous versions, move the <enableAsyncMapping>true</enableAsyncMapping> to where the first ... is. The asynchronous methods will be in the test.consumers.datastaging.standalone.jaxws.DataStaging.java file, which is JARed up inside the lib/UC-01Client.jar file.