オープンソース・ソフトウェアの開発とダウンロード

JBossCache チュートリアル(日本語)の表示

カテゴリ(タグ)ツリー

ファイル情報

カテゴリ(タグ)
JBossCache
ファイル名
JBossCacheTutorial
最終更新
2006-03-10 03:43
種類
HTML
作成者
福居毅至
概要
JBossCache チュートリアル(日本語)
言語
日本語
翻訳する

JBoss TreeCache/TreeCacheAop チュートリアル

[FAMILY Given]

[FAMILY Given]


1. はじめに

JBoss TreeCache (および TreeCacheAop) は (同期または非同期の) レプリケーション可能でトランザクショナルなキャッシュであり、アスペクト指向プログラミング (AOP) をサポートします。 このチュートリアルでは AOP 有りの場合と無しの場合のそれぞれのキャッシュの使い方をデモします。 詳しい API の使い方は TreeCache と TreeCacheAop それぞれのユーザーマニュアル (TreeCache および TreeCacheAop) を参照してください。

2. 取り扱う内容

  • キャッシュの生成と変更

  • レプリケーション

  • AOP キャッシュ

  • トランザクション

3. 設定

まず、 ここからスタンドアロンの TreeCache のコードをダウンロードします。 展開するとディレクトリツリーができます。(ここではツリーのルートが jboss-cache であるとします)

設定ファイルが etc ディレクトリの下に有ります。 これらの各種設定ファイルを書き換えることで TreeCache の挙動を変更することができます。

  • log4j.xml。ログ出力。 ログレベルを切り替えたりログファイルのディレクトリ (デフォルトは/tmp/test.log) を変更したりできます。

  • replSync-service.xml。TreeCache の設定ファイルです。 (ファイル名は変更できます。PropertyConfigurator で読み込む対象のファイル名を指定します。) このファイルの設定ではレプリケーション有り、同期有り、トランザクション有りのキャッシュになっています。 デフォルトの DummyTransactionManager はアイソレーションレベル REPEATABLE_READ で使用されます。 設定パラメタの詳細については Treecache を参照してください。 このファイルは BSH (BeanShell。 Java 互換の軽量スクリプト言語) スクリプト内でキャッシュを設定するのに使われます。

  • jboss-aop.xml。サンプルの POJO クラスである Person や Address の AOP ポイントカットとアドバイスの定義です。 自分で作ったクラスに AOP を適用する方法の詳細についてはTreeCacheAopを参照してください。 このファイルはプロセスの起動時に読み込まれます。

4. スクリプト

このチュートリアルで必要となるスクリプトファイル (インストールディレクトリ直下に有る) は以下の通りです。

  • build.sh (DOS 用は build.bat)。ant の実行をラップするシンプルなスクリプトです。 sh build.shとタイプすればヘルプを見られます。以降では UNIX 版にのみ言及しますが、対応する DOS 版も有ることを覚えておいてください。 次に説明する runDemoShell についても同様です。

  • runDemoShell.sh。 BeanShell の実行をラップするシンプルなスクリプトです。 レプリケートされたキャッシュをコマンドラインで対話的に操作するのに使います。

  • plain.bsh。キャッシュをインスタンス化して設定する Java コードです。 説明用のキャッシュエントリの生成も行います。

  • aop.bsh。AOP キャッシュをインスタンス化して設定する Java コードです。 更に、説明用の POJO (plain old Java object) クラス (Person や Address など) のセットアップも行います。

  • aopWithTx.bsh。トランザクションコンテキストもインスタンス化すること以外は aop.bsh と同じ。

5. 説明用 POJO

TreeCacheAop のデモに使われる説明用の POJO クラスは PersonAddress です。 これらは examples/org/jboss/cache/aop ディレクトリに有ります。 PersonString age, Address addr, List languages などの属性を持ちます。 POJO インスタンスをキャッシュに納めると、素の get/set メソッド呼び出しがキャッシュによってインターセプトされるようになるということをデモします。

