japan.internet.comThe Internet & IT Network
RSS
  • ニュース
  • コラム
  • リサーチ
  • ヘッドライン
  • 特集
  • ブログ
  • プレスリリース
  • 専門チャンネル
  • イベント
  • ランキング
  • ニュースメール
2009年7月4日
文字サイズ文字サイズ小文字サイズ中文字サイズ大
デベロッパー2008年4月22日 10:00

XMLデータの変更をSDOで簡単に追跡する

海外海外internet.com発の記事
  • このエントリーを含むはてなブックマーク
  • この記事をクリップ!
  • Buzzurlにブックマーク
  • Yahoo!ブックマークに登録
  • newsing it!
  • この記事をokyuuへインポート

はじめに

 データの変更を追跡することは、多くのソフトウェア、アプリケーション、およびビジネス統合シナリオで必要とされる基本機能です。一般的な変更のデルタ(差分)をモデル化し、取り扱うことは非常に込み入った作業であり、この基本機能を厳密に実装することは比較的難しい課題です。一方で、すべてのアプリケーションにこの機能を重複して実装するのは無駄です。デルタ処理モデルは同じものをさまざまな状況に応用できますし、大半のケースで求められる機能はよく似ているからです。BEA Systems社とIBM社の主導で進められているService Data Object(SDO)は、異種データアクセスのための汎用ソリューションを定義するJSRで、開発者はこのメカニズムを使用してシステムレベルのデータ履歴追跡機能を簡単に実装できます。

 この記事では、SDOのJava実装であるApache Tuscanyバージョン1.0を使って、XMLデータをSDOで処理する方法の例を示します。SDOは(まだ)XML処理の標準ソリューションではないため、SDOの基本XMLデータ操作についても説明して、前後関係を明らかにします。

3フェーズからなるXMLデータ処理

 この記事のXMLデータ処理例では、次の3つのフェーズがそれぞれ別のグループによって担当されることを前提とします。

  1. 作成
  2. 処理
  3. 元に戻す
 XMLデータは、ファイルシステムを通じてこれらの3フェーズ(および3グループ)間でやり取りされます。この例の基本シナリオはこうです。最初のグループが作成したXMLファイルに第2のグループが変更を加え、この変更を記録します。この記録は、第3のグループがXMLデータを元に戻すときに利用されます。Microsoft Wordの変更履歴機能を使用したことがあれば、この機能の値打ちがすぐに理解できるでしょう。

 この種の機能は、多くのアプリケーションで必要とされます。たとえば、オプティミスティック同時実行制御や、オフラインアプリケーションデータと稼動中データベースとの同期、ビジネスプロセス管理(BPM)システムなどを実装するには不可欠です。以降のセクションでは、SDOを利用してこれらの機能を簡単に実装できることをサンプルコードを示して説明します。

 サンプルのXMLデータ(基はSDO 2.1.0仕様書のサンプル)は、注文処理をモデル化したものです(スキーマ「po_original.xsd」についてはリスト1を参照)。次のセクションでは、SDOを使ってこのスキーマに基づく注文処理を作成し、ファイルシステムに永続化する手順を説明します。このサンプルでは動的APIを使用します。SDOではあらゆるデータソースを扱うための静的APIもサポートされています。

リスト1 po_original.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns="http://www.example.com/PO" 
    targetNamespace="http://www.example.com/PO">
    
    <xsd:import namespace="commonj.sdo/xml" schemaLocation="sdo.xsd"/>

    <xsd:element name="purchaseOrder" type="PurchaseOrderType"/>
    <xsd:element name="comment" type="xsd:string"/>
    
    <xsd:complexType name="PurchaseOrderType">
        <xsd:sequence>
            <xsd:element name="shipTo" type="USAddress"/>
            <xsd:element name="billTo" type="USAddress"/>
            <xsd:element ref="comment" minOccurs="0"/>
            <xsd:element name="items"  type="Items"/>
        </xsd:sequence>
        <xsd:attribute name="orderDate" type="xsd:date"/>
    </xsd:complexType>

    <xsd:complexType name="StatusType">
        <xsd:sequence>
            <xsd:element name="status"   type="xsd:string"/>
            <xsd:element name="contact"   type="xsd:string"/>
            <xsd:element name="changeDate" type="xsd:date"/>
        </xsd:sequence>
    </xsd:complexType>
    
    <xsd:complexType name="USAddress">
        <xsd:sequence>
            <xsd:element name="name"   type="xsd:string"/>
            <xsd:element name="street" type="xsd:string"/>
            <xsd:element name="city"   type="xsd:string"/>
            <xsd:element name="state"  type="xsd:string"/>
            <xsd:element name="zip"    type="xsd:decimal"/>
        </xsd:sequence>
        <xsd:attribute name="country" type="xsd:NMTOKEN" fixed="US"/>
    </xsd:complexType>

    <xsd:complexType name="Items">
        <xsd:sequence>
            <xsd:element name="item" minOccurs="0" 
                         maxOccurs="unbounded">
                <xsd:complexType>
                    <xsd:sequence>

                        <xsd:element name="productName" 
                                     type="xsd:string"/>
                        <xsd:element name="price"
                                     type="xsd:decimal"/>
                        <xsd:element name="quantity">
                            <xsd:simpleType>
                                <xsd:restriction 
                                     base="xsd:positiveInteger">
                                    <xsd:maxExclusive value="100"/>
                                </xsd:restriction>

                            </xsd:simpleType>
                        </xsd:element>
                        <xsd:element ref="comment"   minOccurs="0"/>
                        <xsd:element name="shipDate" 
                                     type="xsd:date" minOccurs="0"/>
                    </xsd:sequence>

                    <xsd:attribute name="partNum" 
                                   type="SKU" use="required"/>
                </xsd:complexType>
            </xsd:element>
        </xsd:sequence>
    </xsd:complexType>
    
    <xsd:simpleType name="SKU">
        <xsd:restriction base="xsd:string">
            <xsd:pattern value="¥d{3}-[A-Z]{2}"/>
        </xsd:restriction>
    </xsd:simpleType>
