では、簡単な「Hello World」サンプルを作成してOpen Terracottaに触ってみよう。

今回作成するサンプルの目的は、Terracottaサーバに保存されたオブジェクトが複数のJVM間で共有でき、かつサーバをシャットダウンするまでオブジェクトが残り続けることを確認する。

サンプルのイメージを図にすると以下のように表せる。

サンプルのイメージ

jvm1が格納した文字列"Hello World"をJVM2からも参照できるということは、オブジェクトが複数JVM間で共有されるということだ。

まずは、サンプルのJavaソースコードを以下に示す。(HelloWorld.java)

import java.util.List;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;

public class HelloWorld {
   // 複数JVM間で共有されるリスト
   private List hellos = new ArrayList();

   public void sayHello() {
      synchronized(hellos) {
         // リストに文字列を格納した後、標準出力にプリント
         hellos.add("Hello, World " + new Date());
         for(Iterator it = hellos.iterator();it.hasNext();) {
            System.out.println(it.next());
         }
      }
   }
   public static void main(String[] args) {
      new HelloWorld().sayHello();
   }
}

これだけだ。HelloWorldクラスのフィールドhellosは、後にJVM間で共有するリストだ。sayHello()メソッド内ではそのリストに文字列を新しく格納した後、標準出力にプリントしている。

まずは、普通にコンパイルして実行してみよう。

> javac -d classes src\HelloWorld.java
> java -cp classes HelloWorld
Hello, World Fri Apr 06 16:51:07 JST 2007  ← 出力結果

このプログラムでは、sayHello()を一回呼んだ後にプログラムが終了するので、何回プログラムを実行しても出力されるのは一行だけだ。

では、クラスHelloWorld内のフィールドhellosを、Terracottaを用いて複数JVM間で共有する。

まずは、Terracottaサーバを立ち上げる必要がある。「Terracottaインストールディレクトリ(以下TC_HOMEと呼ぶ)\bin\start-tc-server.bat」(UNIXでは$TC_HOME/bin/start-tc-server.sh)を実行する。

コンソールに以下のようなメッセージが出力されればTerracottaサーバの実行は完了だ。

INFO - Terracotta Server has started up as ACTIVE node on port 9510 successfully, and is now ready for work.

次に、先ほどのクラス「HelloWorld」内のフィールドhellosを、Terracottaサーバ上で共有する設定を記述する。以下のXMLを「tc-config.xml」という名前で保存する。

<?xml version="1.0" encoding="UTF-8"?>
<tc:tc-config xmlns:tc="http://www.terracotta.org/config">
  <application>
    <dso>
      <roots>
        <root>
          <field-name>HelloWorld.hellos</field-name> ・・・(1)
        </root>
      </roots>
      <locks>
        <autolock>
          <method-expression>* HelloWorld*.*(..)</method-expression> ・・・(2)
          <lock-level>write</lock-level>
        </autolock>
      </locks>
      <instrumented-classes>
        <include><class-expression>HelloWorld</class-expression></include> ・・・(3)
      </instrumented-classes>
    </dso>
  </application>
</tc:tc-config>

(1) 共有するクラスのフィールドを指定する。HelloWorldクラスのhellosということだ。
(2) 共有するクラスのフィールドに対するロックをメソッド単位で指定する。指定方法はAspectWerkzにおけるジョインポイントの指定方法を踏襲している。<autolock>は、<method-expression>で指定されたメソッド内のsynchronizedブロックによって共有オブジェクトのロックを行う。<lock-level>にwrite(書き込みロック)が指定されており、これによってJVMをまたいだ同期ロックが行われる。
(3) Terracottaがバイトコードを修正する対象となるクラスを指定する。正規表現が指定可能だが、ここではクラス名をそのまま記述した。

あとは、この設定ファイルを読み込ませて実行するのみだ。そのためには通常のjavaコマンドではなく、Terracottaによって用意されたコマンドを使用する(TC_HOMEはTerracottaインストールディレクトリ)。

<TC_HOME>\bin\dso-java.bat -cp classes -Dtc.config=tc-config.xml HelloWorld

tc.configというシステムプロパティに先ほどのXMLファイルへのパスを指定している以外は、通常のJavaコマンドの使用法とほとんど変わらない。

実行するとTerracotta関連のログが出力されたあと、先ほどと同じようにメッセージが出力される。

2007-04-06 17:03:14,250 INFO - Terracotta version 2.3-stable1, as of 20070404-110410 (Revision 2258 by SYSTEM@WXPMO0 from 2.3)
2007-04-06 17:03:14,859 INFO - Configuration loaded from the file at 'C:\tmp\tc-config.xml'.
2007-04-06 17:03:14,937 INFO - Log file: 'C:\tmp\logs-XXX.XXX.XXX.XXX\terracotta-client.log'.
Hello, World Fri Apr 06 17:03:17 JST 2007

面白いのはここからだ。もう一度同じコマンドを実行してみてほしい。

Hello, World Fri Apr 06 17:03:17 JST 2007
Hello, World Fri Apr 06 17:04:54 JST 2007

メッセージが2回出力され、1つ目のメッセージは先ほど出力したメッセージと同じ時刻になっている。

もう一度実行するとメッセージが3回出力される。

もちろん、各コマンド呼び出しは別々のJVMプロセスだ。

つまり、HelloWorldクラスのフィールドhellosは、Terracottaサーバ上で共有され、クラスタ内のJVM間で共有できているのである。またこのサンプルでは実感しづらいが、複数のプロセスが同時に共有オブジェクトにアクセスしたとしても、Terracottaがsynchronizedの効果をクラスタレベルにまで拡張しているので、データが破壊される恐れがない。

そして特筆すべきは、コードを一切修正せず、クラスタリングを実現している点だ。