JSR 262: Web Services Connector for Java Management Extensions Agents

JMX(Java Management Extensions)はJ2SE 5.0から導入された、JavaアプリケーションやJVMの状態を監視/管理するための技術である。現行のJMX仕様はJSR 3で定義されており、次期バージョンとなるJMX 2.0はJSR 255として仕様の策定が進められている。

JMXでは監視/管理対象のリソースをMBeanと呼ばれるオブジェクトで表現し、アプリケーション側からはJMX Agent(MBean Server)を介してアクセスする。JMX Agentにはリモートからアクセスすることもでき、そのための技術仕様は「JSR 160: JMX Remote API」としてまとめられている。JMX Agentへのリモートアクセスは、通信プロトコルごとに用意された"Connector"と呼ばれる通信モジュールを介して行う。

JSR 262はこのJMX Remote APIを拡張し、WebサービスでJMX AgentにアクセスするためのConnectorを提供する。すなわち、Javaアプリケーションの監視/管理をWebサービスプロトコル経由で行えるようにする仕様である。これを利用することでJAX-WSなどのWebサービスAPIを利用したJavaアプリケーションから容易にJMXにアクセスできるほか、WSDLをサポートしたJava以外のWebサービスアプリケーションからもJavaアプリケーションのリモート管理ができるようになる。

JSR 262は現在jcp.orgのサイトにてEarly Draftが公開されており、ちょうど23日にEarly Draft Review 2が終了したばかりである。また、java.net内にあるプロジェクトサイトにて参照実装のEarly Access 2が公開されている。

JSR 262の参照実装を試す

JSR 262の参照実装はプロジェクトサイトのドキュメント&ファイルセクションからダウンロードすることができる。配布ファイルはjsr形式で、これを任意の場所に保存してコマンドプロンプトからプロンプト1のように実行すればインストーラが起動する。

プロンプト1 JSR-262参照実装のインストール

> java -jar jsr262-ri.jar

あとはインストーラにしたがってライセンスに同意し、任意の場所にインストールすればよい。

図1 参照実装のインストール1 - ライセンスに同意

図2 参照実装のインストール2 - 任意の場所を指定

なお、JSR 262はJ2SE 5.0およびJava SE 6に対応している、また、それとは別にJAX-WS 2.1.1以降のバージョンが必要となる。JAX-WS 2.1.1の参照実装はJAX-WSのプロジェクトサイトよりダウンロードできる。

この参照実装にはライブラリとサンプルプログラムが含まれている。今回はこのサンプルプログラムのうち、もっとも基本的な「simple」フォルダにある例を動かしてみよう。なお、筆者はWindows XP Pro Service Pack 2およびJava SE 6 Update2という環境でテストしている。

まず、JSR 262のサンプルプログラムを実行するにはApache Antの1.6.x以降のバージョンが必要となる。加えて、JAVA_HOME環境変数にJDKがインストールしてあるフォルダを設定しておく。

サンプルにはAntビルド用のbuild.xmlが付属しているが、これはJDK 5.0用のものなので、JDK 6を使用している場合には若干の修正を加える必要がある。まず、参照実装をインストールした場所(以下、[PATHTOJSR262]と表記する)にあるlib\endorsedフォルダに、JAX-WS 2.1.1に含まれる.jarファイルをすべてコピーする。JAX-WS 2.1.1をインストールした場所のlibフォルダ以下に、jaxws-api.jarやjaxb-api.jarなどのファイルがあるはずだ。

次に各サンプルプログラムのbuild.xmlを修正し、java.endorsed.dirsシステムプロパティにlib\endorsedフォルダを指定するよう属性を追加する。「simple」の場合、サーバプログラム用とクライアントプログラム用のbuild.xmlがあり、それぞれリスト1、リスト2のようになる。

リスト1 サーバプログラムのbuild.xml([PATH_TO_JSR262]\samples\simple\server\build.xml)

<?xml version="1.0" encoding="UTF-8"?>
<!--
 build.xml
 Copyright 2006 Sun Microsystems, Inc. All rights reserved.
 SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
-->
<project name="SimpleAgent" default="all" basedir=".">
    <property name="liblocation" value="../../../lib"/>
    <property file="../../common/common.properties"/>
    <target name="init">  
    </target>
    <target name="all" depends="init">
        <javac srcdir="." includes="*.java"/>
        <java fork="true" classpath="${classpath}" classname="SimpleAgent">
            <!--<sysproperty key="java.util.logging.config.file" value="../../common/logging.properties"/>-->
            <sysproperty key="java.endorsed.dirs" value="../../../lib/endorsed"/>
        </java>
    </target>
</project>

リスト2 クライアントプログラムのbuild.xml([PATH_TO_JSR262]\samples\simple\client\build.xml)

<?xml version="1.0" encoding="UTF-8"?>
<!--
 build.xml
 Copyright 2006 Sun Microsystems, Inc. All rights reserved.
 SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
-->
<project name="SimpleClient" default="all" basedir=".">
    <property name="liblocation" value="../../../lib"/>
    <property file="../../common/common.properties"/>
    <target name="init">  
    </target>
    <target name="all" depends="init">
        <javac srcdir="." includes="SimpleClient.java"/>
        <java fork="true" classpath="${classpath}" classname="SimpleClient">
            <!-- <sysproperty key="java.util.logging.config.file" value="../../common/logging.properties"/>-->
            <sysproperty key="java.endorsed.dirs" value="../../../lib/endorsed"/>
        </java>
    </target>
