並行プログラミング言語(2)、Jini、Ajax

並行システム

                               システム情報工学研究科コンピュータサイエンス専攻、電子・情報工学系
                               新城 靖
                               <yas@is.tsukuba.ac.jp>

このページは、次の URL にあります。
http://www.cs.tsukuba.ac.jp/~yas/sie/csys-2007/2008-02-20
あるいは、次のページから手繰っていくこともできます。
http://www.cs.tsukuba.ac.jp/~yas/sie/
http://www.cs.tsukuba.ac.jp/~yas/

■今日の重要な話

参考文献

Marko Boger: Java in Distributed Systems: Concurrency, Distribution and Persistence, John Wiley & Sons, 2001. ISBN: 0471498386

http://www.dpunkt.de/buch/3-932588-32-0.html (ドイツ語) jivs code

Nicholas C. Zakas, Jeremy Mcpeak, Joe Fawcett "Professional Ajax", Wrox Pr Inc (2006). http://www.wrox.com/ (第2版もある。)

■Dejay

◆Dejeyのオブジェクト

public class Hello implements java.io.Serializable  {
    public void sayHello(java.lang.String name) {
        System.out.println("Hello " + name);
    }
}
djeyc コンパイラで、2つの Java のクラスが作られる。

◆Dejeyの起動コード

	
import dejay.base.*;
public class HelloStartup {
    public static void main(String args[])
    {
        try {
	    DjProcessor p1 = new DjProcessor("//localhost:8000");
	    DjHello hello1 = new DjHello(p1);
	    hello1.sayHello("Thorsten");
	    p1.moveTo("//localhost:9000");
	    hello1.sayHello("Jan");
	    hello1.moveTo("//mac:9000");
        } catch (ConstructProcessorFailedException e) {
            System.out.println("Creating Virtual Processor failed."+e);
        }
    }
}

◆DjProcessor

1つ目の仮想プロセッサは、暗黙的に作られる。 2つ目以降は、明示的に作る。
    DjProcessor p1 = new DjProcessor("hostname:port");
これにより、ホスト hostname 上の ポート番号 port で新たな 仮想プロセッサが作られる。 ホスト hostname では、事前に Voyager デーモンを走らせておく必要がある。

◆遠隔オブジェクト

class A (.dj) から、自動的に スタブ(プロキシ)となる class DjA が生成される。 コンストラクタの引数が1個多い。
    DjA a1 = new DjA( Aの引数・・・, DjProcessor );
これで、遠隔の仮想プロセッサでオブジェクトが生成され、 遠隔参照(remote reference)が返される。 通常の参照と同じになるように頑張っているが、 一部違う。

    DjA a2 ;
    a2 = ... ;
    ...
    if( a2 == a1 ) // 遠隔のオブジェクトではなくてローカルのスタブの比較
    {
       ...
    }
equals() メソッドは、遠隔でも働く。

ローカル・オブジェクトを引数にして、 リモート・オブジェクトを呼ぶと、自動的に遠隔にコピーが渡される。 (遠隔オブジェクトが作られて、遠隔参照が渡されるのではない。)

DjX 型のオブジェクトを 遠隔にリターンすることはできる。 DjX がみつからなければ、 Xのコピーが返される。

remote.getCopy() で、ローカルのコピーが得られる。

    DjA a1 = new DjA(p1);
    A a2 = a1.getCopy();

◆メソッド呼出しのセマンティクス

3つのメソッド呼出しのセマンティクスがある。
    DjA a1 = new DjA(p1);
    B b1 = a.method1( 10 );		 // 同期
    B b2 = a.method1( 10, Dejey.ASYNC ); // 非同期
    a.method1( 10, Dejay.ONEWAY );       // 一方向
非同期には、完了待ちの方法が2種類ある
    B b1 = a.method1( 10, Dejey.ASYNC );
    b1.method2(); // wait by nesessity

    B b2 = a.method1( 10, Dejey.ASYNC );
    if( b2.isAvailable() )
    {
        b2.method2();
    }
    else
    {
        // do something
        b2.waitForResult();
        b2.method2();
    }

◆Dejeyの名前サービス

