分散共有空間(2)

並列分散ソフトウェア

                                       電子・情報工学系
                                       新城 靖
                                       <yas@is.tsukuba.ac.jp>

このページは、次の URL にあります。
http://www.hlla.is.tsukuba.ac.jp/~yas/sie/pdsoft-2001/2002-02-14
あるいは、次のページから手繰っていくこともできます。
http://www.hlla.is.tsukuba.ac.jp/~yas/sie/
http://www.is.tsukuba.ac.jp/~yas/index-j.html
http://www.hlla.is.tsukuba.ac.jp/~yas/index-j.html

■今日の重要な話

参考文献

Eric Freeman, Susanne Hupfer, and Ken Arnold: "JavaSpaces Principles, Patterns, and Practice", Addison Wesley Publishing Company (1999/06). ISBN: 0201309556

■JavaSpaces

Linda のタプル空間 の考え方を Java で実現したもの。

interface JavaSpace を実現したオブジェクト

----------------------------------------------------------------------
Linda	JavaSpace	説明
----------------------------------------------------------------------
out	write		タプルをタプル空間内に生成する。
in	take		タプルを取り去る
rd	read		in/takeと似ているが、タプルがタプルスペースに残る。
----------------------------------------------------------------------

ライブタプル(eval命令)は、ない。

JavaSpaces が提供する空間の特徴

◆interface Entry

空間に置くことができるオブジェクトは、interface Entry を implements し たもの。

net/jini/core/entry/Entry.java:
public interface Entry extends java.io.Serializable {
}

◆interface JavaSpace

net/jini/space/JavaSpace.java:
public interface JavaSpace {
    Lease write(Entry entry, Transaction txn, long lease)
        throws TransactionException, RemoteException;
    long NO_WAIT = 0;
    Entry read(Entry tmpl, Transaction txn, long timeout)
        throws UnusableEntryException, TransactionException, 
               InterruptedException, RemoteException;
    Entry readIfExists(Entry tmpl, Transaction txn, long timeout)
        throws UnusableEntryException, TransactionException, 
               InterruptedException, RemoteException;
    Entry take(Entry tmpl, Transaction txn, long timeout)
        throws UnusableEntryException, TransactionException, 
               InterruptedException, RemoteException;
    Entry takeIfExists(Entry tmpl, Transaction txn, long timeout)
        throws UnusableEntryException, TransactionException, 
               InterruptedException, RemoteException;
    EventRegistration
        notify(Entry tmpl, Transaction txn, RemoteEventListener listener,
               long lease, MarshalledObject handback)
        throws TransactionException, RemoteException;
    Entry snapshot(Entry e) throws RemoteException;
}

notify() では、マッチするテンプレートが write された時、 RemoteEventListener の notify(emoteEvent theEvent) が呼ばれる。

snapshot() では、エントリのスナップショットが返される。元のエントリが 更新されても、変化しない。read(), take() のテンプレート用。

◆PDSoftSpaceFinder

JavaSpace オブジェクトの作成方法やプログラム中での空間の入手方法は、実 行環境に依存する。PDSoftSpaceFinder は、この講義のために、JavaSpace オ ブジェクトを提供するクラスである。これは、Sun 提供している参照実現で、 rmiregistry 経由でJavaSpace オブジェクトを入手する方法を使っている。

public class PDSoftSpaceFinder extends java.lang.Object {
    public PDSoftSpaceFinder();
    public static net.jini.space.JavaSpace getSpace(java.lang.String);
    public static net.jini.space.JavaSpace getSpace();
}

■Hello

◆class Message

空間に String を置くためのクラス。参考文献の1章の例からパッケージを抜 いたもの。

----------------------------------------------------------------------
   1: // From the JavaSpaces book
   2: //package jsbook.chapter1.helloWorld;
   3: 
   4: import net.jini.core.entry.Entry;
   5: 
   6: public class Message implements Entry {
   7:     public String content;
   8: 
   9:     public Message() {
  10:     }
  11: }
----------------------------------------------------------------------

テンプレートでオブジェクトが null なら、ワイルドカードの意味になる。

◆HelloWriter

空間にタプルを書込むプログラム

