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