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.
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.
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.
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 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.
Sample Web service
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:
<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.
Service operations
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.
JAX-RPC
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; }JAX-WS and JAXB
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); }
@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.InvalidDateFaultJAX-WS and JAXB:
public XMLGregorianCalendar calculateShippingDate( XMLGregorianCalendar requestedDate) public List<String> ckAvailability(List<Integer> itemNumbers) throws InvalidDateFault_Exception
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.
For the ckAvailability method, the change is due to the data mappings for XML arrays.
<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[] itemAvailabilityJAX-WS and JAXB map the following WSDL elements:
List<Integer> itemNumbers List<String> ckAvailability
simple.InvalidDateFaultThe JAX-WS code generates the following error:
InvalidDateFault_ExceptionExcept 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.
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; }
Migrating the code for the ckAvailability method
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 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.