</xsd:schema> 

SDOを使ってXMLを作成、永続化する

 CreatePO.javaクラス(リスト2を参照)で、このサンプルのXML作成フェーズが完結します。このクラスで特に注目する必要があるのは、コメントが付けられた7ヶ所です。

リスト2 CreatePO.java
package com.company.sdo.po;

import commonj.sdo.DataObject;
import commonj.sdo.helper.DataFactory;

public class CreatePO {
     public static void main(String[] args) throws Exception {
     //1. Define Types and Properties with XSD
     Util.definePOTypes();
     
     //2. Create the root DataObject 
     DataObject purchaseOrder = 
     DataFactory.INSTANCE.create(Constants.PO_NAMESPACE, 
                                 "PurchaseOrderType");
     
     //3. Set data type Property for the root DataObject
     purchaseOrder.setString("orderDate", "1999-10-20");         
     
     //4. Create child DataObjects
     DataObject shipTo = purchaseOrder.createDataObject("shipTo");
     
     //5. Set data type Property for the child DataObject
     shipTo.set("country", "US");
     shipTo.set("name", "Alice Smith");
     shipTo.set("street", "123 Maple Street");
     shipTo.set("city", "Mill Valley");
     shipTo.set("state", "CA");
     shipTo.setString("zip", "90952");
     
     DataObject billTo = purchaseOrder.createDataObject("billTo");
     billTo.set("country", "US");
     billTo.set("name", "Robert Smith");
     billTo.set("street", "8 Oak Avenue");
     billTo.set("city", "Mill Valley");
     billTo.set("state", "PA");
     billTo.setString("zip", "95819");
     purchaseOrder.set("comment", "Hurry, my lawn is going wild!");    
     
     DataObject items = purchaseOrder.createDataObject("items");

     //6. Create a child DataObject for the child DataObject “items”
     DataObject item1 = items.createDataObject("item");
     item1.set("partNum", "872-AA");
     item1.set("productName", "Lawnmower");
     item1.setInt("quantity", 1);
     item1.setString("price", "148.95");
     item1.set("comment", "Confirm this is electric");
        
     DataObject item2 = items.createDataObject("item");
     item2.set("partNum", "926-AA");
     item2.set("productName", "Baby Monitor");
     item2.setInt("quantity", 1);
     item2.setString("price", "39.98");
     item2.setString("shipDate", "2007-11-21"); 
    
     DataObject item3 = items.createDataObject("item");
     item3.set("partNum", "998-AA");
     item3.set("productName", "Carpet");
     item3.setInt("quantity", 1);
     item3.setString("price", "439.98");
     item3.setString("shipDate", "2007-12-01");
     
     //7. Persist the XML data to an XML file 
     //Util.storeXML(purchaseOrder,"purchaseOrder", 
     //                             Constants.PO_XML_ORIGINAL);
     /*use the following line instead of the above 
      *                   one for tracking changes*/ 
     Util.storeXML(purchaseOrder,"purchaseOrder", Constants.PO_XML);
    }
}
  1. Define Types and Properties with XSD(XSDでデータ型とプロパティを定義する)― データはXMLスキーマによってモデル化されるため、最初に必要なことは、このスキーマに基づいてSDOのデータ型とプロパティをランタイムに定義することです。この操作を行うのは、UtilクラスのdefinePOTypes()メソッドから呼び出されるXSDHelperです。
  2. public static void definePOTypes() throws Exception {
         FileInputStream fis = new FileInputStream(PO_MODEL_ORIGINAL);
         XSDHelper.INSTANCE.define(fis, null);
         fis.close();
    }
    
    (SDO仕様書の9節に、XMLスキーマエンティティからSDOのデータ型およびプロパティへの実際のマッピングが定められています。詳細については、この節を参照してください)。
  1. Create the root DataObject(ルートデータオブジェクトを作成する)― SDOの動的APIは、データオブジェクト階層(オブジェクト別にプロパティを持つ階層)またはDataGraph(データオブジェクトのグラフをメタデータごとパッケージ化したもの)によって構造化データを表します。SDOのDataFactoryインターフェイスを使って未接続のデータオブジェクトを作成できるため、ここで必要な作業は、注文処理のルートデータオブジェクトを作成することです。
  1. Set data type Property for the root DataObject(ルートデータオブジェクトのデータ型プロパティを設定する)― SDOの場合、注文はデータ型です。スキーマの定義に従うと、注文のデータ型プロパティはorderDateであり、これは日付型です。コメント3の下の行で、orderDateに日付の文字列を設定します。これは、JavaのDateオブジェクトを作成し、DataObjectのsetDate()メソッドを使用することに相当します。
  1. Create child DataObjects(子データオブジェクトを作成する)― データオブジェクトpurchaseOrderには、複数の子データオブジェクトがあります。たとえば、コメント4の下の行では、"shipTo"と名前を指定してshipTo子オブジェクトをpurchaseOrderから直接作成します。これは、DataFactoryを使って未接続のshipToデータオブジェクトを作成し、これをDataObjectのいずれかのsetDataObject()メソッドを使ってpurchaseOrderの子として設定することに相当します。
  2. DataObject shipTo
       = DataFactory.INSTANCE.create(CONSTANTS.PO_NAMESPACE, "USAddress");
    ......
    PurchaseOrder.setDataObject("shipTo", shipTo);
    
  1. Set data type Property for the child DataObject(子データオブジェクトのデータ型プロパティを設定する)― USAddressデータ型の定義に従い、shipToデータオブジェクトにはさまざまなデータ型プロパティが与えられます。コメント5の下の行で、これらのプロパティを作成します。
  1. Create a child DataObject for the child DataObject “items”(子データオブジェクト"items"の子データオブジェクトを作成する)― ここでは、XMLをモデル化した場合のSDOデータモデルの階層的な特性について説明します。ItemsはルートデータオブジェクトpurchaseOrderの子であり、これに複数のitemデータオブジェクトの子が含まれます。
  1. Persist the XML data to an XML file(XMLデータをXMLファイルに永続化する)― SDOの動的APIに用意されたXMLHelperインターフェイスを使って、XMLデータをXMLファイルに永続化できます。コメント7の下のコードで、Utilクラスを呼び出し、メソッドを次のように定義します。
  2. public static void storeXML(DataObject data, String rootElementName,
         String xmlFile) throws Exception {
         OutputStream stream = new FileOutputStream(xmlFile);
         XMLHelper.INSTANCE.save(data, PO_NAMESPACE, 
                                 rootElementName, stream);
         }
    
 永続化した注文処理用XMLファイルは、「po_original.xml」という名前です。

 XML用のSDO動的APIは非常に簡単に使用できるため、DOM APIを使って同等の操作を行うよりもずっと便利です。

 次のセクションでは、SDOの動的APIを使って注文処理に改良を加え、変更を追跡できるようにします。

