この章では、標準および動的な管理Bean (MBean)の概念を紹介します。Java Management Extensions (JMX)テクノロジを使用してMBean上でローカルおよびリモートから操作を実行する方法について示します。
この例では、標準および動的MBeanを説明します。
JMX APIの基本要素で説明したように、標準MBeanは内部のメソッドの名前を使用して管理インタフェースを静的に定義します。動的MBeanは、特定のJavaインタフェースを実装し、実行時に自らの属性と操作を示します。
JMXテクノロジは、Remote Method Invocation (RMI)を基にしたコネクタを定義します。RMIコネクタは、Java Remote Method Protocol (JRMP)トランスポートをサポートします。このコネクタを使用すると、MBeanサーバーのMBeanにリモート接続し、ローカルで操作を実行するのとまったく同じように、そのMBeanで操作を実行できます。
この例は、標準MBeanと動的MBeanの実装を説明することを目的としています。両方でローカルに、またサーバーとリモート・クライアント間のRMI接続を通じてリモートに操作を実行する方法も示します。
この例を実行する場合
サーバー側:
MBeanサーバーを作成
ローカルMBeanサーバーにSimpleStandard
とSimpleDynamic
のMBeanを登録
両MBean上でローカル操作を実行
RMIコネクタ・サーバーを作成
クライアント側:
RMIコネクタを作成
リモートMBeanサーバーにSimpleStandard
とSimpleDynamic
のMBeanを登録
両MBeanでリモート操作を実行
基本MBeanの例で使用したクラスの分析
JMXコネクタからwork_dir/jmx_examples/Basic
ディレクトリにソース・コードをコピーします。このディレクトリ内に次のコピーが作成されます。
Server.java
SimpleStandardMBean.java
SimpleStandard.java
SimpleDynamic.java
ClientListener.java
Client.java
README
各*.java
ファイルをIDEまたはテキスト・エディタで開きます。
次のセクションでは、基本的なMBeanの例題で使用される各クラスを分析し、それらのクラスがこれまでのセクションで説明した操作をどのように実行するかについて説明します。
Server.java
クラスは、その大きさにより、次のコード(抜粋)で示されます。
コード例3-1 MBeanの例クラスServer.java (抜粋1)
public class Server { public static void main(String[] args) { try { MBeanServer mbs = MBeanServerFactory.createMBeanServer(); waitForEnterPressed(); String domain = mbs.getDefaultDomain(); waitForEnterPressed(); String mbeanClassName = "SimpleStandard"; String mbeanObjectNameStr = domain + ":type=" + mbeanClassName + ",name=1"; ObjectName mbeanObjectName = createSimpleMBean(mbs, mbeanClassName, mbeanObjectNameStr); waitForEnterPressed(); printMBeanInfo(mbs, mbeanObjectName, mbeanClassName); waitForEnterPressed(); manageSimpleMBean(mbs, mbeanObjectName, mbeanClassName); waitForEnterPressed(); mbeanClassName = "SimpleDynamic"; mbeanObjectNameStr = domain + ":type=" + mbeanClassName + ",name=1"; mbeanObjectName = createSimpleMBean(mbs, mbeanClassName, mbeanObjectNameStr); waitForEnterPressed(); printMBeanInfo(mbs, mbeanObjectName, mbeanClassName); waitForEnterPressed(); manageSimpleMBean(mbs, mbeanObjectName, mbeanClassName); waitForEnterPressed(); [...]
このクラスを検討すると、次のイベントが起こっていることがわかります。
まず、Server.java
クラスは、MBeanServerFactory
クラスのcreateMBeanServer()
メソッドを呼び出してmbs
と呼ばれる新しいMBeanサーバーを作成します。
次にMBeanサーバーを登録するデフォルト・ドメインを、MBeanServer
インタフェースのgetDefaultDomain()
メソッドを呼び出して取得します。このドメインは、文字列domain
で識別されます。
MBeanクラスSimpleStandard
も、この場合は文字列mbeanClassName
という変数により識別されます。SimpleStandard
は、このMBeanをインスタンスとするJavaオブジェクトのJavaクラスの名前です。SimpleStandard.java
オブジェクトについては、詳細なセキュリティの例にあるSimpleStandard.javaを参照してください。
もう1つの変数、文字列mbeanObjectNameStr
は、ドメインと次のキー=値ペアを組み合わせて定義されます。
type
。この場合は、mbeanClassName
。name
。このMBeanを、後で作成される同じタイプの他のMBeanと区別します。この場合、name番号は1
です。mbeanObjectNameStr
の目的は、人間が読むことができる識別子をMBeanに割り当てることです。
createSimpleMBean()の呼出しにより、指定されたオブジェクト名のSimpleStandard MBeanがローカルMBeanサーバーで作成され、登録されます。
次に、printMBeanInfo()
とmanageSimpleMBean()
の両方の操作が、SimpleStandard
MBeanで実行されます。createSimpleMBean()
と同様、これらのメソッドは後でServer.java
コードで定義され、コード例3-4 MBeanの例題クラスServer.java (抜粋4)とコード例3-5 MBeanの例題クラスServer.java (抜粋5)で示されます。
このコードには記述されていませんが、SimpleDynamic
型の2番目のMBeanが、SimpleStandard
MBeanとまったく同じ方法でMBeanサーバーに作成され、登録されます。名前が示すように、このMBeanはMBeanの例のSimpleDynamic.javaで説明するSimpleDynamic
Javaオブジェクトのインスタンスです。
コード例3-2 MBeanの例クラスServer.java (抜粋2)
[...] JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:9999/server"); JMXConnectorServer cs = JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs); cs.start(); waitForEnterPressed(); cs.stop(); [...]
コード例3-2 MBeanの例題クラスServer.java (抜粋2)では、RMIコネクタ・サーバーはMBeanでリモート操作ができるように作成されています。JMXServiceURL
クラスを呼び出すと、コネクタ・サーバーのアドレスとなるurl
という新しいサービスURLが作成されます。この例では、サービスURLはエンコード形式ではなくJNDI形式で指定されます(JNDI形式の詳細は、javax.management.remote.rmi
パッケージのAPIドキュメントを参照)。このサービスURLは、次の内容を定義しています。
rmi
で表されるデフォルトのRMIトランスポートを使用する。9999
で稼動し、サーバー・アドレスはserver
の名前で登録される。例で指定されるポート9999
は任意の数字。使用可能なポートを指定できる。RMIコネクタ・サーバーcs
は、パラメータにサービスURL url
、null
環境マップ、およびMBeanサーバーmbs
を使用して、コンストラクタJMXConnectorServerFactory
を呼び出して作成されます。コネクタ・サーバーcs
は、JMXConnectorServer
のstart()
メソッドを呼び出し、RMIConnectorServer
がRMIオブジェクトserver
をRMIレジストリにエクスポートすることで起動されます。この接続は、Enterキーを押すまで有効です。これはServer
コードの後半で定義する簡単なメソッドwaitForEnterPressed
の手順に従ったものです。
コード例3-3 MBeanの例クラスServer.java (抜粋3)
[...] private static ObjectName createSimpleMBean(MBeanServer mbs, String mbeanClassName, String mbeanObjectNameStr) { echo("\n>>> Create the " + mbeanClassName + " MBean within the MBeanServer"); echo("ObjectName = " + mbeanObjectNameStr); try { ObjectName mbeanObjectName = ObjectName.getInstance(mbeanObjectNameStr); mbs.createMBean(mbeanClassName, mbeanObjectName); return mbeanObjectName; } catch (Exception e) { echo( "!!! Could not create the " + mbeanClassName + " MBean !!!"); e.printStackTrace(); echo("\nEXITING...\n"); System.exit(1); } return null; } [...]
コード例3-3 MBeanの例題クラスServer.java (抜粋3)は、createSimpleMBean()
メソッドの定義を示しています。このメソッドでは、オブジェクト名mbeanObjectNameStr
を使用したMBeanインスタンスが、ObjectName
インタフェースのgetInstance()
メソッドに渡され、MBeanサーバー内でMBeanに登録するための新しいオブジェクト名が作成されます。最終的なオブジェクト名インスタンスは、mbeanObjectName
に設定されます。次にMBeanServer
のメソッドcreateMBean()
により、mbeanClassName
で識別されるJavaオブジェクトとMBeanインスタンスmbeanObjectName
の組み合わせによりMBeanのインスタンスが作成され、MBeanサーバーmbs
にこのMBeanが登録されます。
コード例3-4 MBeanの例クラスServer.java (抜粋4)
[...] private static void printMBeanInfo(MBeanServer mbs, ObjectName mbeanObjectName, String mbeanClassName) { MBeanInfo info = null; try { info = mbs.getMBeanInfo(mbeanObjectName); } catch (Exception e) { echo( "!!! Could not get MBeanInfo object for " + mbeanClassName +" !!!"); e.printStackTrace(); return; } MBeanAttributeInfo[] attrInfo = info.getAttributes(); if (attrInfo.length > 0) { for (int i = 0; i < attrInfo.length; i++) { echo(" ** NAME: " + attrInfo[i].getName()); echo(" DESCR: " + attrInfo[i].getDescription()); echo(" TYPE: " + attrInfo[i].getType() + "READ: "+ attrInfo[i].isReadable() + "WRITE: "+ attrInfo[i].isWritable()); } } else echo(" ** No attributes **"); [...]
コード例3-4 MBeanの例題クラスServer.java (抜粋4)では、メソッドprintMBeanInfo()
の定義が示されます。printMBeanInfo()
メソッドはMBeanServer
のメソッドgetMBeanInfo()
を呼び出して、MBean mbeanObjectName
により開示される属性と操作の詳細を取得します。MBeanAttributeInfo
は次のメソッドを定義します。それらのメソッドはそれぞれ、mbeanObjectName
MBeanの属性に関する情報を取得する場合に呼び出されます。
getName
: 属性の名前を取得する。getDescription
: 人間が読むことのできる属性の説明を取得する。getType
: 属性のクラス名を取得する。isReadable
: 属性が読出し可能かどうかを判断する。isWritable
: 属性が書込み可能かどうかを判断する。ここには示していませんが、呼出しが行われるのは、mbeanObjectName
MBeanのコンストラクタ、操作、および通知に関する情報を取得するためです。
MBeanConstructorInfo
: MBeanのJavaクラスに関する情報を取得する。MBeanOperationInfo
: MBeanで実行される操作の内容、およびMBeanで使用されるパラメータの種類を学習する。MBeanNotificationInfo
: MBeanの操作が実行された場合に、MBeanから送信される通知の種類を調べる。コード例3-5 MBeanの例クラスServer.java (抜粋5)
[...] private static void manageSimpleMBean(MBeanServer mbs, ObjectName mbeanObjectName, String mbeanClassName) { try { printSimpleAttributes(mbs, mbeanObjectName); Attribute stateAttribute = new Attribute("State", "new state"); mbs.setAttribute(mbeanObjectName, stateAttribute); printSimpleAttributes(mbs, mbeanObjectName); echo("\n Invoking reset operation..."); mbs.invoke(mbeanObjectName, "reset", null, null); printSimpleAttributes(mbs, mbeanObjectName); } catch (Exception e) { e.printStackTrace(); } } private static void printSimpleAttributes( MBeanServer mbs, ObjectName mbeanObjectName) { try { String State = (String) mbs.getAttribute(mbeanObjectName, "State"); Integer NbChanges = (Integer) mbs.getAttribute(mbeanObjectName, "NbChanges"); } catch (Exception e) { echo( "!!! Could not read attributes !!!"); e.printStackTrace(); } } [...]
コード例3-5 MBeanの例題クラスServer.java (抜粋5)は、簡単なMBeanを管理するためのメソッドを示しています。
まず、manageSimpleMBean()
メソッドが、Server
でも定義されるprintSimpleAttributes()
メソッドを呼び出します。printSimpleAttributes()
メソッドは、MBean mbeanObjectName
からstate
というMBean属性と、NbChanges
というもう1つのMBean属性を取得します。これらの属性はいずれも、詳細なセキュリティの例にあるSimpleStandard.javaに示すように、SimpleStandard
クラスで定義されます。
次にmanageSimpleMBean()
メソッドは、Attribute
クラスのインスタンスである属性stateAttribute
を定義します。stateAttribute
属性は、値new state
をSimpleStandard
で定義される既存の属性state
に関連付けます。次にMBeanServer
のsetAttribute()
メソッドに呼び出すことで、mbeanObjectName
MBeanの状態がstateAttribute
で定義される新しい状態に設定されます。
最後にMBeanServer
のinvoke()
メソッドに呼び出すことで、mbeanObjectName
MBeanのreset
操作が起動します。reset
操作は、SimpleStandard
クラスで定義されます。
SimpleStandardMBean.java
クラスを、コード例3-6 MBeanの例題クラスSimpleStandardMBean.java
に示します。
コード例3-6 MBeanの例題クラスSimpleStandardMBean.java
public interface SimpleStandardMBean { public String getState(); public void setState(String s); public int getNbChanges(); public void reset(); }
SimpleStandardMBean.java
クラスは、MBean SimpleStandard
の簡単なJMX仕様の管理インタフェースです。このインタフェースは、JMXエージェントからの管理のためにSimpleStandard
で定義された4つの操作を公開します。
SimpleStandard.java
クラスを、コード例3-7 MBeanの例題クラスSimpleStandard.java
に示します。
コード例3-7 MBeanの例題クラスSimpleStandard.java
public class SimpleStandard extends NotificationBroadcasterSupport implements SimpleStandardMBean { public String getState() { return state; } public void setState(String s) { state = s; nbChanges++; } public int getNbChanges() { return nbChanges; } public void reset() { AttributeChangeNotification acn = new AttributeChangeNotification(this, 0, 0, "NbChanges reset", "NbChanges", "Integer", new Integer(nbChanges), new Integer(0)); state = "initial state"; nbChanges = 0; nbResets++; sendNotification(acn); } public int getNbResets() { return nbResets; } public MBeanNotificationInfo[] getNotificationInfo() { return new MBeanNotificationInfo[] { new MBeanNotificationInfo( new String[] { AttributeChangeNotification.ATTRIBUTE_CHANGE }, AttributeChangeNotification.class.getName(), "This notification is emitted when the reset() method is called.") }; } private String state = "initial state"; private int nbChanges = 0; private int nbResets = 0; }
SimpleStandard
クラスは、簡単なJMX仕様の標準MBeanを定義します。SimpleStandard
MBeanは、主体委譲の例にあるSimpleStandardMBean.javaに示すように、対応するSimpleStandardMBean
インタフェースを実装することで、管理のための操作と属性を開示します。
このMBeanで開示される簡単な操作を次に示します。
状態を定義する
現在の状態を更新する
状態の更新回数をカウントする
状態の値と、変更回数を元の値であるゼロにリセットする
リセットが呼び出された場合に常に通知を送信する
リセット操作により発行される通知は、クラスAttributeChangeNotification
のインスタンスであり、リセットの呼出し前にState
属性で実行された変更数に関する情報を収集します。送信される通知の内容は、MBeanNotificationInfo
インスタンスで定義されます。
SimpleDynamic
クラスを、コード例3-8 MBeanの例題クラスSimpleDynamic.java
に示します。
コード例3-8 MBeanの例題クラスSimpleDynamic.java
public class SimpleDynamic extends NotificationBroadcasterSupport implements DynamicMBean { public SimpleDynamic() { buildDynamicMBeanInfo(); } [...]
SimpleDynamic
の動的MBeanでは、DynamicMBean
インタフェースを実装することで、実行時に管理用の属性と操作を開示する方法を示します。ここでは、まず、MBeanに関する情報を動的に取得するためのメソッドbuildDynamicMBeanInfo()
の定義から開始します。buildDynamicMBeanInfo()
メソッドは、動的MBeanにMBeanInfo
を構築します。
SimpleDynamic
のほかのコードは、DynamicMBean
インタフェースの実装に対応しています。開示される属性、操作、通知は、SimpleStandard
MBeanで開示されるものと同じです。
ClientListener.java
クラスをコード例3-9 MBeanの例題クラスClientListener.java
に示します。
コード例3-9 MBean例題クラスClientListener.java
public class ClientListener implements NotificationListener { public void handleNotification(Notification notification, Object handback) { System.out.println("\nReceived notification: " + notification); } }
ClientListener
クラスは、簡単なJMX仕様の通知リスナーを実装します。通知が受領されると、NotificationListener
インタフェースのhandleNotification()
メソッドが呼び出され、通知の受領を確認するためのメッセージが出力されます。
Client.java
クラスをコード例3-10 MBeanの例題クラスClient.java
に示します。
コード例3-10 MBean例題クラスClient.java
public class Client { public static void main(String[] args) { try { // Create an RMI connector client // JMXServiceURL url = new JMXServiceURL( "service:jmx:rmi:///jndi/rmi://localhost:9999/server"); JMXConnector jmxc = JMXConnectorFactory.connect(url, null); ClientListener listener = new ClientListener(); MBeanServerConnection mbsc = jmxc.getMBeanServerConnection(); waitForEnterPressed(); // Get domains from MBeanServer // String domains[] = mbsc.getDomains(); for (int i = 0; i < domains.length; i++) { System.out.println("Domain[" + i + "] = " + domains[i]); } waitForEnterPressed(); String domain = mbsc.getDefaultDomain(); // Create SimpleStandard MBean ObjectName mbeanName = new ObjectName(domain +":type=SimpleStandard,name=2"); mbsc.createMBean("SimpleStandard", stdMBeanName, null, null); waitForEnterPressed(); // Create SimpleDynamic MBean ObjectName dynMBeanName = new ObjectName(domain +":type=SimpleDynamic,name=2"); echo("\nCreate SimpleDynamic MBean..."); mbsc.createMBean("SimpleDynamic", dynMBeanName, null, null); waitForEnterPressed(); // Get MBean count echo("\nMBean count = " + mbsc.getMBeanCount()); // Query MBean names echo("\nQuery MBeanServer MBeans:"); Set names = mbsc.queryNames(null, null); for (Iterator i = names.iterator(); i.hasNext(); ) { echo( "ObjectName = " + (ObjectName) i.next()); } waitForEnterPressed(); mbsc.setAttribute(stdMBeanName, new Attribute("State", "changed state")); SimpleStandardMBean proxy = JMX.newMBeanProxy( mbsc, stdMBeanName, SimpleStandardMBean.class, true); echo("\nState = " + proxy.getState()); ClientListener listener = new ClientListener(); mbsc.addNotificationListener(stdMBeanName, listener, null, null); mbsc.invoke(stdMBeanName, "reset", null, null); mbsc.removeNotificationListener(stdMBeanName, listener); mbsc.unregisterMBean(stdMBeanName); [...] jmxc.close(); } catch (Exception e) { e.printStackTrace(); } } } [...]
Client.java
クラスは、RMIコネクタ・クライアントを作成します。このクライアントは、Server.java
で作成されるRMIコネクタ・サーバーへの接続用に構成されます。Client.java
は、Server.java
.で定義されるのと同じサービスURL url
を定義します。これによって、コネクタ・クライアントは、ローカル・ホストのポート9999
で動作するRMIレジストリからRMIコネクタ・サーバー・スタブserver
を取得し、RMIコネクタ・サーバーに接続することができます。
RMIレジストリが特定された場合、コネクタ・クライアントを作成できます。コネクタ・クライアントjmxc
は、JMXConnectorFactory
のconnect()
メソッドにより作成されたインタフェースJMXConnector
のインスタンスです。connect()
メソッドは、呼び出されると、パラメータurl
とnull
環境マップが渡されます。
またクライアントは、MBeanの例題のClientListener.javaに示すように、ClientListener
のインスタンスを作成して通知を待機します。
次に、JMXConnector
インスタンスjmxc
のgetMBeanServerConnection()
メソッドを呼び出して、JMX仕様MBeanServerConnection
のインスタンスmbsc
が作成されます。
これによってコネクタ・クライアントはServer.java
で作成されたMBeanサーバーに接続され、両端への接続が完全に透過的な状態にあればMBeanの登録とMBeanでの操作が可能になります。
クライアントは、MBeanServerConnection
のcreateMBean()
メソッドを呼び出して、MBeanサーバーにSimpleStandard
MBeanとSimpleDynamic MBeanを作成および登録します。そして、SimpleStandard
とSimpleDynamic
で定義された操作を、ローカルJMX仕様のMBean操作とまったく同様に実行します。
MBeanプロキシによりJavaインタフェースを介してMBeanにアクセスできるため、長いコードを記述しなくても、プロキシ上で呼び出してリモートMBeanにアクセスできます。SimpleStandardMBean
のMBeanプロキシをここで作成するには、javax.management.JMX
クラスのnewMBeanProxy()
メソッドを呼び出し、MBeanのMBeanServerConnection
、オブジェクト名、MBeanインタフェースのクラス名、およびtrueを渡して、このプロキシがNotificationBroadcaster
として動作する必要があることを知らせます。MXBeanのプロキシを標準MBeanの場合とまったく同じ方法で作成するには、newMBeanProxy()
ではなくnewMXBeanProxy()
を呼び出します。
SimpleDynamic
で実行される各操作は、SimpleStandard
で実行されるものと同じであるため、ここでは操作のコードを記載していません。
最後に、クライアントはSimpleStandard
MBeanの登録を解除し、接続を終了します。最後のremoveNotificationListener
はオプションです。これはリモート・クライアントで登録されたリスナーは、そのクライアントの終了時に削除されるためです。
例題のクラスを検証した後、例題を実行できます。例題を実行するには、次の手順を実行します。
$ javac *.java
9999
でRMIレジストリを起動します。 RMIレジストリは、Server
クラスによってRMIコネクタ・スタブの登録に使用されます。
$ rmiregistry 9999 &
Server
クラスを起動します。 $ java -classpath . Server
MBeanサーバーの作成、およびMBeanサーバーでのSimpleStandard
MBeanの作成を確認するプロンプトが表示されます。次にSimpleStandard
MBeanに関する情報を取得し、このMBeanで操作を実行する場合は、Enterキーを押すように要求されます。
SimpleStandard
での操作が終了した後、SimpleDynamic
MBeanについて、同じプロセスが繰り返されます。
両方のMBeanが作成されてその操作が実行された後、RMIコネクタ・サーバーが作成され、リモートのClient
からMBeanでの操作が可能になります。
Client
クラスを起動します。 $ java -classpath . Client
RMIコネクタの作成、およびコネクタ・サーバーとの接続の作成の確認が表示されます。ドメイン名と、SimpleStandard
およびSimpleDynamicのMBeanの作成と登録についても通知されます。クライアントは、SimpleStandard
とSimpleDynamicの両MBeanで操作を実行し、その後、両MBeanの登録を解除します。