Web services migration scenarios: JAX-RPC to JAX-WS and JAXB
This topic explains scenarios for migrating your Java™ API for XML-based RPC (JAX-RPC) web services to Java API for XML-Based Web Services (JAX-WS) and Java Architecture for XML Binding (JAXB) Web services.
Changes in the tooling can be largely hidden from the user by a development environment like the assembly tools available with WebSphere® Application Server. Also, depending on the data specified in the XML, some methods that were used for the JAX-RPC service can be used with little or no change in a JAX-WS service. There are also conditions that do require changes to be made in the JAX-WS service.
Because Java EE environments emphasize compatibility, most application servers that offer support for the newer JAX-WS and JAXB specifications continue to support the previous JAX-RPC specification. A consequence of this is that existing web services are likely to remain JAX-RPC based while new web services are developed using the JAX-WS and JAXB programming models.
However, as time passes and applications are revised and rewritten, there might be times when the best strategy is to migrate a JAX-RPC based web service to one that is based on the JAX-WS and JAXB programming models. This might result from a vendor choosing to provide enhancements to qualities of service that are only available in the new programming models. For example, SOAP 1.2 and SOAP Message Transmission Optimization Mechanism (MTOM) support are only available within the JAX-WS 2.x and JAXB 2.x programming models and not JAX-RPC.
Comparison of JAX-RPC to JAX-WS and JAXB programming models
If you are looking for additional information that describes the changes between the JAX-RPC and JAX-WS and JAXB programming models, see the specifications and publications for web services hints and tips topics.
Development models
At a high level, the models are very similar. The only difference is that the JAX-RPC model produces both portable and non-portable artifacts; the JAX-WS and JAXB model does not produce non-portable artifacts.
- Web Services Description Language (WSDL) file: A document conforming to the WSDL recommendation as published by the W3C organization.
- (Optional) Configuration files:
- WSDL to Java generation tool: The specifications describe the mappings required, not the name of the tools. Typically, the tool for JAX-WS and JAXB is wsimport, and for JAX-RPC the tools are wscompile and wsdeploy.
- Portable artifacts: These are files generated by the WSDL to Java generation tool that have well-defined mappings in the JAX-RPC or JAX-WS and JAXB, whichever is applicable, specifications. These artifacts can be used without modification between different implementations of JAX-RPC or JAX-WS and JAXB.
- Non-portable artifacts: These are files generated by the WSDL to Java generation tool that do not have well-defined mappings in the JAX-RPC or JAX-WS and JAXB, whichever is applicable, specifications. These artifacts generally require modification between different implementations of JAX-RPC or JAX-WS/JAXB.
- Implementation code: This is the code that you need to meet particular requirements for implementation.
- Deployment information: Additional information that is needed to run the application in a particular environment.
- Web archive (WAR) file: In the context of the SampleService image, a WAR file is an archive file that contains all items needed to deploy a web service into a particular environment. This is sometimes called a cooked WAR file.
The Development and runtime environments
A specialized development environment simplifies web services migration by automating many of the tasks. For development of WebSphere-based applications, including web services, you can use the assembly tools provided with WebSphere Application Server.
- IBM WebSphere Application Server V6.1, which includes support for the JAX-RPC specification.
- IBM WebSphere Application Server V6.1 Feature Pack for Web Services, which includes support for the JAX-WS and JAXB specifications.
Examples
The following examples show some of the code changes that you might need to migrate your JAX-RPC Web services to JAX-WS and JAXB web services. In particular, there is an emphasis on the changes in the implementation code that you must write, from both the client and the server side. If no new function is to be introduced, the changes required to move from JAX-RPC to JAX-WS and JAXB might be few.
The first example is a sample JAX-RPC-based web service that was created from a WSDL file (top-down development); the same WSDL file is used to generate the JAX-WS and JAXB-based service. The WSDL file describes the web service, SampleService, with these operations described by the following image:
- xsd:int calcShippingCost(xsd:int, xsd:int)
- xsd:string getAccountNumber(xsd:string)
- xsd:dateTime calculateShippingDate(xsd:dateTime)
- xsd:string ckAvailability(xsd:int) creates invalidDateFault
- Person findSalesRep(xsd:string)
<complexType name="Person">
<sequence>
<element name="name" type="xsd:string"/>
<element name="age" type="xsd:int"/>
<element name="location" type="impl:Address/>
</sequence>
</complexType>
Address is defined by:
<complexType name="Address">
<sequence>
<element name="street" type="xsd:string"/>
<element name="city" type="xsd:string"/>
<element name ="state" type="xsd:string"/>
<element name="zip" type="xsd:int"/>
</sequence>
</complexType>
The following image also shows that the operation, ckAvailability(xsd:int), which produces an
invalidDateFault exception. Review the service code created by the tooling. The following information includes examining what is created for a JAX-RPC runtime and also for a JAX-WS and JAXB runtime.
For JAX-RPC, the tooling accepts the WSDL file as input, and, amongst other files, generates the SampleService.java and SampleServiceImpl.java interfaces. The SampleService.java interface defines an interface and the generated code can be review in the following code block. The SampleServiceSoapBindingImpl.java interface provides the skeleton of an implementation, and you typically modify to add your own logic.
/**
* SampleService.java
*
* This file was auto-generated from WSDL
* by the IBM web services WSDL2Java emitter.
* cf20633.22 v82906122346
*/
package simple;
public interface SampleService extends java.rmi.Remote {
public java.lang.Integer calcShippingCost(java.lang.Integer shippingWt,
java.lang.Integer shippingZone) throws java.rmi.RemoteException;
public java.lang.String getAccountNumber(java.lang.String accountName)
throws java.rmi.RemoteException;
public java.lang.String[] ckAvailability(int[] itemNumbers)
throws java.rmi.RemoteException, simple.InvalidDateFault;
public java.util.Calendar calculateShippingDate(
java.util.Calendar requestedDate)
throws java.rmi.RemoteException;
public simple.Person findSalesRep(java.lang.String saleRepName)
throws java.rmi.RemoteException; }
package simple;
import java.util.List;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.ws.RequestWrapper;
import javax.xml.ws.ResponseWrapper;
/**
* This class was generated by the JAX-WS SI.
* JAX-WS RI IBM 2.0_03-06/12/2007 07:44 PM(Raja)-fcs
* Generated source version: 2.0
*
*/
@WebService(name = "SampleService", targetNamespace = "http://simple") public interface SampleService {
/**
*
* @param shippingWt
* @param shippingZone
* @return
* returns java.lang.Integer
*/
@WebMethod
@WebResult(name = "shippingCost", targetNamespace = "")
@RequestWrapper(localName = "calcShippingCost", targetNamespace = "http://simple",
className = "simple.CalcShippingCost")
@ResponseWrapper(localName = "calcShippingCostResponse", targetNamespace = "http://simple",
className = "simple.CalcShippingCostResponse")
public Integer calcShippingCost(
@WebParam(name = "shippingWt", targetNamespace = "")
Integer shippingWt,
@WebParam(name = "shippingZone", targetNamespace = "")
Integer shippingZone);
/**
*
*
@param accountName
* @return
* returns java.lang.String
*/
@WebMethod
@WebResult(name = "accountNumber", targetNamespace = "")
@RequestWrapper(localName = "getAccountNumber", targetNamespace = "http://simple",
className = "simple.GetAccountNumber")
@ResponseWrapper(localName = "getAccountNumberResponse", targetNamespace = "http://simple",
className = "simple.GetAccountNumberResponse")
public String getAccountNumber(
@WebParam(name = "accountName", targetNamespace = "")
String accountName);
/**
*
* @param requestedDate
* @return
* returns javax.xml.datatype.XMLGregorianCalendar
*/
@WebMethod
@WebResult(name = "actualDate", targetNamespace = "")
@RequestWrapper(localName = "calculateShippingDate", targetNamespace = "http://simple",
className = "simple.CalculateShippingDate")
@ResponseWrapper(localName = "calculateShippingDateResponse", targetNamespace = "http://simple",
className = "simple.CalculateShippingDateResponse")
public XMLGregorianCalendar calculateShippingDate(
@WebParam(name = "requestedDate", targetNamespace = "")
XMLGregorianCalendar requestedDate);
/**
*
* @param itemNumbers
* @return
* returns java.util.List<java.lang.String>
* @throws InvalidDateFault_Exception
*/
@WebMethod
@WebResult(name = "itemAvailability", targetNamespace = "")
@RequestWrapper(localName = "ckAvailability", targetNamespace = "http://simple", className = "simple.CkAvailability")
@ResponseWrapper(localName = "ckAvailabilityResponse", targetNamespace = "http://simple",
className = "simple.CkAvailabilityResponse")
public List<String> ckAvailability(
@WebParam(name = "itemNumbers", targetNamespace = "")
List<Integer> itemNumbers)
throws InvalidDateFault_Exception
;
/**
*
* @param saleRepName
* @return
* returns simple.Person
*/
@WebMethod
@WebResult(name = "salesRepInfo", targetNamespace = "")
@RequestWrapper(localName = "findSalesRep", targetNamespace = "http://simple", className = "simple.FindSalesRep")
@ResponseWrapper(localName = "findSalesRepResponse", targetNamespace = "http://simple",
className = "simple.FindSalesRepResponse")
public Person findSalesRep(
@WebParam(name = "saleRepName", targetNamespace = "")
String saleRepName);
}
Comparing the code examples
@WebMethod
@WebResult(name = "shippingCost", targetNamespace = "")
@RequestWrapper(localName = "calcShippingCost", targetNamespace = "http://simple",
className = "simple.CalcShippingCost")
@ResponseWrapper(localName = "calcShippingCostResponse", targetNamespace = "http://simple",
className = "simple.CalcShippingCostResponse")
public Integer calcShippingCost(
@WebParam(name = "shippingWt", targetNamespace = "")
Integer shippingWt,
@WebParam(name = "shippingZone", targetNamespace = "")
Integer shippingZone);
But, if you discard the annotations,
the following lines of code are: public Integer calcShippingCost(
Integer shippingWt,
Integer shippingZone);
These lines are almost identical to what was generated
for JAX-RPC. The only difference is that the JAX-RPC code can produce
the java.rmi.RemoteException error as follows:public java.lang.Integer calcShippingCost(java.lang.Integer shippingWt,
java.lang.Integer shippingZone) throws java.rmi.RemoteException;
Following this logic, three of the methods have essentially
the same signatures:public Integer calcShippingCost(Integer shippingWt, Integer shippingZone)
public String getAccountNumber(String accountName)
public Person findSalesRep(String saleRepName)
This means that migrating from JAX-RPC to JAX-WS does
not directly affect these methods and the original implementation
code that is successfully running in the JAX-RPC based environment
can probably be used without modification for these methods.However two of the methods do have different signatures:
public java.util.Calendar calculateShippingDate(
java.util.Calendar requestedDate)
public java.lang.String[] ckAvailability(int[] itemNumbers)
throws java.rmi.RemoteException,
simple.InvalidDateFault
JAX-WS and JAXB: public XMLGregorianCalendar calculateShippingDate(
XMLGregorianCalendar requestedDate)
public List<String> ckAvailability(List<Integer> itemNumbers)
throws InvalidDateFault_Exception
- SampleServiceSoapBindingImpl.java
- SampleServiceImpl.java
- Differences mappings from XML names to Java names
For the calculateShippingDate method, both the input parameter and the return parameter have changed from type java.util.Calendar to type XMLGregorianCalendar. This is because the WSDL specified these parameters to be of type, xsd:dateTime, JAX-RPC maps this data type to java.util.Calendar, while JAX-WS and JAXB maps it to XMLGregorianCalendar.
- Different mappings of arrays from XML to Java
For the ckAvailability method, the change is due to the data mappings for XML arrays.
JAX-RPC maps the following WSDL elements:
The previous elements are mapped to the following Java source:<element maxOccurs="unbounded" name="itemNumbers" type="xsd:int"/> <element maxOccurs="unbounded" name="itemAvailability" type="xsd:string"/>
JAX-WS and JAXB map the following WSDL elements:int[] itemNumbers java.lang.String[] itemAvailability
List<Integer> itemNumbers List<String> ckAvailability
- Different mappings of exceptionsFor the ckAvailability method, the JAX-RPC code generated the following error:
The JAX-WS code generates the following error:simple.InvalidDateFault
Except for the names, the constructors for these exceptions are different. Therefore, the actual JAX-RPC code that produces the error might be displayed as:InvalidDateFault_Exception
For JAX-WS, the code uses a command similar to the following example::throw new InvalidDateFault("this is an InvalidDateFault");
In cases that use exceptions, the code that uses it needs to change.throw new InvalidDateFault_Exception( "this is an InvalidDateFault_Exception", new InvalidDateFault());
Migrating the code
Now that the differences between code have been explained, review the original code for the methods that need changing and how to change this code so that it works in a JAX-WS and JAXB environment.
public java.util.Calendar calculateShippingDate(
java.util.Calendar requestedDate) throws java.rmi.RemoteException {
// Set the date to the date that was sent to us and add 7 days.
requestedDate.add(java.util.Calendar.DAY_OF_MONTH, 7);
// . . .
return requestedDate;
}
You can write the new code to directly use the new types
as in the following examples:public XMLGregorianCalendar calculateShippingDate(
XMLGregorianCalendar requestedDate) {
try {
// Create a data type factory.
DatatypeFactory df = DatatypeFactory.newInstance();
// Set the date to the date that was sent to us and add 7 days.
Duration duration = df.newDuration("P7D");
requestedDate.add(duration);
} catch (DatatypeConfigurationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// . . .
return requestedDate;
}
public java.lang.String[] ckAvailability(int[] itemNumbers)
{
String[] myString = new String[itemNumbers.length];
for (int j = 0; j < myString.length; j++) {
. . .
if ( ... )
throw new simple.InvalidDateFault(“InvalidDateFault”);
. . .
if ( . . .)
myString[j] = “available: “ + jitemNumbers[j] ;
else
myString[j] = “not available: “ + jitemNumbers[j];
}
return myString;
}
The previous code accepts an int[] as input and returns
a String[]. For the JAX-WS and JAXB version, these are List<Integer>
and List<String> elements respectively. Processing for these
arrays, and discarding the Exception code, the JAX-RPC code is similar
to the following:public java.lang.String[] ckAvailability(int[] itemNumbers)
{
String[] myString = new String[itemNumbers.length];
for (int j = 0; j < jitemNumbers.length; j++) {
. . .
if ( . . .)
myString[j] = “available: “ + itemNumbers.get(j);
else
myString[j] = “not available: “ + itemNumbers.get(j);
}
return myString;
}
The following JAX-WS and JAXB equivalent code exists using
Lists instead of arrays:List <String> ckAvailability(List <Integer> itemNumbers)
{
ArrayList<String> retList = new ArrayList<String>();
for (int count = 0; count < itemNumbers.size(); count++) {
. . .
if ( . . .)
retList.add(“available: “ + itemNumbers.get(j));
else
retList.add(“not available: “ + itemNumbers.get(j));
}
return retList;
}
The differences in the mappings of exceptions from XML
to Java forces the JAX-WS code to use. InvalidDateFault_Exception
instead of InvalidDateFault.throw new
simple.InvalidDateFault(“InvalidDateFault”);
with some other code. Therefore, the following
line is used for the new code:
throw new InvalidDateFault_Exception( "test InvalidDateFault_Exception", new InvalidDateFault());
The final JAX-WS and JAXB implementation of the method might be similar to the following
code:List <String> ckAvailability(List <Integer> itemNumbers)
{
ArrayList<String> retList = new ArrayList<String>();
for (int count = 0; count < itemNumbers.size(); count++) {
if ( . . . ) {
throw new InvalidDateFault_Exception(
"test InvalidDateFault_Exception",
new InvalidDateFault());
}
. . .
if ( . . .)
retList.add(“available: “ + itemNumbers.get(count));
else
retList.add(“not available: “ + itemNumbers.get(count));
}
return retList;
}
There are multiple ways that you might choose to migrate the code. With practice and an effective development environment, migrating from JAX-RPC to JAX-WS can be straightforward.