XMLデータへの変更を記録する

 SDOに定義されたChangeSummaryメカニズムを利用して、変更を追跡し、変更の記録を注文情報と一緒に保管することができます。第二の目的を達するため、XMLスキーマファイル「po_original.xsd」を変更する必要があります(リスト3を参照)。インポートされるスキーマ「sdo.xsd」(SDO 2.1.0のもの)には、ChangeSummaryTypeなどが定義されています。このChangeSummaryTypeの要素をPurchaseOrderTypeに追加します。要素の名前は任意に決めてかまいませんが、ここでは"changes"とします。

リスト3 修正後のpo_original.xsd
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns="http://www.example.com/PO"
    xmlns:sdo="commonj.sdo"
    xmlns:sdoxml="commonj.sdo/xml" 
    targetNamespace="http://www.example.com/PO">
    
    <xsd:import namespace="commonj.sdo/xml" 
        schemaLocation="C:¥¥eclipse¥¥workspace¥¥SDO¥¥src¥¥main
                        ¥¥resources¥¥sdo.xsd" />

    <xsd:element name="purchaseOrder" type="PurchaseOrderType"/>
    <xsd:element name="comment" type="xsd:string"/>
    <xsd:element name="status" type="StatusType"/>
    
    <xsd:complexType name="PurchaseOrderType">
        <xsd:sequence>
            <xsd:element name="shipTo" type="USAddress"/>
            <xsd:element name="billTo" type="USAddress"/>
            <xsd:element ref="comment" minOccurs="0"/>
            <xsd:element name="items"  type="Items"/>
            <xsd:element ref="status" minOccurs="1" maxOccurs="1"/>
            <xsd:element name="changes" type="sdo:ChangeSummaryType"/>
        </xsd:sequence>
        <xsd:attribute name="orderDate" type="xsd:date"/>
    </xsd:complexType>

   ......
 </xsd:schema>
 この新規のスキーマを適用するには、Util.javaのdefinePOTypes()メソッドで読み込む必要があります。CreatePO.javaから生成されるXMLファイルは、「po.xml」という名前で永続化されます。「po_original.xml」と異なるのは、「po.xml」に<po:purchaseOrder>の新しいサブ要素<changes logging="false" />が追加されることです。注文は、ProcessPO.javaクラス(リスト4を参照)で処理されます。このプログラムを実行すると、処理された注文が変更の記録と共に「po_processed.xml」(リスト5を参照)に永続化されます。

リスト4 ProcessPO.java
package com.company.sdo.po;

import java.io.FileInputStream;
import commonj.sdo.ChangeSummary;
import commonj.sdo.DataObject;
import commonj.sdo.helper.XMLDocument;
import commonj.sdo.helper.XMLHelper;