----------------------------------------------------------------------
   1: //
   2: // HelloWriter.java
   3: // 
   4: 
   5: import net.jini.space.JavaSpace;
   6: 
   7: class HelloWriter {
   8:     public static void main(String[] argv)
   9:     {
  10:         JavaSpace space = PDSoftSpaceFinder.getSpace();
  11:         if( space == null )
  12:         {
  13:             System.err.println("No JavaSpace found.");
  14:             System.exit( 1 );
  15:         }
  16:         Message msg = new Message();
  17:         msg.content = "Hello" ;
  18:         try
  19:         {
  20:             space.write(msg, null, net.jini.core.lease.Lease.FOREVER);
  21:             System.out.println("HelloWriter: wrote["+msg.content+"]");
  22:         }
  23:         catch( Exception e )
  24:         {
  25:             System.err.println("JavaSpace write error "+e.getMessage());
  26:             e.printStackTrace();
  27:             System.exit( -1 );
  28:         }
  29:         System.exit( 0 );
  30:     }
  31: }
----------------------------------------------------------------------

    Lease write(Entry entry, Transaction txn, long lease)
        throws TransactionException, RemoteException;
トランザクションは、複数の操作をグループ化するもの。null を 指定すれば、その機能は使われない。

lease は、時間を指定する。その時間だけは、空間がそのエントリを記憶して いる。空間が Garbage Collection に使う。Lease.FOREVER は、無限に覚えて いることを意味する。単位は、ミリ秒。

(transient-outrigger.jar では、サーバを落とすと消える。)

◆HelloReader

空間からタプルを読込むプログラム。タプルは、空間に残される。

----------------------------------------------------------------------
   1: //
   2: // HelloReader.java
   3: // 
   4: 
   5: import net.jini.space.JavaSpace;
   6: 
   7: public class HelloReader {
   8:     public static void main(String[] argv)
   9:     {
  10:         JavaSpace space = PDSoftSpaceFinder.getSpace();
  11:         if( space == null )
  12:         {
  13:             System.err.println("No JavaSpace found.");
  14:             System.exit( 1 );
  15:         }
  16: 
  17:         Message template = new Message();
  18:         Message result;
  19:         try
  20:         {
  21:             result = (Message)space.read(template, null, Long.MAX_VALUE);
  22:             System.out.println("HelloReader: read ["+result.content+"]");
  23:         }
  24:         catch( Exception e )
  25:         {
  26:             System.err.println("JavaSpace read error "+e.getMessage());
  27:             e.printStackTrace();
  28:             System.exit( -1 );
  29:         }
  30: 
  31:         System.exit( 0 );
  32:     }
  33: }
----------------------------------------------------------------------

    Entry read(Entry tmpl, Transaction txn, long timeout)
        throws UnusableEntryException, TransactionException, 
               InterruptedException, RemoteException;
read の最初の引数は、テンプレートである。 read は、空間からテンプレートとマッチするエントリを読み出す。

次の2つの規則を満たした時に、テンプレートとエントリはマッチする

  1. テンプレートの型が、エントリの型とまったく同じである、または、エ ントリの型の部分型(サブクラス)である。
  2. テンプレートの中の各フィールドが、対応するエントリ中のフィールドと マッチする。
    • テンプレートのフィールドが null である場合、 対応するエントリのフィールドとマッチする。
    • テンプレートのフィールドが null でない場合、 テンプレートとエントリの対応するフィールドが 「同じ値」の場合にマッチする。
null は、ワイルドカード(*)の意味する。 任意のオブジェクトとマッチする。Linda の ? に相当する。

public class Vegetable implements Entry {
}

public class Fruit implements Entry {
}

public class Apple extends Fruit {
}

public class Orange extends Fruit {
}

「同じ値」は、serialize してバイトレベルで同じという意味。エントリは、 空間にある時にはserialize された形で保存されている。

null を和いるとカードに使う問題点は、「本当に null の値を持つエントリ 探す」ということができないこと。 区別したい時には、Boolean を添える。


public class NotePtr implements Entry {
    public Boolean ptrIsNull;
    public Node ptr;
}

...

NodePtr template = new NodePtr();
template.ptrIsNull = new Boolean(true);
template.ptr = null; // for completeness; null by default

null をワイルドカードに使ったので、空間に置くエントリのフィールドは、 オブジェクトにする。int, boolean, float, double などは、Integer, Boolean, Float, Double などの wrappe クラスを使う。

