プラットホームMBeanサーバと独自MXBean

Java6より、インタフェースとアノテーションによってMXBeanは非常に簡単に作れるようになった。
例えば私が実際に作っている「コードベース及びソケットのリスンポートを返すMXBean」は、以下のようにインタフェースにMXBeanアノテーションを付加することで定義される。

@MXBean
public interface ICodeBaseAgent {
    String getCodeBase();
    int getListenPort();
}

実際にこのMXBeanを公開するには、このインタフェースを実装するJavabeansクラスを用意して、MBeanサーバに登録するだけでよい。(実際にはRMIローカルレジストリが起動されている必要があるが、割愛)

//MBeanサーバの起動又は取得
//server = MBeanServerFactory.createMBeanServer();
server = ManagementFactory.getPlatformMBeanServer();

//MXBeanをプラットホームMBeanサーバに登録
ObjectName name = new ObjectName("com.kazz.impl:type=ICodeBaseAgent");
CodeBaseAgentImpl agent = new CodeBaseAgentImpl();
server.registerMBean(agent, name);

MXBeanを公開するMBeanサーバは、元々JSDKが用意している「プラットホームMBeanサーバ」か独自に作成するMBeanサーバを選ぶ必要があるが、JConsoleからの接続を受け入れることや、特に設定が必要無い簡便さからプラットホームMBeanサーバを選ぶことが多いだろう。(上記サンプルもプラットホームMBeanサーバを取得している)

さて、プラットホームMBeanサーバに登録/公開されたMXBeanを他のアプリケーションから取得するには、前回のエントリで説明したように、ローカルコネクタアドレスを取得した上でMXBeanへの接続を確立するJMXConnectorオブジェクトを生成する必要がある。

//プラットホームMBeanサーバローカルコネクタアドレスに接続するためのコネクタを取得する
JMXServiceURL url = new JMXServiceURL(connectorAddress);
JMXConnector connector = JMXConnectorFactory.connect(url);

コネクタが無事取得できれば、あとはコネクタを介してMBeanサーバから該当のオブジェクト名(ObjectName)を持つMXBeanのプロキシを取得すれば良い。

MBeanServerConnection connection = connector.getMBeanServerConnection();
//RMIを介しているため、取得するのはMXBeanのプロキシ(動的プロキシ)となる
ICodeBaseAgent agent = ManagementFactory.newPlatformMXBeanProxy(connection, "com.kazz.impl:type=ICodeBaseAgent", ICodeBaseAgent.class);

さあこれでOKかと思いきや、実行すると「このMXBeanはプラットホームMBeanではない」と怒られてしまう。

Exception in thread "main" java.lang.IllegalArgumentException: com.kazz.impl:type=ICodeBaseAgent is not a platform MXBean

この場合、以下のようにMBeanServerInvocationHandlerを使わなくてはならないのだ。

ObjectName on = new ObjectName("com.kazz.impl:type=ICodeBaseAgent");
ICodeBaseAgent agent = MBeanServerInvocationHandler.newProxyInstance(connection, on, ICodeBaseAgent.class, false);

正直、同じMBeanサーバ上に登録されているMXBeanをnewPlatformMXBeanProxyメソッドで取得できないのは納得がいかないのだが、こうしないと動かないので仕方が無い。

なお、上記では取得したMXBeanをインタフェースに直接キャストしているが、実際にはMBeanServerConnection#isInstanceOfメソッドを使って、現在接続しているMXBeanの型を前もってチェックすることで、InstanceNotFoundExceptionを捕捉できるようになる。

ObjectName on = new ObjectName("com.kazz.impl:type=ICodeBaseAgent");
MBeanServerConnection connection = connector.getMBeanServerConnection();
if (connection.isInstanceOf(on, "com.kazz.ICodeBaseAgent ")) {
    ICodeBaseAgent agent = MBeanServerInvocationHandler.newProxyInstance(connection, on, ICodeBaseAgent.class, false);
}

これでようやく、他のアプリケーションが公開している情報を読み取れるようになった訳だ。