並行システム
                               システム情報工学研究科コンピュータサイエンス専攻、電子・情報工学系
                               新城 靖
                               <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版もある。)
public class Hello implements java.io.Serializable  {
    public void sayHello(java.lang.String name) {
        System.out.println("Hello " + name);
    }
}
djeyc コンパイラで、2つの Java のクラスが作られる。
	
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 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();
    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();
    }
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");
    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");
1999年に、サン・マイクロシステムズ社のビル・ジョイらによって開発され発 表された。
Jini の目標は、ネットワークで Plug & Play を実現すること。 機器をネットワークに接続しただけで、特別な設定 をしなくてもすぐに利用可能になること。
目標
JavaSpaces は、内部的に Jini のルックアップ・サービスの実現で使われて いる。JavaSpaces も、Jini でアクセス可能なサービスの1つと考えることも できる。
Microsoft の、Jini に対抗した技術。
サービスの登録と検索
ルックアップ・サービス自身も、最初から知られている必要はない。 ネットワーク上で自動的に探される。 Discovery と Join。
Java のオブジェクトが Lease。
リース期間は、延長することができる。 延長されなかった lease は、ルックアップ・サービスから削除される。 登録されているサービスを明示的に削除する仕組みは、存在しない。
サービスが削除された時には、分散型イベント配送サービスにより関係してい る所に知らされる。
トランザクションのインタフェースは定められているが、具体的な実現は各サー ビスにまかされている。
サービスの提供者とサービスの利用者の間は、最終的にはに RMI で接続される。
ServiceIDLister インタフェースを implements する。
package com.sun.jini.lookup;
public interface ServiceIDListener extends java.util.EventListener {
    void serviceIDNotify(net.jini.core.lookup.ServiceID serviceID);
}
ルックアップ・サービスからコールバックされる。
Discovery の実現方法

図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");
   }
}
グループは、名前(文字列)で区別される。
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();
      }
   }
}

図3 Lookup
サービスは、次の3つで区別される。
    public ServiceTemplate(ServiceID serviceID,
                           Class[] serviceTypes,
                           Entry[] attrSetTemplates)
サービス(オブジェクト)の探し方
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();
      }
   }
}
信頼性が低いネットワークと、どう戦う方法の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 を使う。
<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>
Mozilla XMLHttpRequest は、オブジェクト指向的にした。

図? 伝統的なWebブラウザ

図? AjaxのWebブラウザ
<!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>
<!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>
<div id="divCustomerInfo"></div>は、将来、結果
を埋めるための場所になる。
"GetCustomerData.php?id=" + sId を入れる。
これで、HTTP の GET 要求が飛ぶ。
<div id="divCustomerInfo"></div>の内部のHTML を置き換える。
<!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>
<div id="divInfoToReturn">データ</div>に、
データを返す。
<div id="divInfoToReturn"></div>で囲まれた
部分のノードを取り出す(divInfoToReturn)。
...
<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 のノードを追加する方法も
ある。
<form method="post"></form> という
ノードを用意し、内容を埋めて、submit する。
<!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>
% wc zxml.js2 144 6597 zxml.js %
![[]](../icons/screen-cursor.gif)
| readyState | 説明 | 
| 0 (Uninitialised) | オブジェクトが作られたが、まだ open() が呼ばれていない。 | 
| 1 (Loading) | open() は、呼ばれたが、まだ要求は送られていない。 | 
| 2 (Loaded) | 要求は送られた。 | 
| 3 (Interactive) | 応答の一部を受け取った。 | 
| 4 (Completed) | 全データを受信した。コネクションも切断した。 | 
readyState == 4 なら、全データが読み込まれた。
XmlHttp.status == 200 なら、成功。
displayCustomerInfo() で、結果を表示する。
XmlHttp.status == 200 ではないなら、エラーを表示する。
<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>
表示例
  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() の動作
<textarea rows="30" cols="60" id="txtResponse"></textarea>
の内容を書き換える。
<input type="text" size="20" id="txtResult">
のvalue="" を書き換える。
% 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;
}
handleResponse() 関数の動作
<input type="text" size="20" id="txtResult">
のvalue="" を書き換える。
  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 関数。