read() は、マッチするエントリがなければ、timeout するまで待つ。 Long.MAX_VALUE は、無限に待つことを意味する。待ちたくない時には、 JavaSpaces.NO_WAIT を使うか、raedIfExists() を使う。

注意:連続する read() が同じオブジェクトを返す保証はない。

◆HelloTaker

空間からタプルを読込むプログラム。タプルは、空間から取り去られる。

----------------------------------------------------------------------
   1: //
   2: // HelloTaker.java
   3: // 
   4: 
   5: import net.jini.space.JavaSpace;
   6: 
   7: public class HelloTaker {
   8:     public static void main(String[] argv)
   9:     {
  10:         JavaSpace space = PDSoftSpaceFinder.getSpace();
  11:         if( space == null )
  12:         {
  13:             System.err.println("No JavaSpace found.");
  14:             System.exit( 1 );
  15:         }
  16: 
  17:         Message template = new Message();
  18:         Message result;
  19:         try
  20:         {
  21:             result = (Message)space.take(template, null, Long.MAX_VALUE);
  22:             System.out.println("HelloTaker: took ["+result.content+"]");
  23:         }
  24:         catch( Exception e )
  25:         {
  26:             System.err.println("JavaSpace read error "+e.getMessage());
  27:             e.printStackTrace();
  28:             System.exit( -1 );
  29:         }
  30: 
  31:         System.exit( 0 );
  32:     }
  33: }

