先々週のStandard MBeanの実装であるDispatcherInfoクラスをよく見てみると,管理対象のオブジェクトへの橋渡しをしているにすぎません。

とすると,この部分を自動的に生成できるのではないかと考えられませんか。このような定型的な記述を自動化してくれるのがDynamic MBeanです。その中でもよく使われているのがModel MBeanです。

Dynamic MBeanを使用すると,Standard MBeanのようにインタフェースやクラスを作る必要がありません。その代わり,「管理対象のオブジェクトにどのようなgetter/setterがあるか」「操作にはどのようなものがあるか」といった情報(メタデータ)を記述します。

JMXでは,このようなメタデータを記述するのにMBeanInfoクラスを使用します。これは通常のBeanのメタデータをBeanInfoで表すのと同じです。しかし,メタデータをソースの中に記述するのも面倒くさいのは確かです。

そこで,「MBeanInfoクラスを記述することなく,管理対象のクラスをそのままMBeanとして扱ってしまおう」というのがJakarta Commonsで開発されているModelerコンポーネントです。

Modelerコンポーネントは,管理対象をラップするクラスを用いることで,管理対象クラスをあたかもMBeanのように使用することを可能にします。最近の言葉でいえば,POJO(Plain Old Java Object)をMBeanとして扱うことができるということです。

Modelerコンポーネントは管理対象のクラスを調べ,パブリックなgetter/setterが存在するならそれをMBeanの属性にし,パブリックなメソッドがあればそれをMBeanの操作として扱います。

ModelerコンポーネントはTomcatの開発から生まれました。もともと,TomcatではModel MBeanを直接使用していましたが,メタデータをコードとして記述することは非効率であると考えたようです。そこで,作成した機能をCommons Modelerコンポーネントとしてまとめたという経緯があります。

今回はこのModelerコンポーネントを使って,簡単にModel MBeanを扱ってみます。

ModelerのダウンロードはJakarta Commonsのダウンロード・ページから行います。2006年3月の時点での最新バージョンは1.1です。ダウンロードしたmodeler-1.1.tar.gzもしくはmodeler-1.1.zipを展開すると,commons-modeler.jarというファイルがあります。これがModelerコンポーネントの本体になります。

Modelerコンポーネントは,ほかにCommons Loggingコンポーネントも必要とするので,一緒にダウンロードして展開しておいてください。

Commons Modeler コンポーネントを使ってみる

それでは,Modelerコンポーネントの使い方を見てみましょう。

サンプルとして使用してきた簡易HTTPサーバーのN1クラスのrunServerメソッドは,Modelerを使用すると次のように記述できます(赤字が追加分)。なお,例外処理は省略しました。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
void runServer() throws Exception {
    Dispatcher1 d = new Dispatcher1();
  
    // MBeanの名前を作成
    ObjectName name = new ObjectName(DISPATCHER_NAME);
		
    // MBeanServerの代わりとなるRegistryを取得
    Registry registry = Registry.getRegistry(null, null);
  
    // Dispatcherを登録
    registry.registerComponent(d, name, null);
  
    d.register(ssc, SelectionKey.OP_ACCEPT,
               new AcceptHandler(ssc, d, sslContext));
    d.run();
}

先々週に紹介したStandard MBeanを使用した場合に比べ,かなり簡単になっています。

今まではMBeanServerを直接使用してきましたが,Modelerの場合はorg.apache.commons.modeler.Registryクラスを使用します。Registryオブジェクトをするために使用するのがgetRegistryメソッドです(8行目)。

複数のRegistryオブジェクトを作成する場合には,getRegistryメソッドの第1引数にRegistryオブジェクトを識別するためのオブジェクトを指定します。nullの場合は常に単一のRegistryオブジェクトを使用します。

getRegistryメソッドの第2引数はガードと呼ばれるもので,Registryオブジェクトへの不正アクセスを防ぐためのオブジェクトになります。ガードを使用しない場合はnullにします。

Registryオブジェクトを取得したら,registerComponentメソッドを使用してオブジェクトを登録します(11行目)。registerComponentメソッドの第1引数の方はObjectクラスなので,どのようなオブジェクトでも登録できます。

registerComponentメソッドの第2引数はObjectNameオブジェクトか,MBeanの名前の文字列です。第3引数がMBeanの名前のtype属性です。第3引数がnullの場合は,第1引数の指定したオブジェクトのクラス名が使用されます。

これだけの変更で,dispatcherがちゃんとMBeanとして扱えるのでしょうか?

まずは,コンパイルです。コンパイルにはModelerコンポーネントのcommons-modeler.jarをクラスパスに含めて行います。ここでは,commons-modeler.jarはカレントディレクトリにおいてあるとします。

>javac -classpath commons-modeler.jar *.java