以下は PersonAddress のクラス定義の一部です。

public class Person { 
   String name=null; 
   int age=0; 
   Map hobbies=null; 
   Address address=null; 
   Set skills; 
   List languages; 

   public String getName() { return name; } 
   public void setName(String name) { this.name=name; } 
   ... 
}
public class Address { 
   String street=null; 
   String city=null;
   int zip=0; 
   
   public String getStreet() { return street; } 
   public void setStreet(String street) { this.street=street; } 
   ... 
}

6. デモ

デモを実行するには、少なくとも2つウィンドウが必要となります。 一つはキャッシュの内容の観察 (と AOP を伴わない操作) のため、もう一つはキャッシュを直接操作するため。 もちろん、複数のメンバーに対してキャッシュのレプリケーションが働いているところを見るために GUI ウィンドウを複数開くこともできます。 スクリプトは配布パッケージ (jboss-cache-dist.zip) を展開した後で jboss-cache ディレクトリ直下で実行する必要が有ります。 GUI の制限の為、以下のことに注意してください。

  • それぞれのデモにおいては、設定を毎回一からやりなおすのがベストです。
  • BSH ウィンドウを更新したときには GUI を手動でリフレッシュする必要があります。普通はそのとき見ているノードとは別のノードをクリックしてから元のノードをクリックすることでリフレッシュできます。
  • GUI ウィンドウ上でキャッシュの内容を変更する (たとえば cache.printDetails() を通じて) と、BSH で表示するキャッシュの内容にも反映されますが、TreeCacheAop のデモでは同じことはできません。したがって、キャッシュの内容の変更は BSH ウィンドウ上でのみ行えます。

ここで実行する2つのプログラムは以下の通りです。

  • 1つ目の GUI ウィンドウで sh build.sh と打てば使用できるコマンドが見られます。 GUI を起動するには sh build.sh run.aop.demo と入力します。それにより TreeCacheAop の GUI が立ち上がります。 ノードをクリックすればその内容が表示されます。 非 AOP のキャッシュエントリのノードの内容に追加したりノードの内容を書き換えたりすることもできます。 今のところ、この GUI では String しか受け付けないので、AOP キャッシュに対する GUI からの操作は(String 型への操作以外は)機能しません。

  • 2つ目のウィンドウで runShellDemo.sh とタイプすると BeanShell のコマンドシェルが立ち上がり、対話的に Java コマンドを実行できます (抜けるためには Windows では ^Z を、Unix では ^D を押します)。 その状態からキャッシュの各種の機能をデモする Java コード (plain.bsh、aop.bsh、aopWithTx.bsh) のスクリプトを読み込むことができます。詳細は以下で。

7. 素のキャッシュ

シェルが立ち上がれば、キャッシュにデータを挿入するスクリプトを実行したりコマンドラインから手動でキャッシュへのデータ挿入を実行したりできます。 スクリプトを実行するには、BSH の対話シェル上で sourceRelative("plain.bsh"); とタイプします。 これをうまく動作させるには、カレントディレクトリを plain.bsh のあるディレクトリにする (あるいは plain.bsh をフルパスで指定する) 必要が有ります。 このスクリプトは GUI 上にレプリケートされるキャッシュエントリを生成します。 (スクリプト実行後に beanshell ウィンドウで show() とタイプしてみましょう) このスクリプトの一部を示します。

import org.jboss.cache.*;
show(); // bean shell の verbose モード
TreeCache tree = new TreeCache(); 
PropertyConfigurator config = new PropertyConfigurator(); // tree cache の設定。クラスパスに入っている必要が有る
config.configure(tree, "META-INF/replSync-service.xml"); 
tree.startService(); // tree cahce の起動
tree.put("/a/b/c", "ben", "me"); // キャッシュエントリ生成
// ノード "/a/b/c" はまだ無ければ生成される

/a/b/c という新しいエントリが作られたことを GUI で確認してください。ノード c をクリックすると中身を見られます。 GUI からその内容を変更することもできます。別のノードを生成するには、 シェル上でたとえば以下のように入力します。

tree.put("/a/b/c/d", "JBoss", "Open Source");
tree.get("/a/b/c/d", "JBoss");

8. CacheAop

シェルを立ち上げたら sourceRelative("aop.bsh"); とタイプしてシェルスクリプトを実行します。 aop.bsh はキャッシュのインスタンス化、設定、各エントリの生成というステップを目に見えるようにしてくれます。 以下はコードの一部です。

import org.jboss.cache.PropertyConfigurator;
import org.jboss.cache.aop.TreeCacheAop; 
import org.jboss.cache.aop.test.Person;
import org.jboss.cache.aop.test.Address;
show(); // verbose mode for bean shell
TreeCacheAop tree = new TreeCacheAop(); 
PropertyConfigurator config = new PropertyConfigurator(); // tree cache の設定
config.configure(tree, "META-INF/replSync-service.xml"); 
Person joe = new Person(); // Person をインスタンス化して joe と名付ける
joe.setName("Joe Black"); 
joe.setAge(31); 
Address addr = new Address(); // Address をインスタンス化して addr と名付ける
addr.setCity("Sunnyvale"); 
addr.setStreet("123 Albert Ave"); 
addr.setZip(94086); joe.setAddress(addr); // addr の参照をセットする
tree.startService(); // tree cache 起動
tree.putObject("/aop/joe", joe); // aop 適用オブジェクト(とそのサブオブジェクト)をキャッシュに追加
// aop 適用されたので、get/set メソッドの呼び出しが自動的にキャッシュの内容に反映される
joe.setAge(41);

オブジェクト (およびそれが依存するオブジェクト群) をキャッシュに納めるのに必要な API は putObject です。 二つ目のウインドウでの実行が終わったら一つ目の GUI ウィンドウに /aop/joe/address というエントリが現れたことを確認してください。 ツリーのノードのどれかをクリックすると、そのノードに関連付けられた値が表示されます。

AOP の動作を見るための次のステップとして get/set メソッドを直接実行してみてください。 キャッシュに何か値を入れることを恐れる必要は有りません。 たとえば、シェル上で joe.setAge(20); と実行すると GUI 上の age フィールドの表示が自動的に更新されることが確認できます。 次にオブジェクトグラフのレプリケーションのデモです。 Joe の address を変更するとキャッシュが自動的にその変更内容を伝えることが分かります。 たとえば、addr.setCity("San Jose"); と対話シェル上でタイプすれば GUI 上で address が変更されたことを見ることができます。

最後に TreeCacheAop がコレクションクラス(List, Map, Set など)の get/set にも対応しているところを見ます。 たとえば、シェルコマンドライン上で以下をタイプします。

ArrayList lang = new ArrayList(); 
lang.add("Ensligh");
lang.add("Mandarin"); 
joe.setLanguages(lang);

9. トランザクション対応 CacheAop

TreeCache のトランザクションの働きを見てみましょう。 セットアップの手順は先ほどのセクションとほぼ同じです。 違うのは bsh のスクリプト aop.bsh を読み込む代わりに aopWithTx.bsh を読み込むことだけです。以下はそのスクリプトの一部です。

import org.jboss.cache.PropertyConfigurator; 
import org.jboss.cache.aop.TreeCacheAop; 
import org.jboss.cache.aop.test.Person;
import org.jboss.cache.aop.test.Address;// Tx インポート
import javax.transaction.UserTransaction; import javax.naming.*; 
import org.jboss.cache.transaction.DummyTransactionManager; 
show(); // bean shell verbose モード
// transaction manager のセットアップ
DummyTransactionManager.getInstance(); 
Properties prop = new Properties(); 
prop.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.cache.transaction.DummyContextFactory");
UserTransaction tx = (UserTransaction)new InitialContext(prop).lookup("UserTransaction");
TreeCacheAop tree = new TreeCacheAop();
PropertyConfigurator config = new PropertyConfigurator(); // tree cache の設定
config.configure(tree, "META-INF/replSync-service.xml"); 
joe = new Person();
joe.setName("Joe Black"); 
joe.setAge(31); 

Address addr = new Address();
addr.setCity("Sunnyvale"); 
addr.setStreet("123 Albert Ave");
addr.setZip(94086); 
joe.setAddress(addr); 

tree.startService(); // tree cache 起動
tree.putObject("/aop/joe", joe); // aop 適用オブジェクト追加
// aop 適用されたので、get/set メソッドの呼び出しが自動的にキャッシュの内容に反映される
// トランザクションにも対応している
tx.begin(); 
joe.setAge(41);
joe.getAddress().setZip(95124); 
tx.commit();

この例ではデフォルトの DummyTransactionManager が使われています。

tx.begin(); 
addr.setZip(95131);
tx.rollback();

10. CacheLoader の例

これ以降のすべての例は単体で配布されている JBossCahce に基づいています。 ZIP ファイルが jboss-cache ディレクトリ以下に展開されていることが前提となっています。

10.1. CacheLoader を使用するローカルキャッシュ

このデモではキャッシュローダ付きのローカルな TreeCacheAop をお見せします。 POJO をキャッシュに挿入すると、その POJO が透過的に CacheLoader によってセーブされます。

これを実行するには、jboss-cache/output/etc/META-INF/oodb-service.xml を変更する必要があります。 CacheLoaderConfig を変更して有効なディレクトリ (無ければ作ってください) を指すようにしてください。

<attribute name="CacheLoaderConfig">
    location=c:\\tmp\\oodb
</attribute>

その後 beanshell を起動して oodb.bsh を読み込みます。

bela@laptop /cygdrive/c/jboss-cache
$ ./runShellDemo.sh
BeanShell 1.3.0 - by Pat Niemeyer (pat@pat.net)
bsh % sourceRelative("oodb.bsh");
interceptor chain is:
class org.jboss.cache.interceptors.CallInterceptor
class org.jboss.cache.interceptors.CacheLoaderInterceptor
class org.jboss.cache.interceptors.TransactionInterceptor
<null>
bsh %

次に Person のインスタンスを生成して address やその他のフィールドをセットします。

bsh % p=new Person();
<name=null, age=0, hobbies=, address=null, skills=null, languages=null>
bsh % p.age=3;
<3>
bsh % p.name="Michelle";
<Michelle>
bsh % addr=new Address();
<street=null, city=null, zip=0>
bsh % addr.city="San Jose";
<San Jose>
bsh % addr.zip=95124;
<95124>
bsh % addr.street="1704 Almond Blossom Lane";
<1704 Almond Blossom Lane>
bsh % p.setAddress(addr);
bsh % p;
<name=Michelle, age=3, hobbies=, address=street=1704 Almond Blossom Lane, city=San Jose, zip=95124, skills=null, languages=null>
bsh %

Person オブジェクトおよびフィールドの値、そのサブオブジェクトはセーブされました。 beanshell を終了して再起動してみましょう。 生成した Person のインスタンスは "p" という名前を与えられていたので、その名前で再び取り出すことができます。

bela@laptop /cygdrive/c/jboss-cache
$ ./runShellDemo.sh
BeanShell 1.3.0 - by Pat Niemeyer (pat@pat.net)
bsh % sourceRelative("oodb.bsh");
interceptor chain is:
class org.jboss.cache.interceptors.CallInterceptor
class org.jboss.cache.interceptors.CacheLoaderInterceptor
class org.jboss.cache.interceptors.TransactionInterceptor
<null>
bsh % tree;
</>
bsh % p=tree.getObject("p");
<name=Michelle, age=3, hobbies=, address=street=1704 Almond Blossom Lane, city=San Jose, zip=95124, skills=null, languages=null>
bsh % tree;
</p
    /address
>
bsh %

興味深い点は、キャッシュは最初の時点では空("/")であったということです。 "p" をロードして初めてそれは実体化されました (遅延ロード)。 "p" の内容として先にセーブしたのと同じ内容がデータストアから読み込まれたことが確認できるはずです。

10.2. データストアを共有するレプリケートキャッシュ

この例で実行するシナリオは JBossCache のドキュメントに書かれているものと同じです。 2 つの別々のノードがその内容をお互いにレプリケートします。 更に、それらは同じデータストアを指しています。 設定ファイルは jboss-cache/output/etc/META-INF/replAsyncSharedCacheLoader-service.xml です:

<!-- クラスタに加わるときに状態を取得するかどうか -->
<attribute name="FetchStateOnStartup">false</attribute>
<attribute name="CacheLoaderClass">org.jboss.cache.loader.FileCacheLoader</attribute>
<attribute name="CacheLoaderConfig">
    location=c:\\tmp
</attribute>
<attribute name="CacheLoaderShared">true</attribute>
<attribute name="CacheLoaderPreload">/</attribute>
<attribute name="CacheLoaderFetchTransientState">false</attribute>
<attribute name="CacheLoaderFetchPersistentState">true</attribute>

FetchStateOnStartup を false にすると新たに起動されるキャッシュは状態の読み込みをしません (一時的にも永続的にも)。 したがって、CacheLoaderFetchTransientStateCacheLoaderFetchPersistentState とは無視されます。 CacheLoaderSharedtrue にすると両方のノードが同じデータストアを共有します。 この例ではデータストアはc:\tmpにあると仮定しています (2 つのノードが同じファイルシステムにアクセスしているという前提です)。 c:\tmp が有ることを確認してください。 無ければ存在する別のディレクトリを指定してください。

この設定は本質的には 2 つの「コールドな」ノードを提供するものです。 「コールドな」とは、新しいキャッシュの内容はすべてデータストア上に有り、アクセスの有ったときに CacheLoader を通じて遅延ロードされるという意味です。 ただし、CacheLoaderPreload がツリー全体のルートである "/" を指している場合にはこのことは正しくありません。 つまり、キャッシュの内容は再帰的にプリロードされます。 キャッシュ内に大量のデータを保持している場合にはこれは悪い設定となります。 なぜならすべてのデータがキャッシュに読み込まれるからです。

共有データストアを用いているときは、変更の有った方のノードが CacheLoader を使ってその変更をデータストアへ書き出します。 このことにより、両方のノードが同じデータを2回書き出すということを防いでいます。

シェルを 2 つオープンして以下の ant ターゲットを実行し、キャッシュを 2 つ立ち上げましょう:

bela@laptop /cygdrive/c/jboss-cache
$ ./build.sh run.demo.async.shared.cacheloader
Buildfile: build.xml

init:

compile:

run.demo.async.shared.cacheloader:
     [java] ** node loaded: /a
     [java] ** node loaded: /a/b
     [java] ** node loaded: /a/b/c
     [java] ** node loaded: /uno
     [java] ** node loaded: /uno/due

     [java] -------------------------------------------------------
     [java] GMS: address is 192.168.1.184:1357
     [java] -------------------------------------------------------
     [java] interceptor chain is:
     [java] class org.jboss.cache.interceptors.CallInterceptor
     [java] class org.jboss.cache.interceptors.ReplicationInterceptor
     [java] class org.jboss.cache.interceptors.CacheLoaderInterceptor
     [java] class org.jboss.cache.interceptors.TransactionInterceptor
     [java] ** view change: [192.168.1.184:1355|1] [192.168.1.184:1355, 192.168.1.184:1357]
     [java] ** node modified: /

2つの GUI が現れてキャッシュの構造をグラフィカルに表示してくれます。 右クリックまたはメニューを使うことでノードを追加したり変更したり削除したりできます。あらゆる変更は 2 つのノード間でレプリケートされます。 ノードを 2 つとも終了した後で片方あるいは両方のノードを再起動すると、CacheLoader により共有データストアに格納されていた、シャットダウン前と同じ状態になります。

上の例では2つのキャッシュは同じマシン (192.168.1.184) 上のポート 1355 とポート 1357 で動作しています。

10.3. データストアを共有しない (ローカルな) レプリケートキャッシュ

この例では、ふたたび 2 つのキャッシュを動作させます。 しかし今回はデータストアを共有せずそれぞれのキャッシュが自分のデータストアを持つようにします。 設定ファイルは jboss-cache/output/etc/META-INF/node{1,2}.xml です。 node1.xml の方を見てみましょう。

<attribute name="CacheLoaderClass">org.jboss.cache.loader.bdbje.BdbjeCacheLoader</attribute>
<attribute name="CacheLoaderConfig">
   location=c:\\tmp\\node1
</attribute>
<attribute name="CacheLoaderShared">false</attribute>
<attribute name="CacheLoaderPreload">/</attribute>
<attribute name="CacheLoaderFetchTransientState">false</attribute>
<attribute name="CacheLoaderFetchPersistentState">true</attribute>

ここでも CacheLoaderClass として Sleepycat CacheLoader を使います。 CacheLoaderConfigc:\tmp\node1 を指します。 これは node1 用の Sleepycat DB が置かれるディレクトリです。 node2.xml には c:\tmp\node2 を指す設定が有ります。 このことにより 2 つの非共有のデータストアができます。 2 つのキャッシュを同じマシン上で実行するため、この例でも同じファイルシステムが前提になっています。 現実的には、これら 2 つのディレクトリは別々の 2 つのマシン上に置かれて JBossCache のプロセスも各々のマシン上で実行されることになるでしょう。 ディレクトリは 2 つとも存在していなければならないことに注意してください。

非共有データストアを生成するには CacheLoaderShared 属性を false に設定します。

例を実行するために再びシェルを 2 つ開いて ant ターゲットを 2 つ実行します (ここでは node2 用のターゲットを示します)。

bela@laptop /cygdrive/c/jboss-cache
$ ./build.sh run.demo.unshared.node2
Buildfile: build.xml

init:

compile:

run.demo.unshared.node2:
     [java] ** node loaded: /a
     [java] ** node loaded: /a/a2
...

run.demo.unshared.node2 ターゲットにより c:\tmp\node2 に置かれる(前述参照)自分自身のデータストアを持つ node2 が起動します。 2 つのキャッシュのどちらかに変更が加えられたときにはもう片方のキャッシュにレプリケートされ、それぞれのローカルなデータストアにセーブされます。 一方または両方のキャッシュを終了し再起動するとバックエンドのデータストアの働きによりデータは利用可能な状態のままになっています。

11. トラブルシューティング

以下はこのデモにおいて問題にぶつかったときのためのトラブルシューティングのコツです。

  • ほとんどの場合、問題はキャッシュレプリケーションのレイヤ、すなわち JGroups パッケージで発生します。 出力ウィンドウ上で JGroups メンバーシップビューを確認できます。 BSH コマンドを実行するとビューが更新されることを確認してください。 ビューには少なくとも 2 つのメンバーが表示されているはずです。 たとえば、私のウィンドウでは以下のようになっています。

     [java] ** view change: [BWANG-HOME:4381|1] [BWANG-HOME:4381, BWANG-HOME:4383]

    4381 と 4383 の2つのメンバーが有ります。 それより前に起動したキャッシュを終了していないのであれば、メンバーシップビューにはそれらのまだ終了していないキャッシュも含まれていることでしょう。 このことが状態をおかしくしている可能性があります。 したがって、各デモを開始する前には TreeCache のプロセスが一つも実行されていない ことを確認する必要があります。もし何か問題が有ればJGroups website に相談してください。