public class ProcessPO {
     public static void main(String[] args) throws Exception {
          Util.definePOTypes();
          FileInputStream fis = new FileInputStream(Constants.PO_XML);
          //1.load the XML document
          XMLDocument xmlDoc = XMLHelper.INSTANCE.load(fis);
          
          DataObject purchaseOrder = xmlDoc.getRootObject();

          DataObject items = purchaseOrder.getDataObject("items");
          //2.get the ChangeSummary object
          ChangeSummary chngSum = purchaseOrder.getChangeSummary();
          //3.start logging changes
          chngSum.beginLogging();
          
          /*(i)send bill to Alice Smith instead of Robert Smith*/
          DataObject billTo = purchaseOrder.getDataObject("billTo");
          billTo.setString("name", "Alice Smith");
          
          /*(ii)change quantity for Baby Monitor, item 2*/          
          DataObject item2 = items.getDataObject("item.1");
          item2.setInt("quantity",2);
          
          /*
           * a potential bug in Tuscany SDO, 
           * this line reports exception 
           * when trying to print ChangeSummary in ReviewPO.java
           *      item2.set("comment", 
           *                 "Only consider electricity powered.");
           */          
          /*(iii)remove item 3 from the order*/
          DataObject item3 = (DataObject) items.getDataObject("item.2");
          item3.delete();

          /*(iv)add an item to the order for armed chair*/          
          DataObject item4 = items.createDataObject("item");
          item4.set("partNum", "999-AA");
          item4.set("productName", "Armed Chair");
          item4.setInt("quantity", 1);
          item4.setString("price", "299.95");
          item4.set("comment", "Make sure the cover is leather.");
                  //4.end logging changes 
          chngSum.endLogging();
          //5.print in system console all the changes made above
          Util.printChangeSummary(chngSum);
          Util.storeXML(purchaseOrder, "purchaseOrder", 
                        Constants.PO_PROCESSED_XML);          
     }
}
リスト5 po_processed.xml
<?xml version="1.0" encoding="ASCII"?>
<po:purchaseOrder xmlns:po="http://www.example.com/PO" 
                  orderDate="1999-10-20">
  <shipTo country="US">
    <name>Alice Smith</name>
    <street>123 Maple Street</street>
    <city>Mill Valley</city>
    <state>CA</state>
    <zip>90952</zip>
  </shipTo>
  <billTo country="US">
    <name>Alice Smith</name>
    <street>8 Oak Avenue</street>
    <city>Mill Valley</city>
    <state>PA</state>
    <zip>95819</zip>
  </billTo>
  <po:comment>Hurry, my lawn is going wild!</po:comment>
  <items>
    <item partNum="872-AA">
      <productName>Lawnmower</productName>
      <price>148.95</price>
      <quantity>1</quantity>
      <po:comment>Confirm this is electric</po:comment>
    </item>
    <item partNum="926-AA">
      <productName>Baby Monitor</productName>
      <price>39.98</price>
      <quantity>2</quantity>
      <shipDate>2007-11-21</shipDate>
    </item>
    <item partNum="999-AA">
      <productName>Armed Chair</productName>
      <price>299.95</price>
      <quantity>1</quantity>
      <po:comment>Make sure the cover is leather.</po:comment>
    </item>
  </items>
  <changes create="     ##//items/item[3]" delete="     
                        ##//changes/items[1]/item[3]" 
           logging="false" xmlns:sdo="commonj.sdo">
    <billTo sdo:ref="     ##//billTo">
      <name>Robert Smith</name>
    </billTo>
    <item sdo:ref="     ##//items/item[2]">
      <quantity>1</quantity>
    </item>
    <items sdo:ref="     ##//items">
      <item sdo:ref="     ##//items/item[1]" />
      <item sdo:ref="     ##//items/item[2]" />
      <item partNum="998-AA">
        <productName>Carpet</productName>
        <price>439.98</price>
        <quantity>1</quantity>
        <shipDate>2007-12-01</shipDate></item>
    </items>
  </changes>
</po:purchaseOrder>
 リスト5では変更がXMLファイルに永続化される過程を見ることができますが、それ以上に注目すべきは<changes>要素です。以前よりずっと複雑になっています。この要素には、このプログラムで注文に加えられた変更がすべて記録されます(<changes>要素の実際の内容は、SDO仕様書に定められています)。この要素の内容に記録された情報と変更後のデータを基に、必要であれば元のデータを復元することができます(リスト6に、コンソールに出力される情報を示します)。

リスト6 Util.javaのprintChangeSummary()からの出力
Deleted: org.apache.tuscany.sdo.impl.DynamicDataObjectImpl@19b5217
 (eClass: org.apache.tuscany.sdo.impl.ClassImpl@10a2d64 (name: item)
 (instanceClassName: null) (abstract: false, interface: false))
        --- originally contained in : Items ---
         &&&BEGNNING- deleted -BEGINNING&&& 
         &&&END- deleted -END&&& 
        --- deleted property information --- 
         $$$ name of the property deleted: productName $$$
         ### the deleted property is a data type ###
                         productName: Carpet
         $$$ name of the property deleted: price $$$
         ### the deleted property is a data type ###
                         price: 439.98
         $$$ name of the property deleted: quantity $$$
         ### the deleted property is a data type ###
                         quantity: 1
         $$$ name of the property deleted: comment $$$
                        %%%originally NOT set%%%
         $$$ name of the property deleted: shipDate $$$
         ### the deleted property is a data type ###
                         shipDate: 2007-12-01
         $$$ name of the property deleted: partNum $$$
         ### the deleted property is a data type ###
                         partNum: 998-AA
        --- deleted property information --- 