% diff HelloReader.npr HelloTaker.npr
2c2
<    2: // HelloReader.java
---
>    2: // HelloTaker.java
7c7
<    7: public class HelloReader {
---
>    7: public class HelloTaker {
21,22c21,22
<   21:             result = (Message)space.read(template, null, Long.MAX_VALUE);
<   22:             System.out.println("HelloReader: read ["+result.content+"]");
---
>   21:             result = (Message)space.take(template, null, Long.MAX_VALUE);
>   22:             System.out.println("HelloTaker: took ["+result.content+"]");
% 
----------------------------------------------------------------------

take() は、read() と同じだが、エントリを空間から取り去る所が異なる。 複数の take() が重なったとしても、 エントリは1つにしか取られない。

◆コンパイル

-classpath に . と transient-outrigger.jar を含める。

----------------------------------------------------------------------
% wget http://www.hlla.is.tsukuba.ac.jp/~yas/sie/pdsoft-2001/2002-02-14/ex/PDSoftSpaceFinder.java [←]
% wget http://www.hlla.is.tsukuba.ac.jp/~yas/sie/pdsoft-2001/2002-02-14/ex/Message.java [←]
% wget http://www.hlla.is.tsukuba.ac.jp/~yas/sie/pdsoft-2001/2002-02-14/ex/HelloWriter.java [←]
% wget http://www.hlla.is.tsukuba.ac.jp/~yas/sie/pdsoft-2001/2002-02-14/ex/HelloReader.java [←]
% wget http://www.hlla.is.tsukuba.ac.jp/~yas/sie/pdsoft-2001/2002-02-14/ex/HelloTaker.java [←]
% wget http://www.hlla.is.tsukuba.ac.jp/~yas/sie/pdsoft-2001/2002-02-14/ex/Makefile [←]
% make clean [←]
% make hello [←]
javac -classpath .:/home/yshinjo/jini1_1/lib/transient-outrigger.jar PDSoftSpaceFinder.java
Note: PDSoftSpaceFinder.java uses or overrides a deprecated API.
Note: Recompile with -deprecation for details.
javac -classpath .:/home/yshinjo/jini1_1/lib/transient-outrigger.jar Message.java
javac -classpath .:/home/yshinjo/jini1_1/lib/transient-outrigger.jar HelloWriter.java
javac -classpath .:/home/yshinjo/jini1_1/lib/transient-outrigger.jar HelloReader.java
javac -classpath .:/home/yshinjo/jini1_1/lib/transient-outrigger.jar HelloTaker.java
% []
----------------------------------------------------------------------

◆実行

ウインドウを4枚開く
  1. rmiregistry 用
  2. TransientSpace 用(JapaSpaceのサーバ)
  3. Writer 用
  4. Reader/Taker用


----------------------------------------------------------------------
% make run-rmiregistry [←]
rmiregistry `id | sed -e 's;uid=;;' -e 's;(.*;;'`
(最後に ^C で止める)
----------------------------------------------------------------------
rmiregistry で使うポート番号は、ぶつからないように、各自の uid を使う。 rmiregistry は自動的に終了しないので、実験が終わったら ^C (Control-C) で殺す。

注意: sakura に rmiregistry を動かしっ放しの人がいる。この実験を始める 前にps で調べて kill しておくこと。


----------------------------------------------------------------------
% make  run-TransientSpace [←]
java -Djava.security.policy=/home/yshinjo/jini1_1/policy/policy.transient-outrigger -Djava.rmi.server.codebase=file:/home/yshinjo/jini1_1/lib/outrigger-dl.jar -classpath .:/home/yshinjo/jini1_1/lib/transient-outrigger.jar -Dcom.sun.jini.use.registry=yes -Dcom.sun.jini.outrigger.spaceName=JavaSpace -Dcom.sun.jini.rmiRegistryPort=`id | sed -e 's;uid=;;' -e 's;(.*;;'` -jar /home/yshinjo/jini1_1/lib/transient-outrigger.jar
Warning: file url in codebase component may cause problems (file:/home/yshinjo/jini1_1/lib/outrigger-dl.jar)

Warning: Your are using the com.sun.jini.use.registry property
in order to bind this Outrigger server into an RMI registry.
Direct support for binding Outrigger servers into RMI registries
is going to be removed in a future version of the
Jini(TM) Software Kit (JSK).
(最後に ^C で止める)
----------------------------------------------------------------------
残りの一方のウインドウで Writer を動作させる。

----------------------------------------------------------------------
% set prompt="Writer% " [←]
Writer% make run-HelloWriter
HelloWriter: wrote[Hello]
Writer% make run-HelloWriter
HelloWriter: wrote[Hello]
Writer%
----------------------------------------------------------------------
最後のウインドウで Reader や Taker を動作させる。

----------------------------------------------------------------------
% make -n run-HelloReader [←]
java -Djava.security.policy=/home/yshinjo/jini1_1/policy/policy.transient-outrigger -Djava.rmi.server.codebase=file:/home/yshinjo/jini1_1/lib/outrigger-dl.jar -classpath .:/home/yshinjo/jini1_1/lib/transient-outrigger.jar -Dcom.sun.jini.use.registry=yes -Dcom.sun.jini.outrigger.spaceName=JavaSpace -Dcom.sun.jini.rmiRegistryPort=`id | sed -e 's;uid=;;' -e 's;(.*;;'` HelloReader
% make  run-HelloReader [←]
HelloReader: read [Hello]
% make run-HelloReader [←]
HelloReader: read [Hello]
% make run-HelloReader [←]
HelloReader: read [Hello]
% make run-HelloReader [←]
HelloReader: read [Hello]
% []
% make run-HelloTaker [←]
HelloTaker: took [Hello]
% make run-HelloTaker [←]
HelloTaker: took [Hello]
% make run-HelloTaker [←]
^Cmake: *** [run-HelloTaker] Error 130

% []
----------------------------------------------------------------------
3回目の take は止まる。別ウインドウで write すれば、先に進む。

タプルがない状態先に take/read すると、止まる。この状態で、write すれ ば、read/take が終了する。

◆serializationの効果

エントリが空間にある時には、serialize された形で保存されている。

read() でのマッチングは、serialize された形で比較される。

空間に入れた出すと、オブジェクトが増えることがある。


public class E1 implements Entry {
    public Integer obj1;
    public Integer obj2;
}

Integer obj = Integer(0);
E1 e = new E1();
e.obj1 =obj;
e.obj2 =obj;

引数なしのコンストラクタが必要である。

read(), take() でのエントリの復元

  1. serialize されたエントリのコピーを得る
  2. 1. から型を得る
  3. 引数なしのコンストラクタでオブジェクトを作る
  4. 1. から、public のフィールドを deserialize する。
  5. 4. の値を、オブジェクトのフィールドに代入する。
実は、Entry 全体は Serializable である必要はなかった。

read(), take() のテンプレートがループの中で不変な場合、 snapshot() を作ると効率が良くなる。

◆セキュリティ

古い (jini1_1) の policy/policy.transient-outrigger は、弱い。

◆PDSoftSpaceFinderの詳細

"//localhost:rport/JavaSpace" という名前で rmiregistory を探す。

----------------------------------------------------------------------
   1: //
   2: // PDSoftSpaceFinder.java
   3: //
   4: 
   5: import java.rmi.*;
   6: 
   7: import net.jini.space.JavaSpace;
   8: import com.sun.jini.mahout.binder.RefHolder; // {transient-outrigger,...}.jar
   9: 
  10: public class PDSoftSpaceFinder {
  11:     public static JavaSpace getSpace(String name)
  12:     {
  13:         try
  14:         {
  15:             String rport ;
  16:             if( System.getProperty("com.sun.jini.use.registry") == null )
  17:             {
  18:                 System.err.println("Run with -Dcom.sun.jini.use.registry=yes");
  19:             }
  20:             else if( (rport=System.getProperty("com.sun.jini.rmiRegistryPort")) == null )
  21:             {
  22:                 System.err.println("Run with -Dcom.sun.jini.rmiRegistryPort=myUID");
  23:             }
  24:             else
  25:             {
  26:                 String url = "//localhost:"+rport+"/"+name ;
  27:                 RefHolder rh = (RefHolder)Naming.lookup(url);
  28:                 return (JavaSpace)rh.proxy();
  29:             }
  30:             // thru
  31:         }
  32:         catch( Exception e )
  33:         {
  34:             System.err.println(e.getMessage());
  35:         }
  36:         return null;
  37:     }
  38:     
  39:     public static JavaSpace getSpace()
  40:     {
  41:         String spaceName = System.getProperty("com.sun.jini.outrigger.spaceName");
  42:         if( spaceName == null )
  43:         {
  44: //          System.err.println("run with -Dcom.sun.jini.outrigger.spaceName=JavaSpace");
  45: //          return( null );
  46:             spaceName = "JavaSpace" ;
  47:             // thru
  48:         }
  49:         return getSpace( spaceName );
  50:     }
  51: }
----------------------------------------------------------------------

■課題

次の課題から1つを選んで提出しなさい。問題を難しい方に変えてもよい。締 め切りは、2002/02/27 (水曜日) 18:00 とする。(23:59:59 ではない)。

レポートは、次のような形式の電子メールで送ること。

----------------------------------------------------------------------
To: yas@is.tsukuba.ac.jp
Subject: [pdsoft/space] <内容に関したサブジェクト>

学籍番号 000000 (各自の学籍番号で置き換える)
名前 漢字の名前

<内容>
----------------------------------------------------------------------

★WWWアクセスカウンタ

WWW のアクセス・カウンタを、実現しなさい。

初期化:

Stringurl = argv[0];
webCounter c = new webCounter(url,0);
space.write(webCounter,null,Lease.FOREVER);

アクセスされた時:

増やすには、take して 増やして、write する。

★Linda の inストリーム

1人が読むとデータが消えるてしまうようなストリームを実現しなさい。
("stream",0,val0)
("stream",1,val1)
("stream",2,val2)
...

ポインタ
("stream","head",0)
("stream","tail",0)

ストリームに要素を追加:
int index;
in("stream","tail",?index);
out("stream","tail",index+1);
out("stream",index,new_element);

ストリームから要素を取り出す:
int index;
in("stream","head",?index);
out("stream","head",index+1);
in("stream",index,?element);

これは、複数source、複数sink。

source、sinkが1つなら、head, tail をタプルスペースに置かなくてもよい。

★Linda の readストリーム

一人の書き手が作成したデータを、複数の読み手でアクセスできるようなスト リームをを実現しなさい。

head の代わりに、局所変数でアクセスする。

ストリームから要素を取り出す:

int index=0 ;
while( ... )
{
   rd("stream",index++,?element);
}

★マスタ・スレーブ

マスタ・スレーブで並列処理を行うプログラムを JavaSpace を使って書き直 しなさい。
↑[もどる] ←[2月07日] ・[2月14日] →[2月21日]
Last updated: 2002/02/14 05:24:38
Yasushi Shinjo / <yas@is.tsukuba.ac.jp>