情報システム技術論’08 — ＃ 1
久野靖∗

2008.4.10

はじめに• 昔は末端の機器の処理能力が低かったことからこれが通常

こんにちは、久野です。今週から「情報システム技術論」
を開始します。この科目は今年度は「現在の情報システムに □ なぜ今日、分散アプリケーションなのか?
おいてどのような技術が使われているのか」について、分• PC の普及→非常に多数の利用者→「センターが全部散アプリケーション、とりわけ Web アプリケーションを中こなす」ではあまり大したことができない、アピー

心に取り上げて行きます。とりあえず、最初の 3 回 (4 月ぶ

ルしないん) は次のように予定します。

• 末端の (利用者が接する) システムの能力が大きくなり仕事を分担可能• 分散アプリケーションと Java 言語における具体例• 端末側とセンター側の分担/有機的な協力→魅力的な機能やインタフェースを実現可能に• Web アプリケーションとその基本技術• ページ内スクリプトとデータベースの連携• 例:

残り 2 回ぶんの内容についてはそこまでの様子を見た上でGoogle Maps、Yahoo!

Maps□ 一方で難しさも決定します。

• 分散アプリケーションは「センター集中」より作り

1

分散アプリケーション

1.1

なぜ分散アプリケーションか?

づらい (面倒、複雑、大変)• そのため、ある程度のフレームワーク/サポートをできあいで用意するこことが行われている• そのようなフレームワークもそれぞれ構造がややこ□ 分散システム→ネットワークで相互に接続された要素どしくマスターするのが面倒という面も。

うしが有機的に連携して動作するようなシステム。

• 例:

電子メール、DNS、WWW、…

分散アプリケーションの構造

1.2□ 分散アプリケーション→アプリケーション (特定の用途□ 既存の分散システムの上に乗る形での構築のためのプログラム) が上述のような形で動作。

□ 独自プロトコル、独自技術によるもの (大変) ←今回• 例: グループウェア、オンライン会議、ファイル交 □ ある程度までフレームワークにたよる←今回
換、…
□ メールの上で動くもの…
□ 「銀行の預金管理システム」「JR の座席予約システム」
などは?

不要)

• →オンラインシステムとは呼ばれるが分散とはあまり言わないかも…
• 理由:• メーリングリスト、メールマガジン: ユーザインタ
フェースはメールソフトや携帯端末 (インストール• それ以外にも、速度を要求しないなら、メールを通信のためのインフラとして使うような分散アプリケー「センター」がほとんどの業務を行い、「端

ションは十分あり得る

末」は入力と出力□ World Wide Web の上で動くもの (Web アプリケーション)∗ 筑波大学大学院経営システム科学専攻1• 今日では非常にメジャー
public class Sample11 extends JFrame {JTextField f1 = new JTextField();
JButton b1 = new JButton("Exec");
public Sample11() {setSize(400, 400);
setDefaultCloseOperation(EXIT_ON_CLOSE);
Container c = getContentPane(); c.setLayout(null);c.add(f1); f1.setBounds(40, 40, 180, 40);c.add(b1); b1.setBounds(240, 40, 80, 40);
b1.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent evt) {f1.setText(f1.getText() + "*");}});}
public static void main(String[] args) {new Sample11().setVisible(true);}}• ユーザインタフェースはブラウザ画面• インストール不要• サーバ側/クライアント側連携• 一方で構築上の難しさ/セキュリティ上の難しさ (その構築技術は次回以降とりあげる)□ 今回は独自プロトコルやフレームワークについて見てみる□ Java 言語によるサンプルを題材に考えて行く• Java 言語: ネットワークまわりの標準 API が充実→題材として適する• とりあえず Java 言語は初めてでも大丈夫なように説
明します

□ 解説:

2

Java での基本的なネット接続• import は必要なクラス群を短く指定する機能

2.1

Java 言語の復習ないし入門• クラス Sample11 は JFrame(ウィンドウ) の機能/
データ構造を継承□ Java 言語の歴史と概観• 画面内に入力欄とボタンを置く→これらのオブジェ
クトを用意• 1991 年に Sun Microsystems 社内で開発• 最後にある main() ではこの窓を生成して直ちに表示させるだけ• 基本的な設計→「複雑な/危ない機能を削除した C++
言語」• 初期設定はクラス名と同じ名前のメソッド (コンス• 1995 年 HotJava ブラウザ (アプレット) →一躍ブートラクタ) で指定

