Tuesday, July 22, 2008

Parameter customization in JAX-WS

After many months on Oracle BPEL, I am testing Apache ODE (Apache BPEL engine). ODE provides a web service which allows to administer the engine including deployment. To simplify developments, I have decided to create a deployment client by using JAX-WS.

To generate the different classes corresponding to the ODE Web Service, I have used the wsimport tool and the following error is appeared :

[ERROR] Invalid operation "deploy", can't generate java method parameter. Local name of the wrapper child "package" in the global element "{http://www.apache.org/ode/pmapi}deploy" is a java keyword. Use customization to change the parameter name.
line 138 of http://localhost:8080/ode/processes/DeploymentService?wsdl

the problem is coming from the Apache ODE wsdl and specially from this code :

...
<xsd:element name="deploy">
<xsd:complexType>
<xsd:sequence>
<xsd:element form="unqualified" name="name" type="xsd:string"/>
<xsd:element form="unqualified" name="package" type="ns30:package"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
...
<wsdl:message name="deployInput">
<wsdl:part name="parameters" element="ns0:deploy"/>
</wsdl:message>
...
<wsdl:portType name="DeploymentServicePortType">
...
<wsdl:operation name="deploy">
<wsdl:input message="tns:deployInput" Action="http://www.apache.org/ode/deployapi/DeploymentPortType/deployRequest"/>
<wsdl:output message="tns:deployOutput" wsaw:Action="http://www.apache.org/ode/deployapi/DeploymentPortType/deployResponse"/>
</wsdl:operation>
...


The error is easy to understand : you cannot transform in a java class an xml element which is named like a java keyword otherwise a compilation error would occur. The problem is that we cannot change the contract, it is Apache which provides it. What can we do ? the solution is JAX-WS Customization : it allows us to adapt the different generated classes.

For our case, we need to rename the package element by, per example, paramPackage. Firstly, we need to create a customization file (we will call it bindings.xml) :

<jaxws:bindings xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:jaxws="http://java.sun.com/xml/ns/jaxws"
wsdlLocation="http://localhost:8080/ode/processes/DeploymentService?wsdl">

<jaxws:bindings node="wsdl:definitions/wsdl:portType[@name='DeploymentServicePortType']/wsdl:operation[@name='deploy']">

<jaxws:parameter
part="wsdl:definitions/wsdl:message[@name='deployInput']/wsdl:part[@name='parameters']"
childElementName="package" name="paramPackage" />
</jaxws:bindings>
</jaxws:bindings>


Let's see this file in detail.

<jaxws:bindings xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:jaxws="http://java.sun.com/xml/ns/jaxws"
wsdlLocation="http://localhost:8080/ode/processes/DeploymentService?wsdl">


indicates that the source wsdl is wsdllocation.

<jaxws:bindings
node="wsdl:definitions/wsdl:portType[@name='DeploymentServicePortType']/wsdl:operation[@name='deploy']">


indicates that the target operation the parameter belongs to is deploy and that this operation has DeploymentServicePortType as port type.

<jaxws:parameter
part="wsdl:definitions/wsdl:message[@name='deployInput']/wsdl:part[@name='parameters']"
childElementName="package" name="paramPackage" />


indicates to wsimport that the xml element package must be mapped to paramPackage in Java. Moreover it is indicated that package element corresponds to parameters part of deployInput message.

Now you have just to use wsimport with the following options to apply the customization file on generated classes :

wsimport -keep -b <PROJECT_ROOT>/jaxws/bindings.xml -d <PROJECT_ROOT>/src -p com.javasoa.ws.ode http://localhost:8080/ode/processes/DeploymentService?wsdl


Finally, if you look at the generated web service interface, you can see that package parameter is renamed as paramPackage :

...
@WebService(name = "DeploymentServicePortType", targetNamespace = "http://www.apache.org/ode/deployapi")
@XmlSeeAlso({
ObjectFactory.class
})
public interface DeploymentServicePortType {
...
public DeployUnit deploy(
@WebParam(name = "name", targetNamespace = "")
String name,
@WebParam(name = "package", targetNamespace = "")
Package paramPackage);

4 comments:

Anonymous said...

Great! Thanks a lot for this article. It allows me to resolve an old old problem in my SOA app. Simone ( Italy )

Grégory Le Bonniec said...

prego !

vijay dange said...

Hey,

It was very good artical and whatever information provided is really helped. I was done everyting and lastly found that we need to add com.sun.xml.ws.transport.http.servlet.WSServlet to start service.

Anonymous said...

Thanks for writing this valuable blog. It helped me solve my problem