650 likes | 797 Views
Java Distributed Objects 3. Masayuki Iwai tailor@ht.sfc.keio.ac.jp. Table of contents. 第 1 回 10 月 11 日木曜日 5 限 Distibuted objects の目指す世界 Rmi 以前 tutorial Rmi tutorial 第 2 回 10 月 19 日金曜日 6 限 Rmi Advanced tutorial Rmi tutorial custom socket package Activatable 第 3 回 10 月 17 日木曜日 5 限
E N D
Java Distributed Objects3 Masayuki Iwai tailor@ht.sfc.keio.ac.jp
Table of contents • 第1回10月11日木曜日5限 • Distibuted objectsの目指す世界 • Rmi 以前 tutorial • Rmi tutorial • 第2回10月19日金曜日6限 • Rmi Advanced tutorial • Rmi tutorial • custom socket package • Activatable • 第3回10月17日木曜日5限 • Jini programming • Java Space programming • Distributed Objectsの世界
Distibuted objectsの目指す世界 • タスクの依頼 • 自由な連携 take write take read write
Local Method Invocation LocalMainClass LocalRuntime 192.168.0.1 • Local Method Invocation LocalClass localClass=new LocalClass(); outputObj=localClass.method(inputObj); • inputObjはprimitive型でない限りcall by referenceとして渡される。
RMI:Remote Method Invocation RemoteClass LocalMainClass method LocalRuntime RemoteRuntime 192.168.0.1 192.168.0.2 Remote Classのmethodを Callbyreferenceで呼びたいのがRMI
RMI:Bの側にCのクラスファイルがない場合RMI:Bの側にCのクラスファイルがない場合 • Interfaceを利用したCastだけではだめ • BはCを保持しておく必要がないが最終的にはCの情報が必要->NetworkClassLoader Cの情報をダウンロード C NetworkClassServer NetworkClassLoader interface C C C’ A serializable B C 0100101001010 ObjectOutputStream VM_A VM_B
RMI:すこし立ち止まって • クラスサーバは果たしてLocalRuntimeにある必要はあるのか? Cの情報をダウンロード C NetworkClassServer NetworkClassServer interface C C C’ A serializable B C 0100101001010 ObjectOutputStream VM_A VM_B
RMI:Marshalled Object • MarshalledObject=serialized object+そのロード元 (可能な場合) となるコードベース URL • BはCを知る必要がない:webserverは何処にあってもよい:動作はVM_B上 C http/get websrver Cクラスの解決情報 C C C C C’ A serializable B ObjectOutputStream VM_A VM_B
RMI: MarshalledObjectの実装 public final class MarshalledObject implements Serializable { /** * @serial Bytes of serialized representation. If <code>objBytes</code> is * <code>null</code> then the object marshalled was a <code>null</code> * reference. */ private byte[] objBytes = null; /** * @serial Bytes of location annotations, which are ignored by * <code>equals</code>. If <code>locBytes</code> is null, there were no * non-<code>null</code> annotations during marshalling. */ private byte[] locBytes = null; /** * @serial Stored hash code of contained object. * * @see #hashCode */ }
RMI:Object I/O Streamの2つの問題 • オブジェクト単位 public class Account implements Serializable{ public void setMoney(){….} public Money getMony(){….} public void addMony(){….} public void subMony(){….} } =================書き出す側 VM_A=================== Socket s = ss.accept(); ObjectOutputStream oos = new ObjectOutputStream(s.getOutputStream()); Account acc= Bank.getAccount(me); oos.writeObject(acc); s.close(); =================読み出す側 VM_B=================== ObjectInputStream ois = new ObjectInputStream(s.getInputStream()); Account acc= (Account) (ois.readObject()); acc.addMony(1000); AccountクラスってRemoteに最新のものあるの? これって変更されるのRemoteのAccountクラスだよね
RMI: • Socket/ObjectStreamレベルのことはStabが行う。 • Stabクラスはrmic –v1.2 ServerClassのコマンドで生成 • 実はMarshalledObjectを利用してRMIは実装されている。 • コードベースのためWebserverが必要 • スタブオブジェクトが必要:動作はVM_A上 C クラスCは必要ない WEBSERVER インタフェースC’ UnicastRemoteObject C C C’ A C_stub C_stub B RMI VM_A VM_B
RMI:SocketProgrammingではない。 =================サーバ側 VM_A=================== public class AccountImpleextends UnicastRemoteObject implements IAccount{ } AccountImple acc= Bank.getAccountImple (me); Naming.rebind(“registryhostname/Account_Iwai”,acc); 起動方法 >>java MainClass java.rmi.server.codebase=http://codebasehost/accout.jar =================クライアンド側 VM_B=================== String location = "rmi://registryhostname/Account_Iwai" ; acc=(IAccount)Naming.lookup(location); acc.addMony(1000); Socketプログラミングあらたな問題
Remote インタフェースの実装 publicinterface MoneyRemote extendsRemote { publicvoid increment() throws RemoteException; publicvoid printMoney() throws RemoteException; publicint getMoney() throws RemoteException; }
MoneyLocalClient publicclass MoneyLocalClient { publicstaticvoid main(String argv[]){ MoneyRemote remotemoney; //インタフェースのみの宣言である点に注意 //codebaseがなければキャスト例外を起すはず try { String location = "rmi://localhost/MoneyServer" ; remotemoney=(MoneyRemote)Naming.lookup(location); System.out.println("Remote obj: " + remotemoney ); System.out.println("money local client:"+remotemoney.getMoney()); remotemoney.increment(); //remote呼び出し remotemoney.printMoney();//remote呼び出し } catch (Exception e) { System.out.println("err: " + e); e.printStackTrace(); } } }
MoneyRemoteImpl publicclass MoneyRemoteImpl extendsUnicastRemoteObjectimplementsMoneyRemote { //ここのpublic必須 <- なぜか理由を考えてみてね。 publicMoneyRemoteImpl() throws RemoteException { super(); } int money = 0; publicvoid increment() throws RemoteException { money++; } publicvoid printMoney() throws RemoteException { System.out.println("Remote Money is:" + money); } publicint getMoney() throws RemoteException { return money; } }
RemoteMain publicclass RemoteMain { publicstaticvoid main(String argv[]) { if (System.getSecurityManager() == null) { System.setSecurityManager(new RMISecurityManager()); } try { MoneyRemoteImpl money = new MoneyRemoteImpl(); money.increment(); Naming.rebind("rmi://localhost/MoneyServer", money); //実は名前は何でもよい。 System.out.println("MoneyServer bound in registry"); money.printMoney(); } catch (Exception e) { System.out.println(e.getMessage()); e.printStackTrace(); } } }
コマンド • Stubの生成 • rmic -v1.2 jp.ac.sfc.keio.sfc.tailor.rmi.test.remote.MoneyRemoteImpl • Stubはクライアントにコピーが必要 • Codebaseのためのjar化 • jar cvf 1rmiMoneyRemoteImpl.jar jp\ac\sfc\keio\sfc\tailor\rmi\test\ • Rmiregistryの起動 • jar cvf 1rmiMoneyRemoteImpl.jar jp\ac\sfc\keio\sfc\tailor\rmi\test\ • サーバの起動 • java -Djava.rmi.server.codebase=http://www.ht.sfc.keio.ac.jp/~tailor/rg/1rmiMoneyRemoteImpl.jar -Djava.security.policy=securitypolicy.txt jp.ac.sfc.keio.sfc.tailor.rmi.test.remote.RemoteMain • クライアントの起動 • java -Djava.security.policy=securitypolicy.txt jp.ac.sfc.keio.sfc.tailor.rmi.test.local.MoneyLocalClient
わざとcodebaseでエラーを起す。 • java -Djava.rmi.server.codebase=http://www.ht.sfc.keio.ac.jp/~tailor/rgX/ -Djava.security.policy=securitypolicy.txt jp.ac.sfc.keio.sfc.tailor.rmi.test.remote.RemoteMain • Rmiregistryのcodebaseに開発コードがないことを確認
rmiregistryの正体 package jp.ac.sfc.keio.sfc.tailor.rmi.registry; import java.rmi.AlreadyBoundException; import java.rmi.Naming; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import jp.ac.sfc.keio.sfc.tailor.rmi.test.remote.MoneyRemoteImpl; public class RegistryMain { /** * rmiregistryが何をしているか知るプログラム */ public static void main(String[] args) { MoneyRemoteImpl money; try { money = new MoneyRemoteImpl(); money.increment(); Registry reg = LocateRegistry.createRegistry(1099); reg.bind("MoneyServer", money); } catch (RemoteException e) { // TODO 自動生成された catch ブロック e.printStackTrace(); } catch (AlreadyBoundException e) { // TODO 自動生成された catch ブロック e.printStackTrace(); } } }
万能rmiサーバを作成する。 public interface Compute extends Remote { Object executeTask(Task t) throws RemoteException; }
Task import java.io.Serializable; public interface Task extends Serializable { Object execute(); }
TaskMillis public class TaskMillis implements Task { public Object execute() { //executeはObjectを返しますので、プリミティブ・タイプのlongではなく、そのラッパーのLongが返り値 return new Long(System.currentTimeMillis()); } }
ComputeEngine publicclass ComputeEngine extends UnicastRemoteObject implements Compute { publicComputeEngine() throws RemoteException { super(); } public Object executeTask(Task t) { returnt.execute(); } publicstaticvoid main(String[] args) { if (System.getSecurityManager() == null) { System.setSecurityManager(new RMISecurityManager()); } try { String name = "rmi://localhost/Compute"; Compute engine = new ComputeEngine(); Naming.rebind(name, engine); System.out.println("ComputeEngine bound"); } catch (Exception e) { System.err.println("ComputeEngine exception: " + e.getMessage()); e.printStackTrace(); } } }
ComputeClientMillis public class ComputeClientMillis { static void main(String[] args) { if (System.getSecurityManager() == null) { System.setSecurityManager(new RMISecurityManager()); } try { String name = "rmi://localhost/Compute"; Compute comp = (Compute) Naming.lookup(name); // ここからは、処理に応じて変わる。これは、サーバ時計を表示する例。 Long millis = (Long) (comp.executeTask(new TaskMillis())); System.out.println("Remote Date : " + new Date(millis.longValue())); System.out.println("Local Date : " + new Date()); } catch (Exception e) { System.err.println("ComputePi exception: " + e.getMessage()); e.printStackTrace(); } } }
実行コマンド • rmic -v1.2 jp.ac.sfc.keio.sfc.tailor.rmi.compute.ComputeEngine • jar cvf 2rmiCompute.jar jp\ac\sfc\keio\sfc\tailor\rmi\compute\ • start rmiregistry • java -Djava.rmi.server.codebase=http://www.ht.sfc.keio.ac.jp/~tailor/rg/2rmiCompute.jar -Djava.security.policy=securitypolicy.txt jp.ac.sfc.keio.sfc.tailor.rmi.compute.ComputeEngine • java -Djava.security.policy=securitypolicy.txt jp.ac.sfc.keio.sfc.tailor.rmi.compute.ComputeClientMillis
ComputeClientPrime ublic class ComputeClientPrime { public static void main(String[] args) { if (System.getSecurityManager() == null) { System.setSecurityManager(new RMISecurityManager()); } try { String name = "rmi://localhost/Compute"; Compute comp = (Compute) Naming.lookup(name); TaskPrime taskprime =new TaskPrime(); //taskprime.setPrimenum("69143"); taskprime.setPrimenum(args[0]); // ここからは、処理に応じて変わる。これは、サーバ時計を表示する例。 Boolean ansprime = (Boolean) (comp.executeTask(taskprime)); System.out.println("prime : " + ansprime.booleanValue()); } catch (Exception e) { System.err.println("ComputePi exception: " + e.getMessage()); e.printStackTrace(); } } }
TaskPrime package jp.ac.sfc.keio.sfc.tailor.rmi.compute; import java.math.BigInteger; publicclass TaskPrime implements Task { String primenum="69127"; public Object execute() { returnnew Boolean(getPrime(primenum)); } privateboolean getPrime(String prime) { BigInteger n = new BigInteger(prime); if (n.compareTo(BigInteger.ZERO) <= 0) { System.out.println("自然数を入力して下さい"); System.exit(1); } System.out.println("入力された数は " + n.toString()); if (n.compareTo(BigInteger.ONE) == 0) { System.out.println("素数ではありません"); returnfalse; } elseif (n.compareTo(BigInteger.valueOf(2)) == 0) { System.out.println("素数です"); returnfalse; } BigInteger r = n.mod(BigInteger.valueOf(2)); if (r.compareTo(BigInteger.ZERO) == 0) { System.out.println("除算可能な数は 2"); System.out.println("素数ではありません"); returnfalse; } BigInteger s = this.bigIntegerSqrt(n); for (BigInteger d = BigInteger.valueOf(3); d.compareTo(s) <= 0; d = d.add(BigInteger.valueOf(2))) { r = n.mod(d); if (r.compareTo(BigInteger.ZERO) == 0) { System.out.println("除算可能な数は " + d.toString()); System.out.println("素数ではありません"); returnfalse; } } System.out.println("素数です"); returntrue; } publicstatic BigInteger bigIntegerSqrt(BigInteger x){ BigInteger b1 = new BigInteger(x.toString()), b2 = (b1.pow(2).add(x)).shiftRight(1).divide(b1); while(b2.compareTo(b1) < 0){ b1 = new BigInteger(b2.toString()); b2 = (b1.pow(2).add(x)).shiftRight(1).divide(b1); } return b1; } public String getPrimenum() { return primenum; } publicvoid setPrimenum(String primenum) { this.primenum = primenum; } }
素数計算をさせる • java -Djava.security.policy=securitypolicy.txt jp.ac.sfc.keio.sfc.tailor.rmi.compute.ComputeClientPrime 69143 • java -Djava.security.policy=securitypolicy.txt jp.ac.sfc.keio.sfc.tailor.rmi.compute.ComputeClientPrime 69149 • java -Djava.security.policy=securitypolicy.txt jp.ac.sfc.keio.sfc.tailor.rmi.compute.ComputeClientPrime 69151 • java -Djava.security.policy=securitypolicy.txt jp.ac.sfc.keio.sfc.tailor.rmi.compute.ComputeClientPrime 69157
Mission • AttendanceTaskクラスを作成しなさい。 • execute()メッソッドに • サーバの私に • 学籍番号,ログイン名,名前 • をprintlnする機能を実装 • 岩井の立てているcompクラスに対して • 通信を行え。 • rmi://岩井のマシンのIPアドレス/Compute
Jini: Rmiregistryのホスト名の発見 • SocketProgrammingは主に相手のホスト名/IPをあらかじめ知っている。 • RMIはrmiregistryをおいている。 • Rmiregistryのホスト名と • そのため通信相手の登録名を知っていればいい • Ex “Accoount_Iwai” • どうやってRmiregistryのホスト名をどうやって見つけるのか?
Jiniのレイヤ構造 Jini RMI/HTTP Socket TCP/UDP/IP
Jiniの特徴で本日話すこと • Discovery • Lease
Jini:Doscovery and Join ①自分のIPをネットワークにマルチキャストで流す ②感知 ④サービス情報の登録(ジョイン) ③感知確認のUnicast応答 (ディスカバリ) • サービスが登録される時 C LookupService サービス サービス C B A
Jini:Service Lookup ⑤要求がある事を通知(activation?) ④サービス情報検索 ⑥利用プログラムの入手 ⑦サービス間と直接やり取り(RMI) LookupServiceの発見(前ページ①~③)までは一緒 C LookupService A B サービス サービス C C
サーバ側 Cクラス public class AccountImle extends UnicastRemoteObject implements Account{ public int getMony() throws RemoteException { return mony; } }
サーバ側 Aクラス public class MainClass implements DiscoveryListener, Serializable { lookupDiscovery = new LookupDiscovery(“Bank”); lookupDiscovery.addDiscoveryListener(this); public void discovered(DiscoveryEvent evnt) { ServiceRegistrar[] regs = evnt.getRegistrars(); AccountImple acc = new AccountImple(); ServiceItem item = new ServiceItem(null, acc , attrs ); regs[i].register(item, Lease.FOREVER); //登録 } }
クライアント側 Bクラス ServiceTemplate tmpl = new ServiceTemplate(null, classes , null); acc = (Account)(regs[i].lookup(tmpl)); acc. getMony(); //テンプレートにそうものが見つからなかった //場合は通知依頼を登録できる。 reg[i].notify(template, transitions, tellme, mo, Lease.ANY); 何の通知を依頼できるか? 新規登録、ステータス変化、消去の3種類
Jini:Lease • Lookupに登録されたObjectはたまっていく一方なのか? →LookupServiceがわの都合で定期的に強制削除されます。 LookupService Lease expire Lease renew 障害 サービス サービス そこでずっと登録してほしければ サービス側から常にLease延長要求をだす。
JavaSpaces: • Spaceプログラミングモデル • たまっていくオブジェクトの概念を使った分散システム構築方法 • Linda • author = "D. Gelernter", • title = "{Generative Communication in Linda}", • note = " ACM Transactions on Programming • Languages and Systems, 7(1):80--112, 1985.", • year = "1985" • T-Spaces(ibm)
Java Spaces 分散された仮想オブジェクト置き場 内部的にはJiniのLease機構 take write take read write
Java Spaces • write(obj) • read() • readIfExist() • take() • taleIfExist() • notify() • snapshot() • 問題点 • ObjectHeavyEaterクラスを作成されたら • 広域ネットワークでつかえるか?
Napster:ログインプロセス どうやって広域ネットワークで相手と通信するか? ログインサーバ群 リダイレクトサーバ群 ③ログインサーバの指定 ④ID、PASS ②ログインサーバの要求 ⑤自らのサーバント情報の登録 サーバンド ①自らのサーバント情報の構築
Napster:検索プロセス ログインサーバ ①検索要求 ③詳細要求 ④サーバンドのIP、Portなど サーバンド ②検索結果 サーバント ⑤自らのサーバントコネクト要求 ⑥直接ダウンロード
Napster:ダウンロードプロセス ログインサーバ ③逆転送要求+相手サーバントIPアドレス ②逆転送要求+自分のIPアドレス サーバンド サーバント ①自らのサーバントコネクト要求 ④サーバントへのアップロード開始接続要求
Gnutella:GnutellaNetへの参加 WEBやキャッシュからneighborを見つける そのNeighborのさらにneighborをおくってもらう。 自分の子供が7人になるまで安定させる。
Gnutella:用語 • Ping • ネットワーク上でホストを探す。ピングのデスクリプターを受け取ったサーバントは1つ以上のPongのデスクリプターを返す。 • Pong • Pingへの返答。接続されたサーバントのアドレス、及び、ネットワークへと利用可能なデータの総量 とみなされる情報を含む。 • Push • ファイアウォール化されたサーバントが、ネットワークへのファイルベースのデータを提供することを可能にするメカニズム
Gnutella:用語 • Query • 分散型ネットワークを検索するために必要となる主要なメカニズム。もしローカルのデータの中に検索にふさわしいものが見つかったら、クエリーのデスクリプターを受け取ったサーバントはクエリーヒットを返信。 • QueryHit • クエリーの返信。一致したクエリーのマッチングを行うデータを手\に入れるために、このデスクリプターは十分な情報を持つ受け取り人を提供