サービスインタフェースにおける xsd:anyType による XML ドキュメントの記述: デザインの詳細

java.net 主催の bpcatalog プロジェクトにあるドキュメント指向のアプリケーションは、ドキュメント中心のインタフェースとエンドポイントのデザインでドキュメント型として xsd:anyType を使用する方法の具体例です。このアプリケーションには、ドキュメント型として xsd:anyType を使用するようにデザインされたサービスエンドポイントがあります。XML ドキュメントは、クライアントが送信する注文書で表されます。

このアプリケーションは、3 つの主要エンティティーで構成されています。

サービスの WSDL ファイル

WSDL ファイルは Web サービスの記述です。コード例 1 は、この サービスの WSDL からの抜粋です。開発スタイルとしては、WSDL から始めて、そこから必要な Java[tm] クラスを生成するスタイルを採用しました。このため、WSDL のメッセージ部に適切な型を選択することは非常に重要です。これらの型が、生成される Java インタフェースに含まれるパラメータの型になります。 要求メッセージが type="anyType" と宣言されていることに注意してください。この宣言のため、サービスに対して コード例 2 に示す Java インタフェースが生成され、入力パラメータは Java SOAPElement 型にマッピングされています。また、WSDL 内にアプリケーション定義のサービス例外が宣言されていることにも注意してください。

  <types>
      <schema xmlns="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" targetNamespace="urn:AnyTypePurchaseOrderService">
      <complexType name="submitPO">
    <sequence>
      <element name="BusinessDocumentRequest" type="anyType" nillable="true"/>                                            
    </sequence>
      </complexType>
      <complexType name="submitPOResponse">
    <sequence>
     <element name="result" type="string" nillable="true"/>                                               
    </sequence>
      </complexType>
      <complexType name="InvalidPOException">
    <sequence>
      <element name="message" type="string" nillable="true"/>                   
    </sequence>
      </complexType>
      <element name="submitPO" type="tns:submitPO"/>
      <element name="submitPOResponse" type="tns:submitPOResponse"/>
      <element name="InvalidPOException" type="tns:InvalidPOException"/>
      </schema>
    </types>
    <message name="AnyTypePurchaseOrderServiceSEI_submitPO">
        <part name="parameters" element="ns1:submitPO"/>
    </message>
    <message name="AnyTypePurchaseOrderServiceSEI_submitPOResponse">
        <part name="result" element="ns1:submitPOResponse"/>
    </message>
    <message name="InvalidPOException">
        <part name="InvalidPOException" element="ns1:InvalidPOException"/>
    </message>
    <portType name="AnyTypePurchaseOrderServiceSEI">
        <operation name="submitPO">
            <input message="tns:AnyTypePurchaseOrderServiceSEI_submitPO"/>
            <output message="tns:AnyTypePurchaseOrderServiceSEI_submitPOResponse"/>
            <fault name="InvalidPOException" message="tns:InvalidPOException"/>
        </operation>
    </portType>

コード例 1: サービスの WSDL からの抜粋

サービスエンドポイントインタフェース

WSDL から始めているため、サービスエンドポイントインタフェースは、WSDL に含まれる情報に基づいて生成されます。サービスエンドポイントインタフェースには、javax.xml.soap.SOAPElement オブジェクトを受け付ける submitPO() メソッドがあります。また、アプリケーション定義の例外 InvalidPOException も WSDL から生成されます。

public interface AnyTypePurchaseOrderServiceSEI extends Remote {
    public String submitPO(SOAPElement businessDocumentRequest) throws
        InvalidPOException,  RemoteException;
}
コード例 2: サービスエンドポイントインタフェース

Java エンドポイント実装

このアプリケーションでは、EJB[tm] (Enterprise JavaBeans[TM]) コンポーネントを使用して、サービスインタフェースを実装しています。代わりに、Web コンポーネントを使用してエンドポイントを実装することもできます。どちらでも問題ありません。大切なことは、注文書 XML が SOAPElement として処理されることです。コード例 3 は、エンドポイント実装クラスを示しています。

public class AnyTypePurchaseOrderServiceBean implements SessionBean {
   
    private SessionContext sc;
    private POXMLUtil xmlUtil;
   
    public AnyTypePurchaseOrderServiceBean() {}
   