Created: org.apache.tuscany.sdo.impl.DynamicDataObjectImpl@3b1d04 
(eClass: org.apache.tuscany.sdo.impl.ClassImpl@10a2d64 (name: item) 
(instanceClassName: null) (abstract: false, interface: false))
        --- to be contained in : Items ---
         &&&BEGNNING- newly created -BEGINNING&&& 
                 productName: Armed Chair
                 price: 299.95
                 quantity: 1
                 comment: Make sure the cover is leather.
                 partNum: 999-AA
         &&&END- newly created -END&&& 

Updated:  org.apache.tuscany.sdo.impl.DynamicDataObjectImpl@176e552
 (eClass: org.apache.tuscany.sdo.impl.ClassImpl@12a0f6c
 (name: USAddress) (instanceClassName: null) (abstract: false, 
interface: false))
         &&&BEGNNING- after update -BEGINNING&&& 
                 name: Alice Smith
                 street: 8 Oak Avenue
                 city: Mill Valley
                 state: PA
                 zip: 95819
                 country: US
         &&&END- after update -END&&& 
        --- property update information --- 
         $$$ name of the property updated: name $$$
         ### the property is a data type ###
         from : Robert Smith         to : Alice Smith
        --- property update information --- 

Updated:  org.apache.tuscany.sdo.impl.DynamicDataObjectImpl@12ad19e
 (eClass: org.apache.tuscany.sdo.impl.ClassImpl@10a2d64 (name: item)
 (instanceClassName: null) (abstract: false, interface: false))
         &&&BEGNNING- after update -BEGINNING&&& 
                 productName: Baby Monitor
                 price: 39.98
                 quantity: 2
                 shipDate: 2007-11-21
                 partNum: 926-AA
         &&&END- after update -END&&& 
        --- property update information --- 
         $$$ name of the property updated: quantity $$$
         ### the property is a data type ###
         from : 1         to : 2
        --- property update information --- 

Updated:  org.apache.tuscany.sdo.impl.DynamicDataObjectImpl@281d4b 
(eClass: org.apache.tuscany.sdo.impl.ClassImpl@89cf1e (name: Items)
 (instanceClassName: null) (abstract: false, interface: false))
         &&&BEGNNING- after update -BEGINNING&&& 
                 item (item):
                         productName: Lawnmower
                         price: 148.95
                         quantity: 1
                         comment: Confirm this is electric
                         partNum: 872-AA
                 item (item):
                         productName: Baby Monitor
                         price: 39.98
                         quantity: 2
                         shipDate: 2007-11-21
                         partNum: 926-AA
                 item (item):
                         productName: Armed Chair
                         price: 299.95
                         quantity: 1
                         comment: Make sure the cover is leather.
                         partNum: 999-AA
         &&&END- after update -END&&& 
        --- property update information --- 
         $$$ name of the property updated: item $$$
         ### the property is a DataObject ###
         ### the property is multiple valued ###
         ### total number of the property is : 3 ###
         ### and here are they with status information ### 
                 &&&BEGNNING- untouched -BEGINNING&&& 
                         productName: Lawnmower
                         price: 148.95
                         quantity: 1
                         comment: Confirm this is electric
                         partNum: 872-AA
                 &&&END- untouched -END&&& 
                 &&&BEGNNING- after update -BEGINNING&&& 
                         productName: Baby Monitor
                         price: 39.98
                         quantity: 2
                         shipDate: 2007-11-21
                         partNum: 926-AA
                 &&&END- after update -END&&& 
                 &&&BEGNNING- newly created -BEGINNING&&& 
                         productName: Armed Chair
                         price: 299.95
                         quantity: 1
                         comment: Make sure the cover is leather.
                         partNum: 999-AA
                 &&&END- newly created -END&&& 
        --- property update information --- 
 では、ProcessPO.javaクラスのコードを追って、SDOを使って変更の詳細な記録を取得する手順を細かく検討してみましょう(リスト4を参照)。

  1. コメント1の下にある行で、「po.xml」をランタイムに読み込みます。
  2. コメント2の下の行で、ChangeSummaryオブジェクトを作成し、これをpurchaseOrderデータオブジェクトに関連付けます。
  3. 変更を追跡するため、コメント3の下の行で、ChangeSummaryオブジェクトのロギングをオンにします。
  4. ここからコメント4の下の行(ロギングをオフにする場所)までの間で、purchaseOrderとその子データオブジェクトへのすべての変更をChangeSummaryオブジェクトchngSumに記録する処理を行います。
  5. 変更情報の出力は、コメント5の下の行にあるUtil.javaのprintChangeSummary()メソッドを呼び出して行います(リスト7を参照)。