ムに

• 「ブラウザ上で動く」機能は Flash などが主流に• 窓のサイズは 400x400• Java は「普通のソフトウェア開発用言語」としての利用がメインに• 窓を閉じるとこのプログラムも終了する• 窓の中の部品を貼る場所を取り出し、自動配置機能
をオフに□ Java 言語の構造と機能を非常に簡単に説明すると…

• 入力欄を貼って位置指定• プログラムはすべて class• ボタンを貼って位置指定• 強い型の言語。型=class 型 + 基本型 (int, float,• ボタンに動作をつける…「入力欄の文字列を取り出...)し、後ろに*を連結し、再び入力欄にセットする」• 「static void main(String args[])」のメソッド□ 演習:このプログラムを打ち込んで動かしなさい。動いから実行開始たら部品の配置を変えたりボタンの動作を変えてみな• 「new クラス (…)」でオブジェクトを新規生成

さい。

• 「オブジェクト. メソッド (…)」でオブジェクトに付随するメソッド起動

2.2□ Sample11.java:簡単な GUI プログラム

C/S モデルとその得失□ C/S(クライアントサーバモデル)

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;• 今日のネット上で主流の分散アプリの構造• サーバは常時稼働状態にある2• クライアントは必要が生じたらユーザが動かす} catch(Exception ex) { l1.setText("!" + ex); }}});• クライアントからサーバに接続してサービスを受ける• サービスが終わったら切断する}
public static void main(String[] args) {new Sample12().setVisible(true);}□ C/S 構成の利点• サーバ上に共有のデータを置いておける→情報の共有 (=ネットの利点) が行いやすい

}

□ 解説:

• サーバ上の共有データを扱うのはサーバプログラム• GUI 部品としてラベル (表示欄) を増やした。

だけ→整合性の問題が置きにくい• ボタンの動作全体を try-catch で囲み、例外が発生したらその例外を受け止めて文字列としてラベルに• クライアントは好き勝手に起動/終了してかまわない→やりやすい表示するようにした。

□ →今日のネット上のサービスアーキテクチャとして主流• ボタン動作の内容は以下の通り。

□ C/S 構成の弱点• ソケットを localhost(このマシン) の 4192 番ポートに接続。

• サーバがボトルネックになりやすい← (性能、single• そのソケットの書き込み口/読み出し口を用意。

point of failure)• 書き込み口には入力欄の内容を書く。

• スケーラビリティに欠ける• 読み出し口から受け取ったものを入力欄にセット。

□ 複数のサーバが互いに情報交換しあいながら動作するものものある (メール、ニュース、IRC、…) →スケーラブ □ 一方、サーバ側は GUI はなし。次のように。
ル、ただし複雑

2.3

import java.net.*;import java.io.*;Java による簡単な C/S 通信

