|
アプリケーション管理に威力を発揮 JMX リモート編 |
||||
|
||||
基礎編で JMX の基本的なところを解説したので、今回はもう 1 つの JMX の JSR に関して解説したいと思います。 基礎編の時は主に HTML プロトコルアダプタを使用して管理をすることを想定していました。しかし、プロトコルアダプタではなく、コネクタに関してはほとんど触れていません。今回はこのコネクタに焦点を当ててみたいと思います。コネクタを使うということはリモートから MBeanServer にアクセスすることになりますが、リモートから JMX を使用するための標準を策定しているのは JSR-160 JMX Remote です。 JMX Remote では基本的に RMI を使用したコネクタに関して標準策定されています。Tiger でも RMI を利用したコネクタが使用できるので、この解説でも RMI のコネクタを使って進めていきたいと思います。とはいっても RMI を意識することはほとんどありません。
|
|
|||||
今回も基礎編と同様に Trivia アプリケーションを管理していきます。MBean も基礎編と同じものを使用します。
前回と異なるのは HTML プロトコルアダプタを使用しないで、コネクタを使用するということです。とはいうもののコネクタを MBeanServer に登録するのはたいしたことではありません。
リモートから JMX を使用するには何らかのアドレスが必要となります。それをあらわすのが JMXServiceURL クラスです。JMXServiceURL であらわす URL は service:jmx:[使用するプロトコル]:[アドレス] のように表記します。 このサンプルでは RMI を使用するので service:jmx の後には rmi と続きます。上記の例のように書くのがお約束のようです。 JMXServiceURL オブジェクトが生成できたら、後は JMXConnectorServerFactory クラスの newJMXConnectorServer メソッドを使用してサーバ側のコネクタである JMXConnectoServer オブジェクトを生成します。 newJMXConnectorServer メソッドの第 1 引数が URL、第 2 引数がコネクタに引きわたすための属性をマップにしたもの、第 3 引数が MBeanServer オブジェクトになります。コネクタに渡す属性はコネクタによって異なるので、コネクタのドキュメントを参照してください。属性がない場合は上記のように null にします。 後は JMXConnectorServer オブジェクトに対して start メソッドをコールするだけです。 これだけで、エージェント・レベルの実装はおしまいです。あっけないですね。 RMI を使用しているので、実行するときにはお約束の rmiregistry を起動させてから行ってください。
|
|
|||||
JMX Remote でメインとなるのは実際に管理を行う管理アプリケーションになります。 とはいうものの MBeanServer クラスをプロキシを介して使用するようなイメージなので、MBeanServer クラスの使い方が分かっていればそれほど大変ではありません。 はじめは登録されている MBean の情報を表示することからやってみましょう。
まず第 1 に行うことはリモートのコネクタに接続することです。
JMXServiceURL クラスを使用してサーバの URL を指定するのは先ほどと同じです。その後に JMXConnectoFactory クラスを使用して JMXConnector オブジェクトを生成します。 JMXConnectorFactory#connect メソッドの第 1 引数は URL、第 2 引数はコネクタに引き渡す属性です。 JMXConnector クラスがサーバ側の JMXConenctorServer クラスと対になるクラスです。JMXConnector オブジェクトは単にサーバとリモートを結びつけるだけなので、実際のオペレーションは JMXConnector オブジェクトから MBeanServerConnection オブジェクトを取り出して行います。 MBeanServerConnection はインタフェースで、MBeanServer インタフェースの親インタフェースになります。したがって、queryMBean メソッドや addNotificationListener メソッドなど MBeanServer インタフェースで使っていた大半のメソッドをそのまま使用することができます。 ですから、ここから後は JMX Remote で策定されたものではなく、JMX の基本的な使い方になります。ただ、基礎編ではあまり MBeanServer インタフェースについては解説しなかったので、ここでそれについて説明しようと思います。
|
|
||||||||||
MBeanServerConnection オブジェクトを取得できたので、登録されている MBean を調べてみましょう。これは queryMBean メソッドで行っています。
登録した MBean を検索するには MBeanServerConnection#queryMBeans メソッドを使用します。第 1 引数が ObjectName オブジェクトで、第 2 引数が QueryExp オブジェクトです。QueryExp はインタフェースでフィルターの役目をします。 通常は第 1 引数に MBean の名前を指定するのですが、上のコードのように null にすると登録されているすべての MBean を検索してくれます。 戻り値は Set オブジェクトになり、第 1 引数で指定された名前の MBean の集合が入ります。この要素は ObjectInstance オブジェクトになります。 それにしてもせっかく Tiger で使うのですから、はじめから Set<ObjectInstance> にしてくれればどんなに楽なことかと思いますね。 ObjectInstance クラスは MBean の名前 (ObjectName オブジェクト) とそのクラス名を保持しています。それぞれ getObjectName メソッド、getClassName メソッドで取得することができるので、上の例ではそれを出力しています。 もうすこし MBean の詳しい情報を見てみましょう。MBean の情報は MBeanInfo クラスで保持しています。MBeanInfo オブジェクトを取得するには MBeanServerConnection#getMBeanInfo メソッドを使用します。引数は ObjectName オブジェクトです。
基礎編で Model MBean を作成するときに ModelMBeanInfoSupport クラスを使用して MBean の情報を記述したのを覚えておられるでしょうか。MBeanInfo クラスはその ModelMBeanInfoSupport クラスの親クラスになります。 Model MBean を作成するときに属性、コンストラクタ、オペレーションのそれぞれの情報をまとめて ModelMBeanInfoSupport オブジェクトを作成しました。MBeanInfo オブジェクトも同じように属性、コンストラクタ、オペレーションの情報を保持しています。また、Model MBean のときには扱わなかったのですが、ノティフィケーションの情報も同じように保持しています。 それぞれの情報も表示してみましょう。まずは属性からです。 属性の情報は MBeanAttributeInfo クラスで表します。MBeanAttributeInfo オブジェクトを取得するためには MBeanInfo クラスの getAttributes メソッドを使用します。属性がない場合は空の配列が帰ってきます。
属性を表す MBeanAttributeInfo クラスやオペレーションを表す MBeanOperationInfo クラスなどはすべて MBeanFeatureInfo クラスの派生クラスとなっています。 MBeanFeatureInfo クラスでは getName メソッドと getDescription メソッドが定義されているので、この 2 つのメソッドは MBeanXXXXXInfo クラスではどれでも使えます。 その他の情報として、属性の型 (getType メソッド)、getter メソッドがあるか setter メソッドがあるかどうか (isReadable メソッド、isWritable メソッド) などの情報を取得することができます。 また、属性の場合、型が boolean や Boolean クラスであると getter メソッドが isXXXX となることがあります。それを調べるために isIs メソッドも定義されています。 次はコンストラクタです。
コンストラクタは MBeanConstructorInfo クラスで表されます。 属性と違うのは引数があるということです。引数は MBeanConstructorInfo#getSignature メソッドで取得でき、戻り値は MBeanParameterInfo クラスの配列となります。 MBeanParameterInfo クラスは型が保持されているので、それを出力しました。 コンストラクタとほとんど同じなのがオペレーションの情報です。
オペレーションの情報は MBeanOperationInfo クラスに保持されますが、MBeanConstructorInfo クラスと違うのは戻り値があるということです。戻り値は MBeanOperationInfo#getReturnType メソッドで取得できます。戻り値は文字列です。 最後にノティフィケーションです。
名前と説明以外にノティフィケーションのタイプを取得することができます (MBeanNotificationInfo#getNotifTypes メソッド)。これは Notification クラスのコンストラクタの第 1 引数で指定したものになります。 さっそく実行してみましょう。 実行するにはまず rmiregistry を起動し、その後 TriviaMBeanStarter を実行します。Trivia アプリケーションのクライアントは今の段階では必要ありません。もちろん、実行してもかまいませんが。 最後に TriviaMBeanRemoteTest1 を実行します。 そのままだと出力結果がかなり長くなってしまうので、TriviaServerStandardMBean に関するところだけを示しておきます。
オペレーションの引数の名前は p1, p2, ... のようになってしまうようですね。実害はないのでこれはこれでかまわないと思います。 TriiaServerStandardMBean はノティフィケーションを使用しないので、ノティフィケーションの部分だけ CounterMonitor の情報を示しましょう。
CounterMonitor クラスが発生させるノティフィケーションは 1 種類ではなく 6 種類もあることが分かります。 さて、MBean の様子が分かったので、次は実際に MBean にアクセスしてみましょう。
|
|
||||||
それでは属性からいきましょう。属性の情報は前章の方法で取得できるので、実際の属性の値を取得してみます。 これを行うサンプルが TriviaMBeanRemoteTest2 です。
属性の値を取得するのもやはり MBeanServerConnection インタフェースのメソッドで行います。複数の属性値をまとめて取得する getAttributes メソッドと、単一の属性を取得する getAttribute メソッドの 2 種類の方法があります。 はじめに getAttributes メソッドの方から見ていきます。
getAttributes メソッドの第 1 引数は ObjectName オブジェクトになります。第 2 引数がこの名前の MBean が持つ属性のうち取得したいものを文字列の配列にしたものです。 上のコードではすべての属性を取得しようとしているので、まず MBeanInfo オブジェクトから MBeanAttributeInfo オブジェクトを取得し、そこから属性の名前を配列に入れています。 getAttributes メソッドの戻り値は AttributeList クラスになります。このクラスは Attribute クラスを要素に持つというだけのリストになっています。そのため add メソッドの引数が Attribute クラスにオーバロードされています。 しかし、get メソッドは戻り値の型だけを変更したオーバロードはできないので、Object クラスのままになっています。 こんな中途半端なクラスをつくらないで、Generics を使用して List<Attribute> としたほうがよっぽど使いやすくなるはずです。 上の例でも Attribute オブジェクトを取得するために AttributeList#get メソッドの戻り値にキャストしています。Tiger で使用するのですから、もうこんな使い方やめればいいのにと思うのですが、そうもいかないのでしょうか。 閑話休題。 Attribute クラスは属性の名前を値を保持したクラスです。getName メソッドで名前、getValue メソッドで値を取得できます。 上記の例では値が配列の場合には展開して表示するようにしています。属性の型は MBeanAttributeInfo クラスを使用して調べることができるので、それによって配列かどうかを判定しています。 次は単一の属性値の取得です。
単一の属性値の取得は getAttribute の第 2 引数に属性の名前を指定するだけです。getAttribute メソッドの戻り値は Attribute オブジェクトではなくて、属性の値が直接戻ってきます。
|
|
||||||
属性値を取得できたので、次は属性値の変更してみます。
属性値の設定は MBeanServerConnection#setAttributes メソッドもしくは MBeanServerConnection#setAttribute メソッドです。 両者の違いは getAttributes メソッドと getAttribute メソッドの違いと同様に、複数属性の値設定と単一属性の値設定の違いになります。 引数も前者が AttributeList クラス、後者が Attribute クラスになります。setAttributes メソッドだけは戻り値があり、AttributeList オブジェクトが戻ってきます。 TriviaMBeanRemoteTest3 クラスでは setAttribute メソッドを使用して値設定しています。
このサンプルでは TotalCount を 10 にしています。実際には Attribute クラスのコンストラクタの第 2 引数は Object クラスなのですが、autoboxing が使用されて Integer オブジェクトに変換されています。 ついでにオペレーションも実行してみましょう。オペレーションの実行はリフレクションを使ったメソッド実行と似たような感じになります。 メソッド実行は MBeanServerConnection#invoke メソッドを使用します。
invoke メソッドの第 1 引数は ObjectName オブジェクトなのはいつもと同じです。第 2 引数はオペレーションの名前、第 3 引数はオペレーションの引数を配列にしたものです。最後の引数がオペレーションの引数の型を文字列の配列にしたものです。 reset メソッドは引数なしのオペレーションなので、第 3, 第 4 引数は null になっています。しかし、実際に引数をとる場合にはここで引数を指定するわけです。 引数がプリミティブの場合はラッパークラスを使用します。int だったら Integer クラス、double だったら Double クラスを使用します。しかし、引数の型は int や double のままにしておきます。 invoke メソッドの戻り値はオペレーションの戻り値になります。 これを実行させてみたらちゃんとリセットがかかることが確認できると思います。 この方法で任意のオペレーションを実行できるわけです。
|
|
||||||
ノティフィケーションも忘れてはならない機能です。MBean に対してリスナ登録を行うのも MBeanServerConnection オブジェクト経由で行います。
リスナ登録は MBeanServerConnection#addNotificationListener メソッドを使用して行います。 TriviaServerStandardMBean はノティフィケーションを扱わないので、代わりに CounterMonitor に対してリスナ登録してみました。
addNotificationListener メソッドは基礎編でも使用しましたね。このメソッドの第 3 引数はフィルタ、第 4 引数はノティフィケーションと一緒にリスナに渡すオブジェクトになります。 これで、ノティフィケーションが発生したら、受け取ることが可能になります。 実行させてみた結果を次に示しておきます。
|
|
|||||
今まで説明してのはすべて MBeanServerConnection インタフェースを介して MBean にアクセスしていました。これはこれでいいのですが、これとは違う方法で MBean にアクセスする方法があります。 それはプロキシを使用する方法です。J2SE 1.3 で導入された動的プロキシを使用して MBean のプロキシを作成し、このプロキシを使用して MBean にアクセスするという方法です。
プロキシの生成には MBeanServerInvokationHandler クラスを使用します。それを行っているのが accessMBeanThroughProxy メソッドです。
MBeanServerInvocation クラスの static なメソッド newProxyInstance を使用してプロキシを生成します。 このメソッドの第 1 引数は MBeanServerConnection オブジェクト、第 2 引数が ObjectName オブジェクトです。第 3 引数は MBean のインタフェースのクラスを指定します。最後の引数はプロキシを作成する MBean がノティフィケーションを扱うかどうかを示すためのフラグになります。 TriviaServerStandardMBean はノティフィケーションは扱わないので、ここは false にしておきます。 プロキシができてしまえば、後は普通のオブジェクトと同様にアクセスすることができます。 プロキシを使用して MBean にアクセスすると、例外処理が簡素化できて見た目はいいかもしれませんが、実際には例外が発生する可能性は残っています。 動的プロキシは InvocationHandler インタフェースの invoke メソッドを使用してメソッドコールを行うのですが、このメソッドは Throwable 例外をスローします。結局、すべての例外の親クラスになるわけですから、チェックすべき例外も実行時例外と同じように try ... catch しなくてすんでしまいます。 逆にこれが例外を隠すことにもなりかねません。実際にプロキシを使用する場合はこの点を注意する必要があります。
|
|
||||||
ずっとコンソールでキャラクタベースで行ってきたので、最後はもう少しましな顔を作ってみました。
メインのクラスが TriviaMBeanClient クラスで、MBeanClientView クラス、AttributeTable クラス、OperationTable クラスが GUI 関連のクラスになります。 実行した結果は次のようになります。
MBean ごとにフレームを表示し、フレームには表形式で属性とオペレーションの一覧があります。一番下は発生したノティフィケーションに関して出力します。 Reload ボタンは属性の再取得を行うためのものです。ただし、このボタンを押さなくても 10 秒ごとに属性の値を自動的に取得しています。 本来ならば Writable の属性は値を設定できるようにすればいいのでしょうが、面倒くさかったので省略してしまいました。同じような理由でオペレーションの実行も引数がないものしかできないようになっています。ようするにテーブルから文字列を拾って、それからオブジェクトを生成するのが面倒くさかっただけなんですが。 アプリケーションの中でやっていることは今までやってきたこととなんら変わりありません。 MBeanServerConnection オブジェクトを取得し、MBean の MBeanInfo オブジェクトを使用してテーブルの構成などを決定し、GUI を描画します。 属性の値は getAttributes メソッドと getAttribute メソッドを使用し、オペレーションの実行は invoke メソッドを使用しています。 また、ノティフィケーションも受けられるようにリスナ登録をして、ノティフィケーションが発生したら表示するようにしました。 かなり手抜きな管理アプリケーションですが、こんなものでも結構役に立つものです。
|
|
||||
今回は JMX リモートで策定されている RMI コネクタに関して、また後半では MBeanServerConnection インタフェースの使い方に関して説明を行いました。 JXM リモートはここで解説した基本的な使い方以外に、JNDI などを利用した MBeanServer の検索やセキュリティに関しても定義されています。本格活用するならば、これらも考慮しなくてはいけないと思います。 とはいうものの、MBean を作ることはあったとしても、なかなか自分で管理アプリケーションを作ろうという方は少ないと思います。しかし、自分なりのカスタマイズした管理アプリケーションを作ってみるのもなかなか面白いのではないでしょうか。
今回使用したサンプルはここからダウンロードできます。
参考
(May. 2004) |
|