リスト7 Util.javaのprintChangeSummary()メソッド
public static void printChangeSummary(ChangeSummary chngSum) {
    if (chngSum == null) {
        System.out.println("ChangeSummary is not in existence!");
        return;
    }
        
    for (Iterator it = chngSum.getChangedDataObjects().iterator(); 
         it.hasNext();) {
        DataObject changedObject = (DataObject) it.next();
        System.out.println();

        if (chngSum.isCreated(changedObject)) {
                                //is the changed object newly created
            System.out.println("Created: " + changedObject);
            if (changedObject.getContainer()!=null){
                System.out.println("¥t--- to be contained in : "
                    + changedObject.getContainer().getType().getName()
                    + " ---");
            }else{
                System.out.println(
                  "¥t--- created object has no container --- ");
            }
            printAnnotatedDataObject("newly created",changedObject, 2);
        } else if (chngSum.isDeleted(changedObject)) {
            System.out.println("Deleted: " + changedObject);         

            if (chngSum.getOldContainer(changedObject) != null){
                System.out.println(
                  "¥t--- originally contained in : "
                  + chngSum.getOldContainer(
                      changedObject).getType().getName() + " ---");
            }else{
                System.out.println(
                  "¥t--- deleted object has no container ---");
            }
            // a potential bug in Tuscany SDO, this shows nothing 
            // in ProcessPO.java
            printAnnotatedDataObject("deleted",changedObject, 2);
            
            // drill down to deleted property
            System.out.println(
               "¥t--- deleted property information --- ");
            // a potential bug in Tuscany SDO, 
            // this section shows nothing in ReviewPO.java
            for (Iterator settingIt
                  = chngSum.getOldValues(changedObject).iterator(); 
                  settingIt.hasNext();) {
                printDeletedProperty((ChangeSummary.Setting) 
                  settingIt.next());
            }
            System.out.println(
              "¥t--- deleted property information --- ");
        } else if (chngSum.isModified(changedObject)) {
            System.out.println("Updated:  " + changedObject);
                
            // print out the updated object
            printAnnotatedDataObject("after update", changedObject, 2);
            
            // drill down to changed property
            System.out.println(
               "¥t--- property update information --- ");
             
            for (Iterator settingIt
                   = chngSum.getOldValues(changedObject).iterator();
                 settingIt.hasNext();) {
                ChangeSummary.Setting changeSetting
                 = (ChangeSummary.Setting) settingIt.next();
                printUpdatedProperty(changeSetting, 
                                     changedObject,chngSum);
            }
            System.out.println(
             "¥t--- property update information --- ");
        } else
            System.out.println("Should never come here!");
    }
}
 この方式では、最初にgetChangedDataObjects()を使ってすべての変更されたデータオブジェクトを取得してから、カテゴリ(作成、削除、または変更)に基づいてこれらを処理します。新規に作成されたデータオブジェクトについては、オブジェクトとすべての関連プロパティに関する情報を出力します(SDO仕様書に添付されたサンプルであるprintDataObject()の注釈付きバージョンを呼び出します)。また、このデータオブジェクトのコンテナがあれば、それも表示します。

 削除されたデータオブジェクトについては、そのデータオブジェクトのコンテナがあればまずそれを特定し、printDataObject()を使って出力しようとします。この場合、出力を見てわかるのはApache Tuscany実装からなにも生成されないことです。ただし、削除されたデータオブジェクトを指定してChangeSummaryのgetOldValues()メソッドを呼び出すと、そのオブジェクトのすべてのプロパティと値を取得できます。変更されたデータオブジェクトのプロパティに関するこれらの情報は、内部クラスChangeSummary.Settingに保存されます。このクラスは、プロパティに値が設定されていたかどうかと、設定されていた場合にその古い値が取得できればその値を取得します。

 変更されたデータオブジェクトについては、printDataObject()を呼び出してすべてのプロパティと現在値を確認できます。ChangeSummaryのgetOldValues()メソッドを使うと、すべてのプロパティに対応するChangeSummary.Settingオブジェクトを取得できます。このプロパティ確認作業は、Util.javaのプライベートメソッドprintUpdatedProperty()で行います(リスト8を参照)。

リスト8 util.javaのprintUpdatedProperty()メソッド
private static void printUpdatedProperty(
        ChangeSummary.Setting changeSetting, 
        DataObject changedObject,
        ChangeSummary chngSum) {
    if (changeSetting == null)return;
    Property property = changeSetting.getProperty();
    System.out.println("¥t $$$ name of the property updated: "
                       + property.getName() + " $$$");
    if (!changeSetting.isSet()){
        System.out.println(
          "¥t ### the updated property is originally NOT set ###");
    }
    if (!property.getType().isDataType()) {
                                        // the property is DataObject
        System.out.println("¥t ### the property is a DataObject ###");
        if (property.isMany()) { // multiple valued
            System.out.println(
              "¥t ### the property is multiple valued ###");
            List objects = (List) changedObject.get(property);
            System.out.println(
              "¥t ### total number of the property is : "
              + objects.size() + " ###");
            System.out.println(
              "¥t ### and here are they with status information ### ");
            for (Iterator objIt = objects.iterator(); 
                 objIt.hasNext();) {
                DataObject itObj = (DataObject) objIt.next();
                if (chngSum.isCreated(itObj)) {
                    printAnnotatedDataObject("newly created", itObj, 3);
                } else if (chngSum.isModified(itObj)) {
                    printAnnotatedDataObject("after update", itObj, 3);
                } else {
                    printAnnotatedDataObject("untouched", itObj, 3);
                }
                    
            }
        } else { // single valued
            System.out.println(
              "¥t ### the property is single valued ###");
            printAnnotatedDataObject("after update",
                 (DataObject) changedObject.get(property), 2);
        }
    } else { // the property is a data type
        System.out.println("¥t ### the property is a data type ###");
        System.out.println("¥t from : "
                            + changeSetting.getValue() 
                            + "¥t to : " 
                            + changedObject.get(property));
    }
}
 printUpdatedProperty()メソッドの主要部分では、最初にプロパティがデータ型であるか、データオブジェクトであるかを判断します。データオブジェクトの場合は、複数値(アイテムなど)のプロパティであるか、単一値(billToなど)のプロパティであるかをチェックします。複数値プロパティの場合は、値が作成済み、変更済み、または未操作のいずれであるかをチェックし、最後にprintDataObjectを呼び出します。DataObject型の単一値プロパティの場合は、printDataObject()を直接呼び出します。プロパティがデータ型である場合は、printUpdatedProperty()メソッドからSetting.getValue()が呼び出されて古い値が取得され、DataObjectのget(Property prop)が呼び出されて現在の値が取得されます。

 Util.javaのプライベートメソッドprintDeletedProperty()の場合も、基本的なロジックはこれと同じです。違いは、削除されたデータオブジェクトのプロパティには現在の値が存在しないことです。

 システムコンソールの出力においては、プログラムによって直接変更されていない場合でもこのDataObjectアイテムは更新されたと見なされます。これは、子アイテムのデータオブジェクトが変更されたためです。

 次のセクションでは、変更された注文を元に戻す方法を説明します。