</project>

プログラムのコンパイルおよび実行は、コマンドプロンプトからantコマンドを使用して行う。まず、プロンプト2のようにしてサーバプログラムを実行する。サーバプログラムが待機状態になったら、別のコマンドプロンプトからクライアントプログラムを実行し、「Hello World(s)!」と表示されたら接続成功(プロンプト3)。

プロンプト2 サーバプログラムの実行

[PATH_TO_JSR262]\samples\simple\server> ant
Buildfile: build.xml

init:

all:
    [javac] Compiling 3 source files
     [java] SimpleAgent started on service:jmx:ws://localhost:9998/jmxws. Waiting...

プロンプト3 クライアントプログラムの実行

[PATH_TO_JSR262]\samples\simple\client> ant
Buildfile: build.xml

init:

all:
    [javac] Compiling 1 source file
     [java] Received : Hello World(s)!
     [java] Connection closed.

BUILD SUCCESSFUL
Total time: 8 seconds

正しく実行できたら、サンプルプログラムの中身を見てみよう。まずサーバ側には、管理対象リソースを表すMBeanとしてSampleMBeanインタフェースおよびそれをimplementsしたSampleクラス、そしてJMX AgentであるSimpleAgentクラスが用意されている。MBeanはhelloプロパティと、その値を返すgetHello()メソッドを持つ(リスト3、リスト4)。

リスト3 SampleMBean.java

public interface SampleMBean
{
    /**
     * helloプロパティの値を返す
     */
    public String getHello();

}

リスト4 Sample.java

import javax.management.*;

public class Sample implements SampleMBean {

    /**
     * helloプロパティ
     */
    private String hello = "Hello World(s)!";

    public Sample() {
    }

    /**
     * helloプロパティの値を返す
     */
    public String getHello() {
        return hello;
    }

}

SimpleAgentクラスではMBeanサーバにSampleMBeanを登録し、Connectorサーバを生成してリモートからの接続を待ち受ける(リスト5)。Connectorサーバの生成はJMXConnectorServerFactoryクラスのnewJMXConnectorServer()メソッドを使用して行う。このとき第1引数にはJMXServiceURLオブジェクトを渡すが、そのプロトコルとして「ws」を指定している点に注意してほしい。こうすることで、ConnectorサーバはWebサービスによる接続を受け付けることができる。

リスト5 SimpleAgent.java

import javax.management.MBeanServer;
import java.lang.management.ManagementFactory;
import javax.management.ObjectName;
import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL;

public class SimpleAgent {

    public static void main(String[] args) throws Exception {

        // ホスト名
        String host = "localhost";        
        // ポート番号
        int port = 9998;
        // URLパス
        String urlPath = "/jmxws";

        // MBeanサーバを取得
        MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();

        // Sample MBeanを登録
        mbs.registerMBean(new Sample(), 
               new ObjectName(":type=Sample")); 

        // Connectorサーバを生成してスタート
        JMXConnectorServer server = 
                JMXConnectorServerFactory.
                newJMXConnectorServer(new JMXServiceURL("ws", host, 
                port, urlPath), null, mbs);       
        server.start();

        System.out.println("SimpleAgent started on " + server.getAddress() + 
                ". Waiting...");

        Thread.sleep(Long.MAX_VALUE);
    }
}

クライアント側プログラムとしては、サーバ側のSimpleAgentにConnectorを介して接続し、MBeanからhelloの値を取得するSimpleClientクラスが用意されている。ここでも、SimpleAgentのURLを表すJMXServiceURLを生成する際に、プロトコルとして「ws」を指定する。

リスト6 SimpleClient.java

import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;

public class SimpleClient {

    public static void main(String[] args) throws Exception {

        // ホスト名
        int port = 9998;
        // ポート番号
        String host = "localhost";
        // URLパス
        String urlPath = "/jmxws";

        // JMX AgentのURLを指定
        JMXServiceURL url = new JMXServiceURL("ws", host, port, urlPath);
        // Connectorに接続
        JMXConnector connector = JMXConnectorFactory.connect(url, null);

        // MBeanServerへのコネクションを取得
        MBeanServerConnection connection = connector.getMBeanServerConnection();

        // 属性を指定して値を取得
        ObjectName mbeanName = new ObjectName(":type=Sample");
        String value = (String) connection.getAttribute(mbeanName, "Hello");

        System.out.println("Received : " + value);

        connector.close();

        System.out.println("Connection closed.");
    }
}

JMX Remote APIでは、ConnectorもConnectorServerもインスタンスの生成はファクトリクラスによって行うようになっている。したがって使用するプロトコルの違いはこのファクトリの部分で吸収されるため、Webサービス経由でJMXを利用する場合にも、RMIなどを使った場合とまったく同様の手順でアクセスすることができる。

JSR 262の仕様はまだEarly Draftの段階なので今後も変更が加えられる可能性は十分にあるが、基本的なコンセプトについてはスペックリードであるEamonn McManus氏のブログこのエントリ にも詳しく解説されているので参考にしてほしい。