この章では、標準および動的な管理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.javaSimpleStandardMBean.javaSimpleStandard.javaSimpleDynamic.javaClientListener.javaClient.javaREADME各*.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の登録を解除します。