別のグループによる変更を元に戻す

 XMLデータ処理の3番目のフェーズでは、次の2つのタスクを実行できる必要があります。

  1. 変更された注文に関する要約情報を出力する。
  2. 処理フェーズで行われたすべての変更の情報を表示する。
 前の変更記録フェーズのように注文をさらに変更することもできますが、このセクションでは、ReviewPO.javaを使ってすべての変更を元に戻す方法を説明します(リスト9を参照)。コメント2「Shows the information in the modified purchase order」(変更された注文の情報を表示する)の下の行では、上記の最初のタスクを実行します。このコードの意味については、もう説明の必要はないでしょう。コメント3「Display changes made in the second phase」(2番目のフェーズで行われた変更を表示する)の下にあるprintChangeSummary()呼び出しによって、ProcessPO.javaで行われた変更がシステムコンソールに再度出力されます。

リスト9 ReviewPO.java
package com.company.sdo.po;

import java.io.FileInputStream;
import java.util.List;

import commonj.sdo.ChangeSummary;
import commonj.sdo.DataObject;
import commonj.sdo.helper.XMLDocument;
import commonj.sdo.helper.XMLHelper;

public class ReviewPO {

     public static void main(String[] args) throws Exception {
          
         Util.definePOTypes();
          FileInputStream fis
            = new FileInputStream(Constants.PO_PROCESSED_XML);
          //1. load the processed purchase order
          XMLDocument xmlDoc = XMLHelper.INSTANCE.load(fis);
          
          DataObject purchaseOrder = xmlDoc.getRootObject();
          //2. Shows the information in the modified purchase order
          System.out.println();          
          System.out.println(
             "---received purchase order information---");
          System.out.println("Order date: " 
                             + purchaseOrder.get("orderDate"));
          System.out.println("Order comment: "
                              + purchaseOrder.get("comment"));
          
          DataObject shipTo = purchaseOrder.getDataObject("shipTo");
          System.out.println("Ship to name: ");
          Util.printDataObject(shipTo,1);

          DataObject billTo = purchaseOrder.getDataObject("billTo");
          System.out.println("Bill to name: ");
          Util.printDataObject(billTo,1);          

          DataObject items = purchaseOrder.getDataObject("items");     
          List itemList = items.getList("item");
          System.out.println("Ordered items: ");
          for (int i = 0; i < itemList.size(); i++) {               
               DataObject item = (DataObject) itemList.get(i);
               System.out.println("¥tItem " + (i+1) + " :" );
               Util.printDataObject(item,2);
          }
          
          //3. Display changes made in the second phase 
          System.out.println();
          System.out.println("---review changes in purchase order ---");
          ChangeSummary chngSum = purchaseOrder.getChangeSummary();
          Util.printChangeSummary(chngSum);
     
          //4. Undo all changes in the second phase
          System.out.println();
          System.out.println(
             "---action taken after reviewing the changes---");      
          /*
           * should really work on changes here, 
           * ChangeSummary only has a undoChanges() method;
           * ideally should have methods such as:
           * (1) restore() for deleted data object,
           * (2) delete() for newly added object, and
           * (3) revoke() for modified object
           * and those methods should apply directly 
           * on the data objects or properties
           */          
          System.out.println("¥t###undo all changes###");
          chngSum.undoChanges();          
          
          //5. Modify the purchase order on the original version 
          System.out.println();
          System.out.println(
            "---changes made in purchase order review---");
          chngSum.beginLogging(); 
                        //this clears old change summary information
          billTo.set("name", "Alice Smith");
          chngSum.endLogging();
          
          //6. print to the system console the new modifications
          Util.printChangeSummary(chngSum);          
          
          //7. persist the reviewed version to an XML file 
          Util.storeXML(purchaseOrder,"purchaseOrder", 
                        Constants.PO_REVIEWED_XML);
     }
}
 この2番目のタスクは、前のセクションのChangeSummaryの出力処理とは違います。前のセクションでは、JavaオブジェクトとしてのChangeSummaryに直接基づいて出力を実行しました。今回は、変更の要約がXMLファイルからランタイムに再度読み込まれます。この違いは、出力を比較すると明らかになります。前のセクションの出力では、削除アイテムに関してprintDataObject()からはなにも生成されませんが、printDeletedProperty()を呼び出すと、削除アイテムのすべてのプロパティと値が取得されます。今回の出力処理では、この正反対の結果になります。この不一致は、Apache Tuscany実装のバグと思われます(第二のバグらしきものがリスト2のCreatePO.javaのコメントに見られます)。ここからも、厳密な変更追跡メカニズムを実装することがいかに難しいかがわかります。

 プログラムの残りの部分を見ていきましょう。コメント4「Undo all changes in the second phase」(2番目のフェーズで行った変更をすべて元に戻す)以下ではChangeSummaryのundoChanges()メソッドを呼び出し、コメント5「Modify the purchase order on the original version」(元のバージョンの注文を変更する)以下では元に戻したばかりの注文に簡単な変更を加えています。永続化されたXML「po_reviewed.xml」を参照して、以上の操作の結果を確認してください。

