並行システム システム情報工学研究科コンピュータサイエンス専攻、電子・情報工学系 新城 靖 <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 %
![]()
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 関数。