    public String submitPO(SOAPElement request) throws InvalidPOException, RemoteException {
        String poID = null;
        SOAPElement reply = null;
        try {
            NodeList list = ((Element)request).getElementsByTagName("poId");
            for (int loop = 0; loop < list.getLength(); loop++) {
                Node node = list.item(loop);
                if (node != null) {
                    Node child = node.getFirstChild();
                    if ((child != null) && child.getNodeValue() != null){
                        poID = child.getNodeValue();
                    }
                }
            }                      
        } catch (Exception exe) {
            throw new EJBException("AnyTypePOService Having Problems:"+exe.getMessage(), exe);
        }
        // アプリケーションに固有の例外のスローを紹介することのみを目的としたコード
        if(poID.equals("100"))
            throw new InvalidPOException("Invalid ID for the purchase order!!! " +
                    "For demo purposes, we throw " +
                    "an application defined exception for the ID value of 100.");
        return poID;
    }
   
    // ライフサイクルメソッド
    ...
}
コード例 3: エンドポイント実装

サービスにアクセスするためのクライアントコード

サービス側コードに続いて、このアプリケーションのクライアント側を簡単に説明します。クライアントはコード例 1 の WSD ファイルを使用し、コード例 4 に示すような Java サービスインタフェースを生成します。コード例 5 のクライアントコードで示しているように、この Java インタフェースは注文書をサービスに送信するために使用されます。このクライアントアプリケーションの注文書データは、コード例 6 に示すように、クライアント定義の PurchaseOrder 型にデータを保持する Web アプリケーションからの入力として収集され、この注文書 Java オブジェクトは SOAPElement に変換されてから、サービスに送信されることに注意してください。

public interface AnyTypePurchaseOrderServiceSEI extends Remote {

    public String submitPO(SOAPElement businessDocumentRequest) throws
        com.sun.j2ee.blueprints.docoriented.client.anytypeposervice.InvalidPOException, RemoteException;
}

コード例 4: Web サービスへのアクセスに使用するクライアント生成の Java サービスインタフェース

public class AnyTypePOServiceBD {
   
    private ServiceLocator serviceLocator;
   
    public AnyTypePOServiceBD(){
        serviceLocator = new ServiceLocator();
    }
   
    public String submitPO(PurchaseOrder po) throws RequestHandlerException {
        try {
            // このフラグは、WSDL に定義されているように
            //ラッパー要素 BusinessDocumentRequest を生成するためのもの
            boolean wrapper = true;
            AnyTypePurchaseOrderServiceSEI port = (AnyTypePurchaseOrderServiceSEI)
            serviceLocator.getServicePort(JNDINames.ANY_TYPE_SERVICE_REF,
                    AnyTypePurchaseOrderServiceSEI.class);
            SOAPElement requestSOAPElem = po.toXMLSOAPElement(wrapper);                  
            return port.submitPO(requestSOAPElem);
        } catch(InvalidPOException ipe){
            ipe.printStackTrace(System.err);
            throw new RequestHandlerException("Request Handler Exception: Service Endpoint Application-Defined Exception "+ipe.getMessage(), ipe);
        } catch(RemoteException re){
            re.printStackTrace(System.err);
            throw new RuntimeException("The web service you are trying to access is not available. A possible reason could be that the service has not been deployed yet. "+ re.getMessage(), re);
        }
    }
}
コード例 5: 注文書 XML ドキュメントを anyType サービスに送信するためのクライアントコード

コード例 6 は、クライアントアプリケーションによって生成されるヘルパー PurchaseOrder クラスを示しています。このクライアントアプリケーションでは、Web ページフォームが送信され、PurchaseOrder にデータが書き込まれます。その後、このクラスは、サービスに送信する注文書 XML ドキュメントの表現の取得に使用されます (コード例 5)。 PurchaseOrder クラスに toXMLSOAPElement() というメソッドがあることに注目してください。このメソッドは、Java 注文書を変換します。ここで大切なことは、注文書 XML が、このサービスの WSDL に定義されている親要素の BusinessDocumentRequest 内にラップされていることです。このため、anyType を使用してサービスを定義する場合、クライアントは XML ドキュメントを変更し、WSDL に定義されている親要素内にそのドキュメントをラップする必要があります。これは、anyType を使用してサービスと交換するドキュメントを記述するサービスインタフェースを使用するクライアントには多少負担になります。

public class PurchaseOrder {    

    private String poId;
    private Calendar createDate;
    private Address shipTo;
    private Address billTo;
    private LineItem[] items;   

    public PurchaseOrder() {}   

    public PurchaseOrder(String poId, Calendar createDate,
            Address shipTo, Address billTo, LineItem[] items) {

        this.poId = poId;
        this.shipTo = shipTo;
        this.createDate = createDate;
        this.billTo = billTo;
        this.items = items; 
    }

    ...

