Web services migration scenarios: JAX-RPC to JAX-WS and JAXB

This topic explains scenarios for migrating the JAX-RPC web services to JAX-WS and 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 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 we might have while migrating from JAX-RPC to JAX-WS and JAXB.

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 we 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:

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:


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, we can use the assembly tools provided with WebSphere Application Server.

The following sample code in this topic was tested in the following runtime environment:


Examples

The following examples show some of the code changes that we might need to migrate the 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:

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.

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 our 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 our own logic.

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 we 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:

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 

You might find it easier to compare the skeleton implementation files since the annotations are not present in SampleServiceImpl.java.:

The differences in signatures are due to the following reasons:


Migrate 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 sent to us and add 7 days.
requestedDate.add(java.util.Calendar.DAY_OF_MONTH, 7);

// . . .

return requestedDate;
}
We 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 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:

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.


Related concepts

  • Development and assembly tools


    Related tasks

  • Task overview: Implement web services applications