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 プログラミング・モデル間の相違を説明する追加情報が必要な場合、仕様および Web サービスのヒント・トピックに関する資料を参照してください。
開発モデル
ハイレベルでは、これらのモデルは非常によく似ています。 相違点は、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 サービスを示しています。
- 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 は、以下で定義されています。<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 インターフェースは実装のスケルトンを提供します。一般には、これを変更して独自の論理を追加できます。
/**
* 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);
}
コード・サンプルの比較
@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 つのメソッドのシグニチャーは異なります。
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
- 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 エレメントをマップします。
上のエレメントは以下の Java ソースにマップされます。<element maxOccurs="unbounded" name="itemNumbers" type="xsd:int"/> <element maxOccurs="unbounded" name="itemAvailability" type="xsd:string"/>
JAX-WS および JAXB は以下の WSDL エレメントをマップします。int[] itemNumbers java.lang.String[] itemAvailability
List<Integer> itemNumbers List<String> ckAvailability
- 例外のマッピングの違いckAvailability メソッドについては、JAX-RPC コードは以下のエラーを生成していました。
JAX-WS コードは以下のエラーを生成します。simple.InvalidDateFault
これらの例外のコンストラクターは、名前以外は異なっています。 したがって、エラーを生成する実際の JAX-RPC コードは次のように表示されます。InvalidDateFault_Exception
JAX-WS の場合、このコードは以下の例に類似したコマンドを使用します。throw new InvalidDateFault("this is an InvalidDateFault");
例外を使用する場合、それを使用するコードを変更する必要があります。throw new InvalidDateFault_Exception( "this is an InvalidDateFault_Exception", new InvalidDateFault());
コードのマイグレーション
コードの相違について説明しましたので、変更が必要な元のコードを調べ、JAX-WS および JAXB 環境で機能するようにこのコードを変更する方法を検討します。
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;
}
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 へのマイグレーションは簡単に行うことができます。