     public SOAPElement toXMLSOAPElement(boolean wrapper) {
        SOAPElement soapElem = null;
        try {
            // DOM ツリーを作成
            DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
            docBuilderFactory.setNamespaceAware(true);
            DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
            Document doc = docBuilder.newDocument();
            Element  poElem = doc.createElement("PurchaseOrder");
            if(wrapper){
                Element  docElem = doc.createElement("BusinessDocumentRequest");
                doc.appendChild(docElem);
                docElem.appendChild(poElem);
            } else{
                doc.appendChild(poElem);
            }
            Element  elem = doc.createElement("poId");
            elem.appendChild(doc.createTextNode(poId));
            poElem.appendChild(elem);
            elem = doc.createElement("createDate");
            elem.appendChild(doc.createTextNode((new SimpleDateFormat("MM-dd-yy")).format(createDate.getTime())));
            poElem.appendChild(elem);
            elem = doc.createElement("shipTo");
            poElem.appendChild(shipTo.toDOM(doc, elem));
            elem = doc.createElement("billTo");
            poElem.appendChild(billTo.toDOM(doc, elem));
            for(int i = 0; i < items.length; ++i){
                poElem.appendChild(items[i].toDOM(doc));
            }
           
            // SOAPElement を作成
            SOAPElement parent = SOAPFactory.newInstance().createElement("dummy");
            TransformerFactory factory = TransformerFactory.newInstance();
            Transformer transformer = factory.newTransformer();
            transformer.transform(new DOMSource(doc), new DOMResult(parent));
            soapElem = (SOAPElement) parent.getChildElements().next();
        } catch(TransformerException te) {
            te.printStackTrace(System.err);
            throw new RuntimeException(te.getMessage(), te);
        } catch(ParserConfigurationException pce) {
            pce.printStackTrace(System.err);
            throw new RuntimeException(pce.getMessage(), pce);
        } catch(SOAPException se) {
            se.printStackTrace(System.err);
            throw new RuntimeException(se.getMessage(), se);
        }
        return soapElem;
}

コード例 6: 注文書 XML ドキュメントのラップ方法を示す PurchaseOrder Java クラスコード

サービスエンドポイントの配備記述子

J2EE[tm] 内の Web サービスは、WAR あるいは EAR ファイルなどの配備モジュールにパッケージングされます。このアプリケーションでは、EAR ファイルとしてパッケージングしました。Web サービスエンドポイントを移植可能な形式でパッケージングするには、Java クラスばかりでなく、いくつかの配備記述子が必要です。以下は、エンドポイント配備記述子からの抜粋です。

<enterprise-beans>
    <session>
      <ejb-name>AnyTypePurchaseOrderServiceBean</ejb-name>
      <service-endpoint>com.sun.j2ee.blueprints.anytypeposervice.AnyTypePurchaseOrderServiceSEI</service-endpoint>
  <ejb-class>com.sun.j2ee.blueprints.anytypeposervice.AnyTypePurchaseOrderServiceBean</ejb-class>
      <session-type>Stateless</session-type>
      <transaction-type>Container</transaction-type>
   </session>  
</enterprise-beans>
コード例 7: ejb-jar.xml 配備記述子からの抜粋

ejb-jar.xml 内でサービスエンドポイントインタフェースが <service-endpoint> として定義されていることに注意してください。このエンドポイントにはまた、次のような Web サービス配備記述子ファイル webservices.xml も必要です。

  <webservice-description>
    <webservice-description-name>AnyTypePurchaseOrderService</webservice-description-name>
    <wsdl-file>META-INF/wsdl/AnyTypePurchaseOrderService.wsdl</wsdl-file>
    <jaxrpc-mapping-file>META-INF/anytypepurchaseorderservice-mapping.xml</jaxrpc-mapping-file>
    <port-component>
      <description>port component description</description>
      <port-component-name>AnyTypePurchaseOrderService</port-component-name>
      <wsdl-port xmlns:PurchaseOrderns="urn:AnyTypePurchaseOrderService">PurchaseOrderns:AnyTypePurchaseOrderServiceSEIPort</wsdl-port>
      <service-endpoint-interface>
        com.sun.j2ee.blueprints.anytypeposervice.AnyTypePurchaseOrderServiceSEI
      </service-endpoint-interface>
      <service-impl-bean>
        <ejb-link>AnyTypePurchaseOrderServiceBean</ejb-link>
      </service-impl-bean>
    </port-component>
  </webservice-description>
コード例 8: webservice.xml 配備記述子からの抜粋

Web サービス配備記述子ファイルには、WSDL ファイルと JAX-RPC マッピングファイルの名前や、サービスエンドポイントインタフェース名、サービス実装クラス名などのサービスに関する情報が含まれます。

© Sun Microsystems 2005. All of the material in The Java BluePrints Solutions Catalog is copyright-protected and may not be published in other works without express written permission from Sun Microsystems.