実行には,起動時オプションに「-Dcom.sun.manamgent.jmxremote」を指定します。また,クラスパスにはcommons-modeler.jar以外にCommons Loggingコンポーネントのcommons-logging.jarも含めます。

>java -cp commons-modeler.jar;commons-logging.jar;.
 -Dcom.sun.management.jmxremote Server N1

2行にわたっていますが,実際には1行です。

サンプルを起動したら,jconsoleを実行します。前回と同じようにMBeanタグを選択すると,ちゃんとsampleが存在しており,PageCountも見ることができます(図1)。

ところが,操作タグを選択すると,resetPageCountメソッド以外にrunメソッドも表示されてしまっています(図2)。

これは,Modelerコンポーネントが管理対象のクラスのパブリックのメソッドをすべて表示してしまっているからです。これを行わないようにするためには,やはりメタデータを記述する必要があります。

Modelerコンポーネントではこの管理対象クラスのメタデータをXMLドキュメントとして記述できます。

Modelerコンポーネントの実行結果(属性)
図1 Modelerコンポーネントの実行結果(属性)
Modelerコンポーネントの実行結果(操作)
図2 Modelerコンポーネントの実行結果(操作)

Dispatcher1クラスのメタデータを次に示します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
<?xml version="1.0"?>
<!DOCTYPE mbeans-descriptors PUBLIC
"-//Apache Software Foundation//DTD Model MBeans Configuration File"
"http://jakarta.apache.org/commons/dtds/mbeans-descriptors.dtd">
 
<mbeans-descriptors>
  <mbean name="dispatcher"
         domain="sample"
         type="Dispatcher1">
    <attribute name="pageCount"
               type="int"
               writeable="false" />
    <operation name="resetPageCount"
               impact="ACTION"
               returnType="void" />
  </mbean>
</mbeans-descriptors>

メタデータのルート要素はmbeans-descriptorsになります。この下にmbean要素を使用してMBeanのメタデータを記述します。ここでは,MBeanは一つですが,複数書くこともできます。

詳しくはmbeans-descriptor.dtdを見てください。mbean要素の属性にはname,domain,typeなどがあります。nameやdomainは,ObjectNameクラスで記述するものと対応します。typeにはクラス名を記述します。

MBeanの属性はattribute要素で記述します。attribute要素の属性にはname,type,writableなどがあります。typeは属性の型,writableは書き込みができるかどうかを指定します。

操作はoperation要素です。この要素もname,impact,returnTypeなどの属性を持ちます。impactはこの操作を実行したときに,内部状態が変化するかどうかを示しています。INFOであれば変更はなく,ACTIONは変更が加わることを示します。よくわからなければUNKNOWNにしておきます。

このようにMBeanのメタデータを記述して,Modelerコンポーネントに読み込ませます。読み込みにはRegistryクラスのloadMetadataメソッドを使用します。引数はFile,URL,InputStreamのいずれかのオブジェクトになります。

なお,メタデータの読み込みは,MBeanの登録よりも前に行う必要があります。

registry.loadMetadata(new File("mbean-descriptors.xml"));
メタデータを読み込んだ場合の実行結果
図3 メタデータを読み込んだ場合の実行結果

このコードではXMLファイルをmbean-descriptors.xmlとしています。

この変更を行なって,再びコンパイル・実行してみましょう。jconsoleの操作タグを見てみると,先ほどは表示されていたrunメソッドが表示されていないことを確認できます(図3)。

このように,Modelerコンポーネントを使うことで,管理対象のクラスをそのままMBeanとして扱うことが可能になります。設定もXMLファイルを記述するだけなので,とても簡単ですよ。

今月のまとめ

先月に引き続いて,Javaでのソフトウエア管理について説明してきました。

ソフトウエアの管理は,今後重要性が増していくことは明らかです。JavaではJMXを用いることで簡単に管理を行うことができます。

通常のソフトウエアの管理はMBean,JVMなどの低レベルの管理にはMXBeanという使い分けをすることで,きめ細かな管理ができると思います。

アプリケーション・サーバーなどのフレームワークを使用する場合,ほとんどの製品にはJMXがすでに組みこまれています。したがって,自分からMBeanを記述することは少ないかもしれませんが,ぜひ積極的に使ってみていただければと思います。

今回は管理ツールとして,jconsoleとHTTP Protocol Adapterを使用しました。J2SE 5.0にはSNMP Protocol Adapterというツールも付属しています。これを使用すれば,ネットワーク機器管理で使用されるSNMPを用いることができるので,既存のネットワーク管理用のツールを流用できます。

何ごとも,起らぬ前の用心が肝心です。自分の健康もそうですが,ソフトウエアの健康も忘れずに。

さて,来月はあまり使われていないAPIの代表格ということで,New I/Oについて紹介したいと思います。

著者紹介 櫻庭祐一

横河電機の研究部門に勤務。同氏のJavaプログラマ向け情報ページ「Java in the Box」はあまりに有名