public class Sample12Server {public static void main(String[] args)
throws Exception {□ クライアント側は先のプログラムを多少手直し
ServerSocket ss = new ServerSocket(4192);
while(true) {import java.awt.*;
Socket cs = ss.accept();
import java.awt.event.*;
PrintWriter out = new PrintWriter(
import javax.swing.*;
cs.getOutputStream(), true);import java.io.*;
BufferedReader in = new BufferedReader(import java.net.*;new InputStreamReader(cs.getInputStream()));
String line = in.readLine();public class Sample12 extends JFrame {out.println(line + line);
JTextField f1 = new JTextField();
cs.close();JButton b1 = new JButton("Exec");if(line.equals("bye")) break;
JLabel l1 = new JLabel("start...");}
public Sample12() {ss.close();setSize(400, 400);}
setDefaultCloseOperation(EXIT_ON_CLOSE);
Container c = getContentPane(); c.setLayout(null); }c.add(f1); f1.setBounds(40, 40, 180, 40);c.add(b1); b1.setBounds(240, 40, 80, 40);
□ 解説:
c.add(l1); l1.setBounds(20, 360, 360, 40);
b1.addActionListener(new ActionListener() {• ポート 4192 番のサーバソケットを作る。以下ループ。
public void actionPerformed(ActionEvent evt) {• クライアントが接続してくるのを待つ。接続してく
try {Socket cs = new Socket("localhost", 4192);
ると通常の通信用ソケットが返されて来る。
PrintWriter out = new PrintWriter(• これに対して書き込み口/読み出し口を用意する。
cs.getOutputStream(), true);
BufferedReader in = new BufferedReader(• まず 1 行読み込み、その文字列を 2 回連結したもの
new InputStreamReader(cs.getInputStream()));
を書き出す。
out.println(f1.getText());• 終わったらソケットは閉じる。
f1.setText(in.readLine());

3• 渡された 1 行が bye だったらループ終わり。

□ 演習:上記だと、1 つのクライアントが使用中は他のクライアントは接続しようとしても待たされるはず。これを• 終わる時はサーバソケットも閉じる。
□ 演習:この 2 つのプログラムを打ち込んで動かしなさい。
複数のクライアントでも動作することを確認すること。
さらに、サーバ内部で次々に受け取った文字列を連結す

各クライアントが 1 つのスレッドとして実行するように手直しし、待たされなくなることを確認してみよ。(これは上記ができてればそれほど大変ではないと思う。)□ ステートレス vs ステートフル

るようにして、複数のクライアント間のデータ共有ができることを確認しなさい。

• 「どちらがいい」というわけではなく、それぞれの利点と弱点がある。

3

分散システムの通信プロトコル

3.1

C/S 通信のさまざまな側面• SMTP、FTP、TELNET、… →ステートフル• HTTP、NFS、… →ステートレス• ステートレスにするには「プロトコル側で状態を持たない」ことをどうやって補うかを考える必要がある。

□ ステートレスなプロトコル• NFS であれば「何番のファイルの何バイト目から何バイト読め/書け」という形で要求を統一することで• 先の例題では「接続→パラメタ送信→結果返送→すぐ切断」の反復。

ステートレスに。一番普通の「順番に読む」操作も• 1 つの要求が終わったらすぐ次の要求を処理。相互に関係がない (独立、状態を持たない→ステートレス)。
• サーバダウン時などの影響が少ない。サーバが簡単、要求時に上の形に直して要求。
□ テキストベースのプロトコル• SMTP、HTTP などはテキストベースのプロトコル

効率的。

% telnet w3in 80
GET /index.html HTTP/1.1
Host: w3inConnection: close• Web サーバや NFS サーバはこのような方針。ただし接続を毎回張っているとオーバヘッドがあるので、接続は張ったままで動作可能。

(ページ返送…)%□ ステートフルなプロトコル• ステートレスの反対。接続している間「自分専用の担当者」に相手してもらう感じ。リモートログイン• テストしてみようと思った時に手で打ち込んだりチェックできるという利点がある。

を想像すればよい。

• その反面、コマンドや付加情報を生成したり受け側• たとえば:接続→パスワード要求→パスワード応答→で解釈したりといった手間が必要。

複数の処理→最後に切断、など。ステートレスでは• その反対→バイナリデータ表現を用いるもの。

これはできない。

• プログラム的には、先の例題で in/out を用意した

3.2

後、1 往復ではなく何往復もやりとりする。

• その場合、やりとりしている間は accept() が実行さ

RPC

□ RPC(Remote Procedure Call)

れないと次のサービスが受け付けられない→スレッ• 通信路を張って往復しつつ通信→プロトコルが手作り、繁雑ドを生成して、そのスレッドがサービスを行い、本体はすぐ次ぎの accept() に進むのが普通。

• もっと多くのものを統一的に扱えプログラミングし□ 演習:先の例題を改造して、クライアントごとに数回にやすい方法は?

分けて文字列を送り、完了するとそれらがまとめて共有• ネットワーク経由の往復は手続き呼び出し時の制御の往復に似ている→プログラム側からは手続き呼び

データに連結される、という風に直してみよ。(結構大変だと思います。ボタンも送信ボタンと完了ボタンの 2

出しに見えるようにしたらいい→ RPC のアイデアつが必要になりますし。)

4

クライ
アント
側

• このため、クライアント側では「どのホスト宛接続

サーバ側する」という情報を供給する必要がある。

要求
送信• サーバ側では RPC 呼び出し全体を管轄し、指定されたプログラムのスタブへの橋渡しを行うための「RPCサーバ」プログラムを動かしている必要がある。

応答
送信

手続き
呼び出し

• FreeBSD、Solaris などでは ONC RPC と呼ばれる RPC
システムが動いていて、「rpcinfo」でサーバ側のプ
ログラム登録状況が見られる。

ＲＰＣ□ RPC の構成…

3.3

ORB と CORBA• クライアント側…クライアントスタブ (普通の手続
きのように見えて呼び出せるが、裏側ではパラメタ □ オブジェクト指向と ORBをパケットに入れてサーバに送り、返されて来たパ• ここまでは「手続き指向」だったが、オブジェクト指ケットから結果を取り出して返送する。

向の普及とともに、呼び出される対象は「オブジェ• サーバ側…サーバスタブ (RPC システムからパケット

クトのメソッド」と考える方が素直に思えるようにを渡され、それを普通のパラメタの形に直して本来なってきた。

の手続きを呼び、戻って来たら結果をパケットに入• サーバ側には「複数のオブジェクト」があり、それぞれが固有のデータを持っている。そのオブジェクト

れて返送する。
ユーザプログラム

サーバスタブx = sub(1, 2);

パケットから
引数取り出しv = sub(1, 2);のメソッドを RPC で呼び出すことで、その固有デー
タに間接的にアクセスできる。

• その遠隔呼び出しの橋渡し (バインディング) →ORB(Object Request Broker) と呼ばれるようにな

結果ｖを
返送する

った。

sub(a, b)a, b をパケッ
トに入れ送信
結果を返す□ CORBA --- ORB の標準仕様。1990 頃からある。

sub(a, b)
手続き本体• ORB と し て は 色々な も の が 作 ら れ た が 、相 互 運

クライアントスタブ
クライアント側用性のために共通のものを作ろうという話になり、OMG(Object Management Group) が 作った 標

サーバ側

準仕様が CORBA(Common Object Request Broker

Architecture)□ RPC ジェネレータ

• CORBA 仕様に従っているなら、どのクライアントとどの ORB を組み合わせても呼び出し利用できる (はず)• 上記のスタブ類を用意するのはすごく面倒に思えるが…

• ただし Microsoft はこれにくみせず COM/DCOM を採• 実際には「名前」「パラメタ個数とそれぞれの型」「返値の型」を指定してやれば、自動的に両側のスタブ

用 (Windows 用の CORBA ソフトももちろん作られたを生成してくれるジェネレータ (RPC ジェネレータ、が) 。

• CORBA IDL(Interface Definition Language)--- どんなメソッドがどんなパラメタを持っている

スタブジェネレータ) が用意されている。

• パラメタや返値が複雑な型のときも、それを詰め込か、を指定する標準的な記法として使われるように

み (marshal)、取り出し (unmarshal) を行うコードなった。(スタブジェネレータに食べさせるのが本が用意される。

来の目的だったがどちらかというと人間が読む仕様□ RPC のバインディング

記述の記法として…)• このように RPC では一見手続き呼び出しのようにサー □ CORBA の現在…
バ上のコードが呼び出せるが、実際には「ネットワー
クの向こう」への通信になっている。
5• 汎用的すぎて面倒であり遅かった。そしてあまり普 import java.rmi.*;及しないうちに Web アプリケーションの時代になっinterface Sample13IF extends Remote {public String exec(String s) throws RemoteException;}てしまった。

• Web ア プ リ で は 遠 隔 呼 び 出 し に CORBA よ り はSOAP(Simple Object Access Protocol) など XML□ 解説:
系の技術を使う。Web アプリ側が接続先を指定できるので ORB はなくても済んでしまう。

• Remote インタフェースである。

• OMG は今は UML などの方に力を入れている (UML は• メソッドは文字列を 1 つ受け取り 1 つ返す exec()

メジャーになったのでそれなりに成功している)。

のみ。

• それはそれとして、現在でも Java などから一応
CORBA は使えるようになっている。

4

Java RMI

4.1

Java RMI の概要• このメソッドは RemoteException を発生させ得る。
□ [2] サーバ側コード• サ ー バ 側 に 置 か れ た オ ブ ジェク ト は extends
UnicastRemoteObject と 指 定 さ れ て い る 必 要 がある。

• サーバ側に置かれるオブジェクトは [1] で宣言した□ RMI(Remote Method Invocation) --- 遠隔オブジェ
クトに対してメソッド呼び出しを行うような機能全般。
□ Java では比較的初期からそのための機能が組み込まれている→ Java RMI。

• ORB、スタブジェネレータなども一通り揃っている。

インタフェースを実装する必要がある。

• このクラス名を指定して Java 付属のスタブジェネ
レータ rmic を実行することでクライアントスタブ(Java ではスケルトンと呼ぶ)、サーバスタブ (Javaでは単にスタブと呼ぶ) が生成される。
• 呼び出し方は次の通り (☆のところにクラス名を指• Java 言語の枠内で揃うようになっているので、CORBA
より簡単。

定)

• データだけでなく、コードも転送可能→大きな特徴。 % /compat/linux/usr/local/Java/jdk1.6.0_05/bin/rmic ☆
□ 以下で先の例題をもとにした「文字列記憶サービス」を用いて RMI の必要事項を説明していく。

□ サーバ側コードはクラス本体部分と main() に分けて説明する。まずクラス本体部分から:

4.2

例題:文字列サービス

import java.rmi.*;import java.rmi.server.*;
import java.rmi.registry.*;import java.io.*;□ [1] インタフェース仕様• Java RMI では CORBA IDL の代わりに Java 言語に備わっているインタフェース定義で遠隔オブジェク public class Sample13Server
トの仕様を規定する。

• このインタフェース仕様は必ず「extends Remote」を指定する。これにより、このインタフェースは遠隔オブジェクトの仕様として使われることが宣言される。

• こ の イ ン タ フェー ス に 現 れ る メ ソッド は す べ てRemoteException という例外を発生させ得るように指定する。

extends UnicastRemoteObject implements Sample13IF {String data;
public Sample13Server() throws RemoteException {data = "?";}
public String exec(String s) {data = data + s; return data;}
public String toString() {return data;}• 実際、ネット経由で呼び出すのでネットワークが切□ 解説 (サーバクラス):
断したらエラーになるから、そのような場合を必ず• extends UnicastRemoteObject の指定必要。
考慮するべき。(今回は例題を短くするためにさぼっ• implements Sample13IF の指定必要。

ている。)

6

• 変数 data は記憶する文字列を保持。

try {
f1.setText(srv.exec(f1.getText()));} catch(Exception ex) { l1.setText("!" + ex); }public static void main(String[] args)
throws Exception {}BufferedReader in = new BufferedReader(});
new InputStreamReader(System.in));}Sample13Server srv = new Sample13Server();
public static void main(String[] args)
Registry reg = LocateRegistry.createRegistry(4918);
throws Exception {reg.bind("str1", srv);
new Sample13().setVisible(true);
System.out.print("command> ");}while(!in.readLine().equals("bye")) {}System.out.println(srv.getData());
System.out.print("command> ");□ 解説:}
System.exit(0);• Naming.lookup() でホスト、ポート、名前を指定→}

そのホストの RMI レジストリ (先に main() で起動}したもの) に接続してリモートオブジェクトを取得□ 解説 (main):• ボタン押した時は単に srv.exec() を呼ぶだけでよい (簡単)。

• コマンド入力のための入力ストリーム用意。
• サーバオブジェクトを作る。

□ 演習:• レジストリ (登録サーバ) をポート 4918 で作る。

文字列サービスを動かして前の演習と同様に確認してみよ。

• レジストリにサーバを str1 という名前で登録。
• コマンド入力ループで、bye という行が入力されるまで 1 行入力ごとに現在のサーバの文字列表現を書き出す。
□ [3] クライアント側コード

5

オブジェクトとコードのモビリティ

5.1

モビリティ(可動性) とその内容□ モビリティ(mobility) →ネットワークを通じて「××」• クライアントはこれまでと同じく GUI を持つ。

が移動可能なこと。

• 初期設定のところでサーバのリモートオブジェクト• 通常のデータ (テキストとか) が移動するのは当り前

を取得

だが…

• あとはこれに対するメソッド呼び出し→ RMI により• 「通常」とは? たとえばポインタは? →ポインタ
は「特定のマシンの上でのメモリアドレス→そのま

サーバで実行

ま別のマシンに移動させても「とんでもない番地を

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.rmi.*;指すポインタ」になるだけ。

• 正しくは「ポインタの指す先のデータ」もコピーする必要がある。

public class Sample13 extends JFrame {Sample13IF srv;
転送
JTextField f1 = new JTextField();
JButton b1 = new JButton("Exec");
A B C
A B C
JLabel l1 = new JLabel("start...");
public Sample13() throws Exception {srv = (Sample13IF)
転送
Naming.lookup("rmi://localhost:4918/str1");
setSize(400, 400);?
?
setDefaultCloseOperation(EXIT_ON_CLOSE);
X Y
P Q R
X Y
P Q R
Container c = getContentPane(); c.setLayout(null);c.add(f1); f1.setBounds(40, 40, 180, 40);c.add(b1); b1.setBounds(240, 40, 80, 40);c.add(l1); l1.setBounds(20, 360, 360, 40);□ 構造のあるデータのコピーは必ずしも簡単ではない…
b1.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent evt) {7

• 共有部分があったらどうするか?

• 直列化可能なクラスのインスタンス変数は基本型 (単→ その形を保っ

純なデータ) または再び直列化可能なクラス型でなてコピーしたい。

• ループがあったらどうするか?

ければならない (送れる必要があるから)。

→ その形を保って• インスタンス変数に transient というキーワードを指定しておくとその変数は送らない (復元したときコピーしたい。
P Q R

P Q R

転送

は null になる)。そのため上記の制約もない。

P Q R□ コードモビリティ(モバイルコード)

P Q R• 受動的なデータだけならただ送ればよいが、オブジェクト指向言語では「データ+メソッド」がオブジェ

転送

クト。
X Y

X Y

• このため、オブジェクトのデータを送ったが送った先にそのオブジェクトのメソッドのコードがなかっ

X Y

た、では済まされない。
X Y• 1 つの方法…あらかじめ必要なクラスのコードは用意させる。それがある状態でのみ送ることとする (普• ☆ポインタを順次たどっていくが、既に転送に着手

通のアプローチ)。

したのと同じものに到達したら、新たに転送する代• 別の方法…オブジェクトを送る時、送り先にそのクわりに「既に転送した何番目と同じ」という情報をラスのコードがまだなければコードも送る (Java の送り、復元時にもそれに応じて復元。

アプローチ) ← コードモビリティ□ コピー vs 参照セマンティクス□ なぜ Java でこれができるか?

• コピーするということは、行った先と手元は別物になるということ。

• もともと「アプレットのための言語」→コードをネッ
ト経由で転送することを前提として開発された言語+• それでよい場合もあるが、オブジェクトは勝手に複環境 ← i-アプリもそう製されてほしくないという場合もある。

• 具体的には、Java バイトコードを送ってそれぞれの• その場合、オブジェクトは動かずにずっとどこかに存在し、そのオブジェクトを指す参照はあちこちに実行環境の JVM (バーチャルマシン) で実行→バイ
トコードは CPU 種別などに影響されずすべて共通→

渡して、それらの参照からオブジェクトを呼び出しこれを送れば OK。

て利用、という考え方もある← CORBA など。

• ただし、「コードが送られる」ことにはセキュリティ• しかしその方法しかないとなると、すべて RMI のよ
うになるため遅いという問題がある。

上の危うさがあるのでそのための工夫も必要 (今回
は詳しくはやらない)。

• CORBA では言語が様々なのでどうにもならないがJava では「コピー」も「参照」も可能。参照は先にやったのがそれ。ではコピーは…

5.2

例題:文字列サービス改訂版□ [1] インタフェース仕様□ Java の場合は…

import java.rmi.*;import java.io.*;• Serializable インタフェースを implements で指定したクラスのオブジェクト→直列化可能 (ネット

ワーク経由での転送、ファイルへのそのままの格納 interface Sample14IF extends Remote {public Data get() throws RemoteException;
public void put(Data d) throws RemoteException;interface Data extends Serializable {public String exec(String s);}が可能)• そのときに前記☆のような制御は用意されている。さらに特別な処理をしたい場合はそれぞれのオブジェ
クトにメソッドを用意することで制御可能。

}

8

□ 解説:• get()、put() はその Date オブジェクトをやりとり。
• toString() では Data オブジェクトの toString()を返す。

• インタフェース Sample14IF の中に Data というインタフェースを定義している。このインタフェースは• クラス MyData はこれまでのサーバと類似している

extends Serializable と指定しているので、これが、通信機能はなく、単にデータを保持し計算。

を実装するオブジェクトは転送可能。

• Data オ ブ ジェク ト に は こ れ ま で サ ー バ に あった □ [3] クライアント側コード
exec() を持たせる。
• フィールド、ボタンとも 2 つに増えている。
• サーバは今度はこの Data オブジェクトを取り出した• 最初のフィールドには「rmi://sma:4918/str1」なり戻したりするようなインタフェースに直した。
どの URI を与えて Conn ボタンを押すとサーバから□ [2] サーバ側コード
Data オブジェクトを取得。その状態で再度 Conn ボ• Data イ ン タ フェー ス を implements す る ク ラ ス

タンを押すと取得してあった Data オブジェクトを書

MyData を用意。このオブジェクトがクライアント
側に転送され、そこで動作する。

• 2 番目のフィールド/ボタンはこれまでの機能に類似。

き戻す。
ただしサーバが処理を行うのではなく、手元に持っimport java.rmi.*;import java.rmi.server.*;
import java.rmi.registry.*;import java.io.*;て来た Data オブジェクトが処理を実行する。

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Sample14Server
extends UnicastRemoteObject implements Sample14IF { import java.rmi.*;Data data;
public class Sample14 extends JFrame {public Sample14Server(Data d)
Sample14IF srv;
throws RemoteException { data = d; }Sample14IF.Data data;
public Data get() { return data; }JTextField f0 = new JTextField();
public void put(Data d) { data = d; }JButton b0 = new JButton("Conn");public String toString() { return data.toString(); }JTextField f1 = new JTextField();
static class MyData implements Data {JButton b1 = new JButton("Exec");
String str = "?";
JLabel l1 = new JLabel("start...");
public String exec(String s) {public Sample14() throws Exception {str = str + s + "!"; return str;
setSize(400, 400);}
setDefaultCloseOperation(EXIT_ON_CLOSE);
public String toString() { return str; }Container c = getContentPane(); c.setLayout(null);}c.add(f0); f0.setBounds(40, 40, 180, 30);
public static void main(String[] args)
c.add(b0); b0.setBounds(240, 40, 80, 30);
throws Exception {c.add(f1); f1.setBounds(40, 80, 180, 30);
BufferedReader in = new BufferedReader(c.add(b1); b1.setBounds(240, 80, 80, 30);
new InputStreamReader(System.in));Sample14Server srv = new Sample14Server(new MyData()); c.add(l1); l1.setBounds(20, 360, 360, 30);b0.addActionListener(new ActionListener() {Registry reg = LocateRegistry.createRegistry(4918);
public void actionPerformed(ActionEvent evt) {reg.bind("str1", srv);
try {System.out.print("command> ");if(srv == null) { // connect & getwhile(!in.readLine().equals("bye")) {srv = (Sample14IF)Naming.lookup(f0.getText());
System.out.println(srv.toString());
if(srv == null) l1.setText("Connect failed.");System.out.print("command> ");else { l1.setText("OK."); data = srv.get(); }}} else { // save
System.exit(0);srv.put(data); srv = null; l1.setText("save.");}}}} catch(Exception ex) { l1.setText("!" + ex); }
}
□ 解説:
});
b1.addActionListener(new ActionListener() {•public void actionPerformed(ActionEvent evt) {• サーバは最初 MyData オブジェクトを与えて初期化
try {if(data != null)
する。

9f1.setText(data.exec(f1.getText()));} catch(Exception ex) { l1.setText("!" + ex); }}});}public static void main(String[] args)
throws Exception {new Sample14().setVisible(true);
}
}

□ 解説:

• 部品の配置などはこれまでと同様。
• ボタン b0 の動作では、srv が null であれば f0 に指定された文字列を使って Naming.lookup() を呼びサーバを取得。さらにサーバから Data オブジェクト取得。

• srv が null でないならそこに Data オブジェクトを書き込んで srv を null に。
• ボタン b1 の方はこれまでと同様ただし data に対し
て実行。
□ 演習:

この例題を動かせ。文字列を加工してもサーバ側には反映されていなくて、最後に書き戻すと反映されることを確認すること。
□ 演習:

クラス MyData の exec() の中身を変更して自分独自のものにしてみよ。続いて自分のサーバを動かし、動作を確認。OK なら、その rmi:

URI をホワイトボー
ドに書き、他人に使ってみてもらう (また自分も他人のものを動かしてみる)。

6

まとめ□ 分散システムの技術として、C/S モデル、分散オブジェ
クトモデル、ORB、Java RMI、モビリティなどの話題をひととおり取り上げた。実際にお仕事に使うとなるとさらに大変だが、世の中は結構頑張っていると思いますよね?

(しかし今日では Web アプリが主流に…)

10