オブジェクトに文字列の名前を付けて、遠隔からアクセスできるように公開す ることができる。

2レベルの名前。

    DjProcessor p1 = new DjProcessor("lin:8000");
    p1.registerByName("p1");
    DjA a1 = new DjA(p1);
    a1.registerByName("a1");
    DjProcessor p1 = Dejay.getProcessorByName("p1");
    DjA a1 = p1.getObjectByName("a1");

◆永続性

分散と永続は、かなり関係している。 Dejay では、仮想プロセッサ単位で永続にできる。 DjProcessor のコンストラクタで、文字列の名前と データベースの名前を取る。
    DjProcessor p1 = new DjProcessor("PersistProc","ProcesssorDB","lin:8000");
    DjA a1 = new DjA();
    if( p1.isPersistable ) {
       p1.persist();
       p1.flush();
    }
persist() を呼ぶと、データベースに保存される。 flush() を呼ぶと、メモリが GC で回収可能になる。

persist() でデータベースに保存されたオブジェクトも、 使われる自動的にデータベースから回復される。

仮想プロセッサ全体がデータベースに保存できる。 次のようにして回復できる。

    DjProcessor p1 = Dejay.getProcessorFromDB("PersistProc",
        "ProcesssorDB","lin:8000");

◆制限

■Jini

Jini (ジニー)は、Java を中核にした、情報家電製品を相互接続するための通 信技術。

1999年に、サン・マイクロシステムズ社のビル・ジョイらによって開発され発 表された。

Jini の目標は、ネットワークで Plug & Play を実現すること。 機器をネットワークに接続しただけで、特別な設定 をしなくてもすぐに利用可能になること。

目標

サービスには、ハード的なもの(プリンタ、ディジタルカメラ、CD Player) の他に、ソフト的なもの(スペルチェック、翻訳、アドレス帳)がふくまれる。

JavaSpaces は、内部的に Jini のルックアップ・サービスの実現で使われて いる。JavaSpaces も、Jini でアクセス可能なサービスの1つと考えることも できる。

◆UPnP (Universal Plugand Play)

Microsoft の、Jini に対抗した技術。

◆ルックアップ・サービス

Jini中心的な技術が、ルックアップ・サービス。

サービスの登録と検索

ルックアップ・サービス自身も、最初から知られている必要はない。 ネットワーク上で自動的に探される。 Discovery と Join。

◆リース

サービスは、永続的に登録されるではなくて、 特定の時間だけ利用可能になる。

Java のオブジェクトが Lease。

リース期間は、延長することができる。 延長されなかった lease は、ルックアップ・サービスから削除される。 登録されているサービスを明示的に削除する仕組みは、存在しない。

サービスが削除された時には、分散型イベント配送サービスにより関係してい る所に知らされる。

トランザクションのインタフェースは定められているが、具体的な実現は各サー ビスにまかされている。

◆Jiniを使うのに必要とされているもの

IPアドレスの割り当ては、Jiniの一部ではない。

◆サービス

サービスは、Java のオブジェクトで実現される。

サービスの提供者とサービスの利用者の間は、最終的にはに RMI で接続される。

ServiceIDLister インタフェースを implements する。

package com.sun.jini.lookup;
public interface ServiceIDListener extends java.util.EventListener {
    void serviceIDNotify(net.jini.core.lookup.ServiceID serviceID);
}
ルックアップ・サービスからコールバックされる。

◆Discovery と Join

各ネットワークには、ルックアップ・サーバを置く。

Discovery
Jini を使うには、まず、ルックアップ・サーバを探す。
Join
サービスの提供者が、インタフェースと属性をルックアップ・サーバに 登録する。

Discovery の実現方法

ルックアップ・サーバは、起動時、再起動時、224.0.1.84:4160 にMulticast Announcement Protocol で、利用可能性を広告する。

サービスの提供者がネットワーク上のルックアップサービスをマルチキャストで探す。

図1 マルチキャストによる Discovery

サービスの提供者がルックアップサービスにサービスオブジェクトを登録する。

図2 Join

	
import java.rmi.*;

public interface RemoteBall extends Remote {
   public void hit() throws java.rmi.RemoteException;
}
	
