SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

japan.internet.com翻訳記事

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

SDOのJava実装を使ってXMLデータへの変更を追跡する

  • X ポスト
  • このエントリーをはてなブックマークに追加

ダウンロード サンプルソース (12.6 KB)

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アイテムは更新されたと見なされます。これは、子アイテムのデータオブジェクトが変更されたためです。

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

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

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
japan.internet.com翻訳記事連載記事一覧

もっと読む

この記事の著者

japan.internet.com(ジャパンインターネットコム)

japan.internet.com は、1999年9月にオープンした、日本初のネットビジネス専門ニュースサイト。月間2億以上のページビューを誇る米国 Jupitermedia Corporation (Nasdaq: JUPM) のニュースサイト internet.comEarthWeb.com からの最新記事を日本語に翻訳して掲載するとともに、日本独自のネットビジネス関連記事やレポートを配信。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

Young Yang(Young Yang)

ウォール街の金融会社に勤務する上級エンタープライズアーキテクト。数学博士号を取得。IBM社のeビジネスデザイン、DB2管理、ビジネスインテリジェンス、XMLおよび関連テクノロジ、WebSphere Application Serverの認定資格、BEA社のWebLogic Application S...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/2225 2008/03/04 14:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング