JBossCache チュートリアル(日本語)の表示カテゴリ(タグ)ツリーファイル情報
JBoss TreeCache (および TreeCacheAop) は (同期または非同期の) レプリケーション可能でトランザクショナルなキャッシュであり、アスペクト指向プログラミング (AOP) をサポートします。 このチュートリアルでは AOP 有りの場合と無しの場合のそれぞれのキャッシュの使い方をデモします。 詳しい API の使い方は TreeCache と TreeCacheAop それぞれのユーザーマニュアル (TreeCache および TreeCacheAop) を参照してください。 まず、 ここからスタンドアロンの TreeCache のコードをダウンロードします。 展開するとディレクトリツリーができます。(ここではツリーのルートが jboss-cache であるとします) 設定ファイルが etc ディレクトリの下に有ります。 これらの各種設定ファイルを書き換えることで TreeCache の挙動を変更することができます。
このチュートリアルで必要となるスクリプトファイル (インストールディレクトリ直下に有る) は以下の通りです。
TreeCacheAop のデモに使われる説明用の POJO クラスは Person と Address です。 これらは examples/org/jboss/cache/aop ディレクトリに有ります。 Person は String age, Address addr, List languages などの属性を持ちます。 POJO インスタンスをキャッシュに納めると、素の get/set メソッド呼び出しがキャッシュによってインターセプトされるようになるということをデモします。 以下は Person と Address のクラス定義の一部です。 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; } ... } デモを実行するには、少なくとも2つウィンドウが必要となります。 一つはキャッシュの内容の観察 (と AOP を伴わない操作) のため、もう一つはキャッシュを直接操作するため。 もちろん、複数のメンバーに対してキャッシュのレプリケーションが働いているところを見るために GUI ウィンドウを複数開くこともできます。 スクリプトは配布パッケージ (jboss-cache-dist.zip) を展開した後で jboss-cache ディレクトリ直下で実行する必要が有ります。 GUI の制限の為、以下のことに注意してください。
ここで実行する2つのプログラムは以下の通りです。
シェルが立ち上がれば、キャッシュにデータを挿入するスクリプトを実行したりコマンドラインから手動でキャッシュへのデータ挿入を実行したりできます。 スクリプトを実行するには、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"); シェルを立ち上げたら 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); 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(); これ以降のすべての例は単体で配布されている JBossCahce に基づいています。 ZIP ファイルが jboss-cache ディレクトリ以下に展開されていることが前提となっています。 このデモではキャッシュローダ付きのローカルな TreeCacheAop をお見せします。 POJO をキャッシュに挿入すると、その POJO が透過的に CacheLoader によってセーブされます。 これを実行するには、jboss-cache/output/etc/META-INF/oodb-service.xml を変更する必要があります。 CacheLoaderConfig を変更して有効なディレクトリ (無ければ作ってください) を指すようにしてください。
その後 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 次に Person のインスタンスを生成して address やその他のフィールドをセットします。 bsh % p=new Person(); 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 興味深い点は、キャッシュは最初の時点では空("/")であったということです。 "p" をロードして初めてそれは実体化されました (遅延ロード)。 "p" の内容として先にセーブしたのと同じ内容がデータストアから読み込まれたことが確認できるはずです。 この例で実行するシナリオは JBossCache のドキュメントに書かれているものと同じです。 2 つの別々のノードがその内容をお互いにレプリケートします。 更に、それらは同じデータストアを指しています。 設定ファイルは jboss-cache/output/etc/META-INF/replAsyncSharedCacheLoader-service.xml です:
FetchStateOnStartup を false にすると新たに起動されるキャッシュは状態の読み込みをしません (一時的にも永続的にも)。 したがって、CacheLoaderFetchTransientState と CacheLoaderFetchPersistentState とは無視されます。 CacheLoaderShared を true にすると両方のノードが同じデータストアを共有します。 この例ではデータストアは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 で動作しています。 この例では、ふたたび 2 つのキャッシュを動作させます。 しかし今回はデータストアを共有せずそれぞれのキャッシュが自分のデータストアを持つようにします。 設定ファイルは jboss-cache/output/etc/META-INF/node{1,2}.xml です。 node1.xml の方を見てみましょう。
ここでも CacheLoaderClass として Sleepycat CacheLoader を使います。 CacheLoaderConfig は c:\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 つのキャッシュのどちらかに変更が加えられたときにはもう片方のキャッシュにレプリケートされ、それぞれのローカルなデータストアにセーブされます。 一方または両方のキャッシュを終了し再起動するとバックエンドのデータストアの働きによりデータは利用可能な状態のままになっています。 以下はこのデモにおいて問題にぶつかったときのためのトラブルシューティングのコツです。
|