import java.rmi.*;
import java.rmi.server.*;
import net.jini.core.lookup.*;
import com.sun.jini.lookup.*;

public class Ball extends UnicastRemoteObject 
                  implements RemoteBall, ServiceIDListener {
   public Ball() throws RemoteException {
      super();
   }
   public void serviceIDNotify(ServiceID id) {
      System.out.println("ServiceId is "+id);
   }
   public void hit() {
      System.out.println("Ball has been hit");
   }
}

◆サービスのグループ

サービスは、1つ以上のグループに属する。

グループは、名前(文字列)で区別される。

名前としては、DNS 風の表記のものが推奨されている。

◆JoinManager(サーバ側)

Discovery と Join は、規格上は、ネットワーク・プロトコルになっている。

Jini パッケージは、JoinManager という参照クラスを含む。

    public JoinManager(Object obj,  Entry[] attrSets,
                       ServiceIDListener callback,
                       LeaseRenewalManager leaseMgr)
        throws IOException
    public JoinManager(Object obj, Entry[] attrSets, String[] groups,
                       LookupLocator[] locators,
                       ServiceIDListener callback,
                       LeaseRenewalManager leaseMgr )
        throws IOException

import java.rmi.*;
import net.jini.core.entry.*;
import net.jini.lookup.entry.*;
import com.sun.jini.lookup.*;
import com.sun.jini.lease.*;

public class BallStarter {
   public static void main(String[] args) {
      try {
         System.setSecurityManager(new RMISecurityManager());
         RemoteBall ball = (RemoteBall) new Ball();
         LeaseRenewalManager renewal = new LeaseRenewalManager();
         Entry[] attributes = new Entry [] { new Name("Jini enabled ball")};
         JoinManager join = new JoinManager( ball, attributes, (Ball) ball, renewal );
         System.out.println("Ball started and registered at Lookup-Server");

      } catch (Exception e) {
         e.printStackTrace();
      }
   }
}

◆Lookup(クライアント側)

クライアントは、サービスを探す。

サービスの利用者がルックアップサービスに対してサービスオブジェクトを検索する。

図3 Lookup

サービスは、次の3つで区別される。 サービスを検索する時に ServiceTemplate のオブジェクトを作成する。 JavaSpaces のように、null を指定すれば、ワイルドカードを意味する。
    public ServiceTemplate(ServiceID serviceID,
                           Class[] serviceTypes,
                           Entry[] attrSetTemplates)
サービス(オブジェクト)の探し方
  1. LookupLocator により、マルチキャスト、または、 ユニキャストで ルックアップ・サービスを探す。
  2. ルックアップ・サービスのプロキシ ServiceRegistrar を作る。
  3. テンプレートを作り、ServiceRegistrar に渡す。
サービスを見つける過程で、サービス(オブジェクト)のRMI のスタブ(クライ アント側スタブ)が転送される。

◆サービスの利用

最終的にクライアントは、サービス・プロバイダを RMI で呼び出す。

import java.rmi.*;
import net.jini.core.discovery.*;
import net.jini.core.lookup.*;

public class Bat {

   public Ball ball;

   public void play(RemoteBall ball) {
      try {
         ball.hit();
         System.out.println("I hit the ball");
      } catch (RemoteException e) {
         System.out.println(e);
      }
   }

   public static void main (String[] args) {
      Bat bat = new Bat();
      try {
         System.setSecurityManager(new RMISecurityManager());
         LookupLocator locator = new LookupLocator("jini://localhost");
         ServiceRegistrar registrar = locator.getRegistrar();
         Class[] classes = new Class[] { RemoteBall.class  };
         ServiceTemplate template = new ServiceTemplate( null, classes, null);
         RemoteBall remoteBall = (RemoteBall) registrar.lookup(template);
         bat.play(remoteBall);
      } catch (Exception e) {
         e.printStackTrace();
      }
   }
}

サービスの利用者がサービスオブジェクトを通じてサーバを利用する。

図4 Invoke

◆Leasing

信頼性が低いネットワークと、どう戦う方法の1つ。

lease には、期限がある。