SDOの明るい未来

 今後、SDOはEclipse、BEA AquaLogic Data Services Platform(ALDSP)、IBM WebSphere Process Serverなどの主要なオープンソースソフトウェアや商用ソフトウェアだけでなく、Java、C++、PHPなどのさまざまな言語にも実装される予定です。

 2007年、SDO界に2つの大きな前進が見られました。

  1. 3月、Open SOAはSDO仕様をOASISに提出すると発表しました。
  2. 4月、SDOをJava EEの将来のバージョンでサポートすることを検討するとJCPが公式に発表しました。
 Apache TuscanyからもSDO Java実装が最近リリースされました。以上を総合すると、SDO採用の動きはますます盛んであると言えるでしょう。

 この記事は、SDOの一機能を紹介したに過ぎません。他にも、パワーあふれる便利な機能がたくさんあります。SDOテクノロジを丹念に調べれば、サービスドメインにおけるデータ管理に広く求められる機能を簡単に実現する方法が他にも見つかることでしょう。

謝辞
 この記事の初稿とサンプルソースコードを校閲し、貴重な意見を提供していただいたLaxma Reddy氏に感謝いたします。

著者紹介

Young Yang(Young Yang)
ウォール街の金融会社に勤務する上級エンタープライズアーキテクト。数学博士号を取得。IBM社のeビジネスデザイン、DB2管理、ビジネスインテリジェンス、XMLおよび関連テクノロジ、WebSphere Application Serverの認定資格、BEA社のWebLogic Application Serverの認定資格、Sun社のエンタープライズアーキテクトその他の認定資格を保有。
このエントリーを含むはてなブックマーク この記事をクリップ!
BuzzurlにブックマークBuzzurlにブックマーク Yahoo!ブックマークに登録
この記事をokyuuへインポート
最新トップニュース
データメーション
【データメーション】
中国が「Green Dam」フィルタ規制を撤回(7月1日)
Graphic Design Forum
【Graphic Design Forum】
Chris Dickman(6月25日)
プライバシー ジャパン・インターネットコム版
【プライバシー ジャパン・インターネットコム版】
グーグル・ストリートビューの問題について総務省の見解(6月23日)
エンジニアの独り言
【エンジニアの独り言】
システムを「使う」時代のエンジニアに求められるもの(6月2日)
最新ハイテク講座
最新ハイテク講座
電気は家庭でつくる時代へ!燃料電池「エネファーム」(7月3日)
アクセス解析で見るWebマーケティング
アクセス解析で見るWebマーケティング
決定力を探るアクセス解析(7月3日)
百式のネットビジネス研究
百式のネットビジネス研究
ファーストフードを高級っぽく盛り付けて紹介している「Fancy Fast Food」(7月3日)
週刊-サイト別アクセス状況データ
週刊-サイト別アクセス状況データ
ビデオリサーチインタラクティブ調査(月間インターネットオーディエンスデータ)(7月2日)
成約率、反応率を上げる Web 文章術
成約率、反応率を上げる Web 文章術
言葉がダイレクトにキャッシュを生む(7月2日)
不況時代の Web ビジネス最適化講座
不況時代の Web ビジネス最適化講座
アクセス解析エキスパートここだけの話、Web コンシェルジュの“勉強法”こっそり教えます(7月2日)
「Webからの脅威」―その傾向と最新対策
「Webからの脅威」―その傾向と最新対策
不正プログラムの分類(7月1日)
DevX
DevX
JavaScriptとDOMによる動的なWebページの作成(6月30日)
エンジニア転職ノウハウ開発室
エンジニア転職ノウハウ開発室
今のままで大丈夫?3匹の子ブタ的キャリア危険度診断(6月30日)
アイレップの SEM フロンティア
アイレップの SEM フロンティア
Web サイトは「無駄な穴のたくさん開いたじょうご」〜サイト成果向上の基本的な考え方(6月30日)
Copyright 2009 Japan Internet.com K.K. All Rights Reserved.http://www.internet.com/