Web サービスのマイグレーション・シナリオ: JAX-RPC から JAX-WS および JAXB へ

このトピックでは、Java™ API for XML-based RPC (JAX-RPC) Web サービスを Java API for XML-Based Web Services (JAX-WS) および Java Architecture for XML Binding (JAXB) Web サービスにマイグレーションするためのシナリオについて説明します。

ツールの変更は、 WebSphere® Application Serverで使用可能なアセンブリー・ツールなどの開発環境によって、ユーザーからほとんど見えないようにすることができます。 また、XML で指定されるデータによっては、JAX-RPC サービスで使用されたいくつかのメソッドを、ほとんど、あるいはまったく変更せずに JAX-WS サービスでも使用できます。 ただし、場合によっては、JAX-WS サービスで変更が必要になることもあります。

Java EE 環境では互換性が強調されているため、より新しい JAX-WS および JAXB 仕様をサポートするほとんどのアプリケーション・サーバーは、引き続き従来の JAX-RPC 仕様をサポートします。 したがって、既存の Web サービスを JAX-RPC ベースのままにしておいて、JAX-WS および JAXB プログラミング・モデルを使用して新規の Web サービスを開発することができます。

ただし、時間の経過とともにアプリケーションの改訂と再作成が行われるため、やがては JAX-RPC ベースの Web サービスを JAX-WS および JAXB プログラミング・モデルに基づく Web サービスにマイグレーションするのが最善の方針となる時期が到来する可能性があります。 これは、ベンダーが、新規プログラミング・モデルでのみ使用可能なサービスの品質が得られるように機能強化することを選択することが原因です。 例えば、SOAP 1.2 および SOAP Message Transmission Optimization Mechanism (MTOM) サポートは、JAX-WS 2.x および JAXB 2.x プログラミング・モデルでのみ使用可能であり、JAX-RPC では使用できません。

以下の情報には、JAX-RPC から JAX-WS および JAXB のマイグレーションする際に発生する可能性のある問題が含まれています。
注: マイグレーションマイグレーション、および マイグレーション という用語がこのトピックで使用される場合は、特に明記されていない限り、JAX-RPC 環境から JAX-WS および JAXB 環境への移行について説明します。

JAX-RPC と JAX-WS および JAXB のプログラミング・モデルの比較

JAX-RPC、JAX-WS、および JAXB プログラミング・モデル間の相違を説明する追加情報が必要な場合、仕様および Web サービスのヒント・トピックに関する資料を参照してください。

開発モデル

JAX-RPC と JAX-WS および JAXB の統合開発モデルの概要を以下のイメージに示します。
開発モデルの比較

ハイレベルでは、これらのモデルは非常によく似ています。 相違点は、JAX-RPC モデルでは移植可能な成果物と移植できない成果物の両方が作成されるのに対し、JAX-WS および JAXB モデルでは、移植できない成果物が作成されないということのみです。

このイメージでは、以下のコンポーネントが示されています。
  • Web サービス記述言語 (WSDL) ファイル: W3C organization によって公開された WSDL 勧告に準拠する文書です。
  • (オプション) 構成ファイル:
    • WSDL から Java を生成するツール: これらの仕様は、ツールの名前ではなく、必要なマッピングを説明しています。 一般には、JAX-WS および JAXB 用のツールは wsimport であり、JAX-RPC 用のツールは wscompile と wsdeploy です。
    • 移植可能な成果物: これらは、WSDL から Java を生成するツールによって生成されたファイルであり、JAX-RPC または JAX-WS および JAXB の仕様 (いずれか、適用可能なもの) における、十分に定義されたマッピングを含みます。 これらの成果物は、JAX-RPC または JAX-WS および JAXB のさまざまな実装で、変更なしに使用することができます。
    • 移植できない成果物: これらは、WSDL から Java を生成するツールによって生成されたファイルであり、JAX-RPC または JAX-WS および JAXB の仕様 (いずれか、適用可能なもの) における、十分に定義されたマッピングを含みません。 これらの成果物は、一般に、JAX-RPC または JAX-WS/JAXB のさまざまな実装で使用するには変更が必要です・
    • 実装コード: これは、実装のために特定の要件を満たす必要のあるコードです。
    • デプロイメント情報: 特定の環境でアプリケーションを実行するために必要な追加情報です。
    • Web アーカイブ (WAR) ファイル: SampleService イメージのコンテキストでは、WAR ファイルは、Web サービスを特定の環境にデプロイするために必要なすべての項目を含むアーカイブ・ファイルです。 これは、cooked WAR ファイル とも呼ばれます。