期限の長さは、交渉可能。 期限が短い(1分以下、数秒)というのは、あまり想定されていない。

リースの利点

Jini のサービスを提供しているオブジェクトは、Lease インタフェースを実 装している。

public interface Lease {
    long FOREVER = Long.MAX_VALUE;
...
    long getExpiration();
    void renew(long duration)
        throws LeaseDeniedException, UnknownLeaseException, RemoteException;
    void cancel() throws UnknownLeaseException, RemoteException;
...
}
getExpiration() で、リースの残り時間がわかる。ミリ秒単位。

renew() で延長する。 延長できない時には、LeaseDeniedException が返される。

もう使わなくなった時には、cancel() できる。 期限切れと同じことになる。

リースの管理には、LeaseRenewalManager を使う。

■Ajax

元々の意味は、Asynchronous JavaScript + XML。 JavaScript 言語により記述されたアプリケーション。 JavaScript 自身は、逐次プログラム。 スレッドの機能がない。 サーバ側のプログラムとの連携を考えると、 並行プログラム。 ただし、非同期通信を行う。

◆普通のJavaScriptのプログラム

◆フレーム

1つのWebページを、複数の文書で作る。
<HTML>
    <HEAD>
	<TITLE>フレームの例</TITLE>
    </HEAD>
    <FRAMESET cols="200,*" border=1>
	<FRAME name="side" src="side.html">
	<FRAME name="body" src="main.html">
	<NOFRAMES>
	    <P><A HREF="main.html">フレームなし</A></P>
	</NOFRAMES>
    </FRAMESET>
</HTML>

◆hidden frame

width か hight を 0 にしたフレーム。 ここに、JavaScript のプログラムを置ける。 表示されている HTML とは非同期的にプログラムが動作する。

◆Dynamic HTML と DOM

(JavaScript で) HTML の内容を書き換える。この時、DOM (Document Object Model) に基づき、木構造の枝を追加したり置き換えたりする。

◆iframe

<iframe> を使うと、 <frameset>が不要になる。




◆Microsoft XMLHttpとMozilla XMLHttpRequest

Microsoft XMLHttp は、 単に HTTP でデータを取って来るだけ。 内容は、HTML か XML か JavaScript か不明。 Active X も使える。

Mozilla XMLHttpRequest は、オブジェクト指向的にした。

◆モデル

WWWブラウザ、サーバ、HTTPの要求

図? 伝統的なWebブラウザ

WWWブラウザ、サーバ、HTTPの要求、Ajaxエンジン

図? AjaxのWebブラウザ

◆hidden frameを使う方法

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>hidden Frame Example 1</title>
</head>
<frameset rows="100%,0" frameborder="0">
    <frame name="displayFrame" src="display.htm" noresize="noresize" />
    <frame name="hiddenFrame" src="about:blank" noresize="noresize" />
</frameset>
</html>

◆display.htm

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">

<html>
<head>
    <title>Customer Account Information</title>
    <script type="text/javascript">
        function requestCustomerInfo() {
            var sId = document.getElementById("txtCustomerId").value;
            top.frames["hiddenFrame"].location = "GetCustomerData.php?id=" + sId;
        }

        function displayCustomerInfo(sText) {
            var divCustomerInfo = document.getElementById("divCustomerInfo");
            divCustomerInfo.innerHTML = sText;
        }
    </script>
</head>
<body>
    <p>Enter customer ID number to retrieve information:</p>
    <p>Customer ID: <input type="text" id="txtCustomerId" value="" /></p>
    <p><input type="button" value="Get Customer Info" 
    onclick="requestCustomerInfo()" /></p>
    <div id="divCustomerInfo"></div>
</body>
</html>

◆GetCustomerData.php

GetCustomerData.php は、次のような HTML を返す。 (XML ではない。)
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
    <title>Get Customer Data</title>
    <script type="text/javascript">
        window.onload = function () {
            var divInfoToReturn = document.getElementById("divInfoToReturn");
            top.frames["displayFrame"].displayCustomerInfo(divInfoToReturn.innerHTML);        
        };
</head>
<body>
    <div id="divInfoToReturn">データ</div>
