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.

The following information includes issues that you might have while migrating from JAX-RPC to JAX-WS and JAXB.
Note: When the terms migrate, migrating, and migration are used in this topic, unless there is some statement to indicate otherwise, the move from a JAX-RPC to a JAX-WS and JAXB environment is being described.

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, IBM developerWorks has published several web services hints and tips topics.

Development models

The high level combined development models for JAX-RPC and JAX-WS and JAXB are shown the following image: Compare 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.

The image displays the following components:
  • 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.

The following sample code in this topic was tested in the following runtime environment:
  • 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:

As shown in the image, the five operations offered are:
  • 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)
Also assume that Person is defined by the following schema fragment in the WSDL file:
<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.
Sample service

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.

JAX-RPC version of SampleService.java:
/**  
* 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; }   
For JAX-WS and JAXB, the tooling accepts the WSDL file as input, and as in the case of JAX-RPC, generates SampleService.java and SampleServiceImpl.java interfaces. As with JAX-RPC, the SampleService.java interface also defines an interface as shown in the following code block. The SampleServiceImpl.java interface provides the skeleton of an implementation, and you typically modify to add your own logic.
Note: The code has been annotated by the tooling.
JAX-WS and JAXB version of the SampleService.java interface:
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

At first glance, it might seem that there is little similarity between the interfaces. If you disregard the additional information added by the annotations for JAX-WS and JAXB, the code samples are similar. For example, the calcShippingCost method in the JAX-WS and JAXB version the following lines of code exist:
@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:

For JAX-RPC:
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 
Note: You might find it easier to compare the skeleton implementation files since the annotations are not present in SampleServiceImpl.java.:
  • SampleServiceSoapBindingImpl.java
  • SampleServiceImpl.java
The differences in signatures are due to the following reasons:
  • 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:
    <element maxOccurs="unbounded" name="itemNumbers" type="xsd:int"/>
    <element maxOccurs="unbounded" name="itemAvailability" type="xsd:string"/>
    The previous elements are mapped to the following Java source:
    int[] itemNumbers
    java.lang.String[] itemAvailability
    
    JAX-WS and JAXB map the following WSDL elements:
    List<Integer> itemNumbers 
    List<String> ckAvailability
  • Different mappings of exceptions
    For the ckAvailability method, the JAX-RPC code generated the following error:
    simple.InvalidDateFault
    The JAX-WS code generates the following error:
    InvalidDateFault_Exception
    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:
    throw new InvalidDateFault("this is an InvalidDateFault");
    For JAX-WS, the code uses a command similar to the following example::
    throw new InvalidDateFault_Exception( "this is an InvalidDateFault_Exception", new InvalidDateFault()); 
    In cases that use exceptions, the code that uses it needs to change.

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.

Assume that the original (JAX-RPC) implementation code is similar to following:
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;
}
When migrating the code for the ckAvailability method, changes were required because of the way that arrays and exceptions are mapped from XML is different between JAX-RPC and JAX-WS. Assume that the original JAX-RPC web service code is similar to the following example:
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.
This means that you must replace 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.