開発およびランタイム環境

特殊化された開発環境で、多くのタスクを自動化することによって Web サービスのマイグレーションが簡素化されています。 WebSphereベースのアプリケーション (Web サービスを含む) を開発する場合は、 WebSphere Application Serverで提供されるアセンブリー・ツールを使用できます。

このトピックで以下に示すサンプル・コードは、以下のランタイム環境でテストされています。
  • IBM WebSphere Application Server V6.1。JAX-RPC 仕様のサポートが含まれています。
  • IBM WebSphere Application Server V6.1 Feature Pack for Web Services (JAX-WS および JAXB 仕様のサポートを含む)。

以下の例は、JAX-RPC Web サービスを JAX-WS および JAXB の Web サービスにマイグレーションするために必要な、いくつかのコード変更を示しています。 特に、クライアント・サイドとサーバー・サイドの両方で行う必要のある実装コードの変更が示されています。 新機能を導入しない場合、JAX-RPC から JAX-WS および JAXB に移行するためには、ほとんど変更の必要がない場合があります。

最初の例は WSDL ファイルから作成されたサンプルの JAX-RPC ベース Web サービスです (トップダウン開発)。同じ WSDL ファイルが、JAX-WS および JAXB ベースのサービスの生成に使用されます。 この WSDL ファイルは、以下のイメージで説明されている操作を含む、SampleService という Web サービスを示しています。

イメージで示されているように、次の 5 つの操作が提供されています。
  • 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)
また、WSDL ファイル内で、以下のスキーマ・フラグメントによって Person が定義されているものと想定します。
<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 は、以下で定義されています。
<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>
また、以下のイメージは、invalidDateFault 例外を生成する ckAvailability(xsd:int) 操作を示しています。
サンプル・サービス

ツールによって作成されるサービス・コードを検討してください。 以下の情報には、JAX-RPC ランタイムと JAX-WS および JAXB ランタイム用に作成されるコードの検査が含まれています。

JAX-RPC の場合、このツールは WSDL ファイルを入力として受け入れ、他のファイルとの間の SampleService.java インターフェースと SampleServiceImpl.java インターフェースを生成します。 SampleService.java インターフェースはインターフェースを定義します。生成されたコードは、以下のコード・ブロックで確認することができます。 SampleServiceSoapBindingImpl.java インターフェースは実装のスケルトンを提供します。一般には、これを変更して独自の論理を追加できます。

JAX-RPC バージョンの 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; }   
JAX-WS および JAXB の場合、このツールは WSDL ファイルを入力として受け入れ、JAX-RPC の場合と同じように SampleService.java インターフェースと SampleServiceImpl.java インターフェースを生成します。 JAX-RPC の場合と同じく、SampleService.java インターフェースは、以下のコード・ブロックに示されているインターフェースも定義します。 SampleServiceImpl.java インターフェースは実装のスケルトンを提供します。一般には、これを変更して独自の論理を追加できます。
注: ツールによってコードにアノテーションが付けられています。
JAX-WS および JAXB バージョンの SampleService.java インターフェース:
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);  

}   

コード・サンプルの比較

一見すると、インターフェースには類似性がほとんどないように見えます。 JAX-WS および JAXB に関するアノテーションによって追加された情報を除外すると、両方のコード・サンプルは類似しています。 例えば、JAX-WS および JAXB バージョンの calcShippingCost メソッドには、以下のコード行が存在します。
@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);
しかし、アノテーション部分を除去すると、以下のコード行になります。
public Integer calcShippingCost(
        Integer shippingWt,
        Integer shippingZone);
これらの行は、JAX-RPC について生成されたコードとほとんど同一です。 相違点は、JAX-RPC コードは以下のような java.rmi.RemoteException エラーを生成する可能性がある、ということです。
public java.lang.Integer calcShippingCost(java.lang.Integer shippingWt,
			java.lang.Integer shippingZone) throws java.rmi.RemoteException;
この論理に従うと、次の 3 つのメソッドのシグニチャーは基本的に同じになります。
public Integer calcShippingCost(Integer shippingWt, Integer shippingZone)
	public String getAccountNumber(String accountName)
	public Person findSalesRep(String saleRepName) 