</body>
</html>

◆iframeを使う例

frameset はなし。本体に <iframe> を書く。
...
<body>
    <p>Enter customer ID number to retrieve information:</p>
    <p>Customer ID: <input type="text" id="txtCustomerId" value="" /></p>
    <p><input type="button" value="Get Customer Info" onclick="requestCustomerInfo()" /></p>
    <div id="divCustomerInfo"></div>
    <iframe src="about:blank" name="hiddenFrame" width="0" height="0" frameborder="0"></iframe>
</body>
</html>
GetCustomerData.php の window.onload を次のように変える。parent で外の フレームがアクセスできる。
...
    <script type="text/javascript">
        window.onload = function () {
            var divInfoToReturn = document.getElementById("divInfoToReturn");
            parent.displayCustomerInfo(divInfoToReturn.innerHTML);        
        };
    </script>
...
その他に、Dynamic HTML の機能を使って、iframe のノードを追加する方法も ある。

◆post要求

HTTP の GET ではなく、POST を使う時には、 <form method="post"></form> という ノードを用意し、内容を埋めて、submit する。

◆XMLHttpオブジェクト

◆zXmlHttp.createReques()の利用

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
    <title>XMLHttp Example 1</title>
    <script type="text/javascript"src="zxml.js"></script>

    <script type="text/javascript">
        function requestCustomerInfo() {
            var sId = document.getElementById("txtCustomerId").value;
            var oXmlHttp = zXmlHttp.createRequest();
            oXmlHttp.open("get", "GetCustomerData.php?id=" + sId, true);
            oXmlHttp.onreadystatechange = function () {
                if (oXmlHttp.readyState == 4) {
                    if (oXmlHttp.status == 200) {
                        displayCustomerInfo(oXmlHttp.responseText);
                    } else {
                        displayCustomerInfo("An error occurred: " + oXmlHttp.statusText);
			// statusText is not always accurate
                    }
                }            
            };
            oXmlHttp.send(null);
        }
        function displayCustomerInfo(sText) {
            var divCustomerInfo = document.getElementById("divCustomerInfo");
            divCustomerInfo.innerHTML = sText;
        }
    </script>
</head>
<body>
    <p>Enter customer ID number to retrieve information:</p>
    <p>Customer ID: <input type="text" id="txtCustomerId" value="" /></p>
    <p><input type="button" value="Get Customer Info" onclick="requestCustomerInfo()" /></p>
    <div id="divCustomerInfo"></div>
</body>
</html>
oXmlHttp.onreadystatechange に設定した関数は、readyState の状態が変化 した時に呼ばれる。
readyState 説明
0 (Uninitialised) オブジェクトが作られたが、まだ open() が呼ばれていない。
1 (Loading) open() は、呼ばれたが、まだ要求は送られていない。
2 (Loaded) 要求は送られた。
3 (Interactive) 応答の一部を受け取った。
4 (Completed) 全データを受信した。コネクションも切断した。
displayCustomerInfo() は、前述のものと同じ。

◆XML Webサービスの利用

大きく2種類の流派がある。

◆MS Behaviour

Behaviour の機能を使うと、JavaScript のオブジェクトの振る舞いを変更で きる。 拡張子 .hct ファイルに入れられる。XML Web サービスのための behaviour は、マイクロソフトかwebservice.htc として配布している。
<html>
  <head>
    <title>Web Service Test Harness</title>
    <script type="text/javascript" src="WebServiceExampleBehavior.js"></script>
    <script type="text/javascript">

      var SERVICE_URL = "http://localhost/Math/Math.asmx"; 
      var SOAP_ACTION_BASE = "http://www.wrox.com/services/math";

      function setUIEnabled(bEnabled)
      {
        var oButton = document.getElementById("cmdRequest");
        oButton.disabled = !bEnabled;
        var oList = document.getElementById("lstMethods");
        oList.disabled = !bEnabled
      }

      function performOperation()
      {
        var oList = document.getElementById("lstMethods");
        var sMethod = oList.options[oList.selectedIndex].value;
        var sOp1 = document.getElementById("txtOp1").value;
        var sOp2 = document.getElementById("txtOp2").value;

        //Clear the message panes
        document.getElementById("txtRequest").value = "";
        document.getElementById("txtResponse").value = "";
        document.getElementById("txtResult").value = "";
        performSpecificOperation(sMethod, sOp1, sOp2);
      }
    </script>
  </head>
  <body onload="setUIEnabled(true)">
    <div id="divServiceHandler" style="behavior: url(webservice.htc);"></div>
    Operation: <select id="lstMethods" style="width: 200px" disabled="disabled">
      <option value="add" selected="selected">Add</option>
      <option value="subtract">Subtract</option>
      <option value="multiply">Multiply</option>
      <option value="divide">Divide</option>
    </select>
    <br/><br/>
    Operand 1: <input type="text" id="txtOp1" size="10"/><br/>
    Operand 2: <input type="text" id="txtOp2" size="10"/><br/><br/>
    <input type="button" id="cmdRequest" 
       value="Perform Operation"
       onclick="performOperation();" disabled="disabled"/>
    <br/><br/>
    Result: <input type="text" size="20" id="txtResult">
    <br/>       
    <textarea rows="30" cols="60" id="txtRequest"></textarea>
    <textarea rows="30" cols="60" id="txtResponse"></textarea>
  </body>
</html>
表示例
Operation:

Operand 1:
Operand 2:



Result:
  var iCallId = 0;
  function performSpecificOperation(sMethod, sOp1, sOp2)
  {
    var oServiceHandler = document.getElementById("divServiceHandler");
    if (!oServiceHandler.Math)
    {
      oServiceHandler.useService(SERVICE_URL + "?WSDL", "Math");
    }
    iCallId = oServiceHandler.Math.callService(handleResponseFromBehavior,
                                                sMethod, sOp1, sOp2);
  }


  //This handles the response
  function handleResponseFromBehavior(oResult)
  {
    var oResponseOutput = document.getElementById("txtResponse");
    if (oResult.error)
    {
      var sErrorMessage = oResult.errorDetail.code
                        + "\n" + oResult.errorDetail.string;
      alert("An error occurred:\n"
            + sErrorMessage
            + "See message pane for SOAP fault.");      
      oResponseOutput.value = oResult.errorDetail.raw.xml;
    }
    else
    {
      var oResultOutput = document.getElementById("txtResult");
      oResultOutput.value = oResult.value;
      oResponseOutput.value = oResult.raw.xml;
    }
  }
performSpecificOperation() の動作 SOARP の呼出しが完了すると、 callback関数 handleResponseFromBehavior() が 呼ばれる。

◆Mozilla SOAPCall

HTML 部分で異なるのは、外部のモジュールだけ。
% diff WebServiceTestHarness-Behavior.htm WebServiceTestHarness-SoapClasses.htm
4c4
<     <script type="text/javascript" src="WebServiceExampleBehavior.js"></script>
---
>     <script type="text/javascript" src="WebServiceExampleSoapClasses.js"></script>
34d33
<     <div id="divServiceHandler" style="behavior: url(webservice.htc);"></div>
% 
WebServiceExampleSoapClasses.js の内容:
	
function performSpecificOperation(sMethod, sOp1, sOp2)
{
  var oSoapCall = new SOAPCall();
  oSoapCall.transportURI = SERVICE_URL;
  oSoapCall.actionURI = SOAP_ACTION_BASE + "/" + sMethod;
  var aParams = [];
  var oParam = new SOAPParameter(sOp1, "op1");
  oParam.namespaceURI = SOAP_ACTION_BASE;
  aParams.push(oParam);
  oParam = new SOAPParameter(sOp2, "op2");
  oParam.namespaceURI = SOAP_ACTION_BASE;
  aParams.push(oParam);
  oSoapCall.encode(0, sMethod, SOAP_ACTION_BASE, 0, null, aParams.length, aParams);
  var oSerializer = new XMLSerializer();
  document.getElementById("txtRequest").value = 
                        oSerializer.serializeToString(oSoapCall.envelope);                    
  setUIEnabled(false);

  //more code here
  oSoapCall.asyncInvoke(
                          function (oResponse, oCall, iError)
                          {
                            var oResult = handleResponse(oResponse, oCall, iError);
                            showSoapResults(oResult);
                          }
                        );

}