つまり、JAX-RPC から JAX-WS へのマイグレーションはこれらのメソッドには直接的な影響を与えず、JAX-RPC ベースの環境で正常に実行される元の実装コードは、おそらく、変更無しでこれらのメソッドで使用できます。

ただし、次の 2 つのメソッドのシグニチャーは異なります。

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 および JAXB の場合:
public XMLGregorianCalendar calculateShippingDate( 			
	XMLGregorianCalendar requestedDate)  	
public List<String> ckAvailability(List<Integer> itemNumbers) 
	throws InvalidDateFault_Exception 
注: SampleServiceImpl.java にはアノテーションが存在しないため、スケルトン実装ファイルの比較が容易になる場合があります。
  • SampleServiceSoapBindingImpl.java
  • SampleServiceImpl.java
シグニチャーの違いは、以下の理由によります。
  • XML 名から Java 名へのマッピングの違い

    calculateShippingDate メソッドでは、入力パラメーターと戻りパラメーターの両方が java.util.Calendar タイプから XMLGregorianCalendar タイプに変更されています。 これは、WSDL がこれらのパラメーターを xsd:dateTime タイプのパラメーターとして指定し、JAX-RPC はこのデータ型を java.util.Calendar にマップし、JAX-WS および JAXB は XMLGregorianCalendar にマップするためです。

  • XML から Java への配列のマッピングの違い

    ckAvailability メソッドでは、XML 配列のデータ・マッピングによって違いが生じます。

    JAX-RPC は以下の WSDL エレメントをマップします。
    <element maxOccurs="unbounded" name="itemNumbers" type="xsd:int"/>
    <element maxOccurs="unbounded" name="itemAvailability" type="xsd:string"/>
    上のエレメントは以下の Java ソースにマップされます。
    int[] itemNumbers
    java.lang.String[] itemAvailability
    
    JAX-WS および JAXB は以下の WSDL エレメントをマップします。
    List<Integer> itemNumbers 
    List<String> ckAvailability
  • 例外のマッピングの違い
    ckAvailability メソッドについては、JAX-RPC コードは以下のエラーを生成していました。
    simple.InvalidDateFault
    JAX-WS コードは以下のエラーを生成します。
    InvalidDateFault_Exception
    これらの例外のコンストラクターは、名前以外は異なっています。 したがって、エラーを生成する実際の JAX-RPC コードは次のように表示されます。
    throw new InvalidDateFault("this is an InvalidDateFault");
    JAX-WS の場合、このコードは以下の例に類似したコマンドを使用します。
    throw new InvalidDateFault_Exception( "this is an InvalidDateFault_Exception", new InvalidDateFault()); 
    例外を使用する場合、それを使用するコードを変更する必要があります。

コードのマイグレーション

コードの相違について説明しましたので、変更が必要な元のコードを調べ、JAX-WS および JAXB 環境で機能するようにこのコードを変更する方法を検討します。

元の (JAX-RPC) 実装コードが次のようになっていると想定します。
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;
}
以下の例のような新規コードを書いて、新規タイプを直接使用させることができます。
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;
}
ckAvailability メソッド用のコードをマイグレーションするときには、XML から配列および例外をマップする方法が JAX-RPC と JAX-WS とで異なっているため、変更が必要でした。 元の JAX-RPC Web サービス・コードが次の例のようになっていると想定します。
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;
}
前のコードは int [] を入力として受け入れ、String [] を返します。JAX-WS および JAXB バージョンの場合、これらはそれぞれ List < Integer> エレメントと List < String> エレメントです。 Exception コードを除外すると、これらの配列を処理するための JAX-RPC コードは以下に類似したものになります。
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;
}
配列の代わりにリストを使用すると、上と等価な JAX-WS および JAXB コードは次のようになります。
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;
}
XML から Java への例外のマッピングの違いにより、使用する JAX-WS コードが強制的に決まります。 InvalidDateFault の代わりに、InvalidDateFault_Exception を使用する必要があります。
これは、 throw new simple.InvalidDateFault(“InvalidDateFault”); を他のコードに置き換える必要があることを意味します。 したがって、新規コードでは以下の行が使用されます。
throw new InvalidDateFault_Exception( "test InvalidDateFault_Exception", new InvalidDateFault());  
このメソッドの最終的な JAX-WS および JAXB 実装は、以下のコードに似たものになります。
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;
}

コードをマイグレーションする方法の選択肢は、複数あります。 実際の経験と効果的な開発環境を活用することにより、JAX-RPC から JAX-WS へのマイグレーションは簡単に行うことができます。