function handleResponse(oResponse, oCall, iError)
{
  setUIEnabled(true);
  if (iError != 0)
  { 
    alert("Unrecognized error.");
    return false;
  }
  else
  {
    var oSerializer = new XMLSerializer();                          
    document.getElementById("txtResponse").value = 
                    oSerializer.serializeToString(oResponse.envelope);
    var oFault = oResponse.fault; 
    if (oFault != null)
    { 
      var sName = oFault.faultCode; 
      var sSummary = oFault.faultString; 
      alert("An error occurred:\n"  + sSummary
                                + "\n" + sName
                                + "\nSee message pane for SOAP fault");
      return false;
    }
    else
    {
      return oResponse;
    }
  }
}

function showSoapResults(oResult)
{
  if (!oResult) return;       
  document.getElementById("txtResult").value = oResult.body.firstChild.firstChild.firstChild.data;
}
callback 関数では、handleResponse() と showSoapResults() を呼ぶ。 (すぐには呼ばれない。SOAPの応答が来てから呼ばれる。)

handleResponse() 関数の動作

showSoapResults() の動作 応答オブジェクトの getParameters() メソッドが使えると、 もう少し簡単。

◆多くのブラウザで動作する方法

多くのブラウザで動作させるには、 XmlHttp (zXmlHttpなど)を使うしかない。
  function performSpecificOperation(sMethod, sOp1, sOp2)
  {
    oXmlHttp = zXmlHttp.createRequest();
    setUIEnabled(false);    
    var sRequest = getRequest(sMethod, sOp1, sOp2);
    var sSoapActionHeader = SOAP_ACTION_BASE + "/" + sMethod;
    oXmlHttp.open("POST", SERVICE_URL, true);
    oXmlHttp.onreadystatechange = handleResponse;
    oXmlHttp.setRequestHeader("SOAPAction", sSoapActionHeader);
    oXmlHttp.setRequestHeader("Content-Type", "text/xml");
    oXmlHttp.send(sRequest);
    document.getElementById("txtRequest").value = sRequest; 
  }

  function getRequest(sMethod, sOp1, sOp2)
  {
    var sRequest = "<soap:Envelope xmlns:xsi=\""
                 + "http://www.w3.org/2001/XMLSchema-instance\" "
                 + "xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" "
                 + "xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">\n"
                 + "<soap:Body>\n"
                 + "<" + sMethod + " xmlns=\"" + SOAP_ACTION_BASE + "\">\n"
                 + "<op1>" + sOp1 + "</op1>\n"
                 + "<op2>" + sOp2 + "</op2>\n"
                 + "</" + sMethod + ">\n"
                 + "</soap:Body>\n"
                 + "</soap:Envelope>\n";
    return sRequest;                 
  }

  function handleResponse()
  {
    if (oXmlHttp.readyState == 4)
    {
      setUIEnabled(true);
      var oResponseOutput = document.getElementById("txtResponse");
      var oResultOutput = document.getElementById("txtResult");
      var oXmlResponse = oXmlHttp.responseXML;
      var sHeaders = oXmlHttp.getAllResponseHeaders();
      if (oXmlHttp.status != 200 || !oXmlResponse.xml)
      {
        alert("Error accessing Web service.\n"
              + oXmlHttp.statusText
              + "\nSee response pane for further details.");
        var sResponse = (oXmlResponse.xml ? oXmlResponse.xml : oXmlResponseText);        
        oResponseOutput.value = sHeaders + sResponse;
        return;
      }
      oResponseOutput.value = sHeaders + oXmlResponse.xml;
      var sResult = oXmlResponse.documentElement.firstChild.firstChild.firstChild.firstChild.data;
      oResultOutput.value = sResult;
    }
  }  

handleResponse() は、callback 関数。
↑[もどる] ←[2月8日] ・[2月15日] →[2月20日]
Last updated: 2008/02/20 11:30:01
Yasushi Shinjo / <yas@is.tsukuba.ac.jp>