第二章 JMX 版本的 “Hello World”
(想象你是個(gè)音樂(lè )發(fā)燒友)你決定買(mǎi)一套音響設備,于是先到音響店,挑了一個(gè)自己喜歡的型號帶回家。接下來(lái),你是哪種類(lèi)型的人呢?第一種:仔細的打開(kāi)所有的包裝、檢查零件清單,然后順著(zhù)組裝手冊一步一步的組裝;第二種:打開(kāi)所有包裝、然后自己開(kāi)始琢磨著(zhù)所有的連接線(xiàn)開(kāi)始組裝。本章是為第二種類(lèi)型的人寫(xiě)的,如果你是第一種類(lèi)型的人,請確信你是否讀了第一章,它描述了JMX 框架和基本架構(本章會(huì )重點(diǎn)描述)。
本章的目的是為了讓你熟悉由Sun 提高的JMX 參考實(shí)現( Reference Implementation :RI )。學(xué)習完本章,你將會(huì )學(xué)會(huì )管理你的第一個(gè)資源,創(chuàng )建一個(gè)JMX 代理,并通過(guò)瀏覽器和代理通信。換句話(huà)說(shuō),你將新建一個(gè)MBean ,使用MBean Server ,通過(guò)Sun 提供的參考實(shí)現里的HTML 適配器來(lái)管理你的MBean 。
本書(shū)的剩余部分假定你已經(jīng)有JDK1.3 (最低版本)安裝在你的機器上了,并且你正確的設置了環(huán)境變量。如果需要,你可以從
http://www.javasoft.com上下載一個(gè)JDK 。
開(kāi)始吧
在開(kāi)始繼續之前,讓我們簡(jiǎn)單的回顧一下JMX 的架構,然后建立一個(gè)開(kāi)發(fā)環(huán)境。
2.1.1 JMX 架構回顧
第一章詳細的描述了JMX 的架構,并且討論了它是怎樣提供一個(gè)管理解決方案的。然而為了確保你掌握了第一章介紹的知識,我們先來(lái)回顧一下吧。JMX 架構設計為一個(gè)包含三部分或三層的Java 框架,它們一塊工作從而提供一個(gè)java 的管理解決方案。表2.1 列出了JMX 架構的三層。
2.1 JMX 架構的三層組件
層
描述
分布層( Distributed layer )
包含使管理系統和 JMX 代理通信的組件
代理層( Agent layer )
包括代理和 MBean 服務(wù)器
指令層( Instrumentation layer )
包括代表可管理資源的 MBean
圖 2.1 說(shuō)明三層怎樣協(xié)同工作
圖 2.1 三層協(xié)同工作
本章你將會(huì )同三層中的各各層的組件打交道。在指令層,將會(huì )使用一個(gè)MBean ,MBean 是一個(gè)Java 對象,它封裝了一個(gè)資源并將它暴露給管理程序。在代理層,將會(huì )使用一個(gè)JMX 代理,實(shí)際上,你會(huì )寫(xiě)一個(gè)你自己的代理,它包含你的MBean 。最后,在分布層,將會(huì )使用HTML 適配器,它是一個(gè)java 對象,它允許管理應用將HTML 作為一個(gè)通信協(xié)議來(lái)和你的代理通信。管理應用是任何對侵入、配置、操縱可管理資源感興趣的應用。
2.1.2 設置開(kāi)發(fā)環(huán)境
如果你沒(méi)有Sun 的JMX RI ,可以從
http://www.javasoft.com 上面下載,你可以在Products and APIs 的下載區域找到它。下載了zip 文件后,解壓到磁盤(pán),會(huì )產(chǎn)生一個(gè)JMX 的父目錄,其中包含以下目錄:
§ contrib— — 包括Sun 不聲明支持的分發(fā)。比如,Sun 在其中提供了一個(gè)RMI 連接器,或者說(shuō)適配器,允許管理應用通過(guò)RMI 和JMX 代理通信。
§ jmx— 包括JMX RI/ 例子和文檔
在本書(shū)的剩下部分,你需要將你的java 源文件放在JMXBook 目錄下,然后可以使用一個(gè)批處理來(lái)設置path 和classpath ,以便于從JMXBook 下編譯和運行例子。批處理文件包含以下內容:
set CLASSPATH=c:/JMXBook;C:/jmx-1_0_1-ribin/jmx/lib/jmxri.jar;
C:/jmx-1_0_1-ribin/jmx/lib/jmxtools.jar;
C:/jmx-1_0_1-ri-bin/contrib/remoting/jar/jmx_remoting.jar;
set PATH=c:/jdk1.3/bin
批處理文件用于在Windows 環(huán)境下設置JMX 環(huán)境來(lái)編譯和運行例子,如果你使用Unix ,你需要相應的修改腳本。如你在腳本所見(jiàn),我們使用JDK1.3 ,但是每一個(gè)例子都可以運行在任何J2SE 版本。當工作在命令行時(shí),作進(jìn)一步工作前,你需要先運行這個(gè)腳本。運行設置腳本后,可以輸入java javax.management.ObjectName 來(lái)測試CLASSPATH 設置是否正確,如果你得到一個(gè)錯誤,告訴你此類(lèi)中沒(méi)有main 方法,那么恭喜你,你設對了。
使用ant: 對于熟悉ant 的讀者,在附錄B 中有一個(gè)ant 構建的設置,在哪里還有ant xml 構建文檔以及相關(guān)的環(huán)境設置信息。
譯者注:個(gè)人理解JMX RI 現在應該由JMDK 替代了,所以請下載JMDK 就可以使用本書(shū)的例子了。JMDK 下載:java.sun.com/products/jdmk/index.jsp 。下載opendmk-1.0-b02-bin-dual-01-Oct-2007_19-17-46.jar ,然后加壓,按照README 的操作,在命令行下運行:java -jar opendmk-1.0-b02-bin-dual-01-Oct-2007_19-17-46.jar ,就可以得到j(luò )dmkrt.jar 、jdmktk.jar 、jmxremote_optional.jar 三個(gè)jar ,將他們放在你classpth 中。
管理你的第一個(gè)資源
到現在為止,我們已經(jīng)簡(jiǎn)單的回顧了JMX 的主要組件,并且你已經(jīng)有一個(gè)開(kāi)發(fā)環(huán)境了。接下來(lái)準備構建你的第一個(gè)MBean 吧。本章只是對MBena 的一個(gè)簡(jiǎn)介,更復雜的例子將會(huì )在以后的章節中描述。
第一個(gè)例子,你將會(huì )構建一個(gè)簡(jiǎn)單的HelloWorld MBean 。HelloWorld MBean 暴露一個(gè)簡(jiǎn)單的Java String 對象作為被管理的資源,它僅是是一個(gè)成員變量。我們使用這個(gè)例子作為一個(gè)工具,向你介紹使用JMX 的方方面面,包括MBean 、MBean Server 、HTML Adapter 。需要強調的是,可管理資源是任何通過(guò)Mbean 可侵入和可管理的資源。(本章中,我們不需要擔心Mbean 的編碼標準。還記得第一章曾經(jīng)介紹過(guò)嗎?本書(shū)會(huì )介紹三種類(lèi)型的Mbean :標準Mbean 、動(dòng)態(tài)MBean 和模型MBean 。但是本章只會(huì )創(chuàng )建一個(gè)標準MBean ,MBean 的具體開(kāi)發(fā)規則會(huì )在以后的章節中心詳細描述)圖2.2 顯示了HelloWorld MBean 的UML 圖。
HelloWorld
下一節開(kāi)始討論圖中描述的接口和實(shí)現。
2.2.1 寫(xiě) HelloWorld MBean
開(kāi)發(fā)HelloWorld MBean 第一步是寫(xiě)以哦Java 接口,包含三個(gè)方法:getter 、setter 和一個(gè)打印HelloWorld MBean 的greeting 的方法。通常,你可能不需要為如此簡(jiǎn)單的HelloWorld 例子定義一個(gè)接口,但是你將會(huì )在第4 章和第5 章看到,JMX 使用接口來(lái)描述Mbean 暴露的屬性和操作。
回憶一下,getter 方法是類(lèi)中以getMember() 命名的方法,setter 是以setMember() 命名的方法。將標準Mbean 接口中的方法想象成具體類(lèi)實(shí)現的一個(gè)描述,你只需要簡(jiǎn)單的通過(guò)名字理解它的意圖就可以了。Getter 和setter 定義了使用Mbean 的對象的成員變量的訪(fǎng)問(wèn)方式。通過(guò)成員變量的getter 方法,可以允許讀操作,setter 方法云訊寫(xiě)操作。如同你看到的下面的接口,這個(gè)MBean 十分簡(jiǎn)單。
package jmxbook.ch2;
public interface HelloWorldMBean
{
public void setGreeting( String greeting );
public String getGreeting();
public void printGreeting ();
}
注意package 語(yǔ)句,本章的所有例子都在包jmxbook.ch2 下,其他章節的例子也都在對于包下(例如第三章在mxbook.ch3 下)
HelloWorldMBean 接口定義了一個(gè)getter 方法 (getGreeting()) 和一個(gè)setter 方法(setGreeting()) 以及 printGreeting () 方法。隨后你可以使用printGreeting() 方法答應Mbean 的greeting 的值。2.1 展示了接口
Listing 2.1 HelloWorld .java
package jmxbook.ch2;
public class HelloWorld implements HelloWorldMBean |#1
{
private String greeting = null;
public HelloWorld () |#2
{
this.greeting = "Hello World! I am a Standard MBean "; |#2
} |#2
public HelloWorld ( String greeting ) |#2
{
this.greeting = greeting; |#2
} |#2
public void setGreeting( String greeting )
{
this.greeting = greeting;
}
public String getGreeting()
{
return greeting;
}
public void printGreeting ()
{
System.out.println( greeting );
}
}
( 注解)<#1 實(shí)現HelloWorldMBean 接口 >
( 注解)<#2 定義兩個(gè)公共構造函數>
如此就新建了你的第一個(gè)MBean ,為了測試它,需要建立一個(gè)JMX 代理來(lái)容納這個(gè)MBean ,下一節討論HelloAgent 類(lèi)的創(chuàng )建。創(chuàng )建了代理后就可以使用Mbean 了。
迄今為止Mbean 已經(jīng)有了,我們需要把它注冊在一個(gè)代理中使它可用。所以我們新建一個(gè)HelloAgent 類(lèi),它是一個(gè)簡(jiǎn)單的JMX 代理。
正如第一章說(shuō)的那樣,JMX 代理是JMX 代理層的一個(gè)組件,它是Mbean 容器。本書(shū)第三部分會(huì )詳細描述JMX 代理。
HelloAgent 會(huì )做三件事:
§ 1. 創(chuàng )建一個(gè)Mbean 實(shí)例來(lái)放MBean
§ 2. 創(chuàng )建一個(gè) HTML adapter 來(lái)處理從HTML 客戶(hù)端來(lái)的連接
§ 3. 注冊一個(gè)HelloWorld MBean 的實(shí)例
如你將要看到的,HelloAgent 可能是你寫(xiě)的最簡(jiǎn)單的代理,但是它很有用。這樣反應了使用JMX 的一個(gè)最主要的點(diǎn):它非常有用但是卻很簡(jiǎn)單。剛剛已經(jīng)開(kāi)發(fā)了HelloWorld MBean ,那么現在可以寫(xiě)一個(gè)簡(jiǎn)單的代理來(lái)管理這個(gè)MBean ,代理使用Sun 提供的HTML adapter 。
使用任一個(gè)瀏覽器,適配器使得你可以和代理交互,以查看注冊在它里面的MBean 和它們的屬性。圖2.3 顯示了這個(gè)交互。
Figure 2.3 Using a Web browser to contact the HTML adapter present in the MBean server
適配器可以讓你:
§ 1. 查看可讀的MBean 屬性
§ 2. 更新可寫(xiě)的MBean 屬性
§ 3. 調用一些方法
不僅僅如此,適配器同時(shí)向你提供了動(dòng)態(tài)創(chuàng )建和注冊額外的Mbean 的一個(gè)簡(jiǎn)便方法。最基本的,適配器提供了一個(gè)簡(jiǎn)單的管理工具來(lái)操縱MBean 。HTML 適配器返回一個(gè)協(xié)議(HTML ),使得你的瀏覽器成為一個(gè)有用的管理應用。但是我們還是別太超前了,首先來(lái)創(chuàng )建JMX 代理吧。
2.3.1 創(chuàng )建 HelloAgent
Listing 2.2 是HelloAgent 類(lèi),先別擔心看不懂這些代碼,在本書(shū)的第三部會(huì )詳細的接受JMX 代理?,F在,你只需要簡(jiǎn)單的理解就可以了,簡(jiǎn)而言之,listing 2.2 做了如下工作:
1 1. 創(chuàng )建了一個(gè)MBean Server 和一個(gè)HTML adapter
2 2. 注冊了MBean (因此你才管理MBean )
3 3. 給了Mbean 一個(gè)唯一標識符
4. 注冊HTML adapter 然后運行它
Listing 2.2 HelloAgent .java
package jmxbook.ch2;
import javax.management.*;
import com.sun.jdmk.comm.*;
public class HelloAgent {
private MBeanServer mbs = null;
public HelloAgent () {
mbs = MBeanServerFactory .createMBeanServer( "HelloAgent " ); |#1
HtmlAdaptorServer adapter = new HtmlAdaptorServer(); |#2
HelloWorld hw = new HelloWorld(); |#3
ObjectName adapterName = null;
ObjectName helloWorldName = null;
try {
helloWorldName = |#4
new ObjectName ( "HelloAgent :name=helloWorld1" ); |#4
mbs.registerMBean( hw, helloWorldName ); |#4
adapterName = |#5
new ObjectName ( "HelloAgent :name=htmladapter,port=9092" ); |#5
adapter.setPort( 9092 );
mbs.registerMBean( adapter, adapterName ); |#5
adapter.start(); |#5
} catch( Exception e ) {
e.printStackTrace();
}
}
public static void main( String args[] ) {
System.out.println( "HelloAgent is running" );
HelloAgent agent = new HelloAgent();
}
}//class
( 注解 )<#1,#2 創(chuàng )建MBean Server 和 HTML adapter >
( 注解 )<#3 創(chuàng )建 HelloWorld MBean 實(shí)例 >
( 注解 )<#4 創(chuàng )建 ObjectName 實(shí)例 ; 注冊 HelloWorld MBean >
( 注解 )<#5 注冊并運行 HTML adapter MBean >
創(chuàng )建 MBean Server 和 HTML 適配器
#1 HelloAgent 類(lèi)包含一個(gè)main方法,它可以使HelloAgent 作為一個(gè)獨立的進(jìn)程運行。Main方法只是簡(jiǎn)單調用HelloAgent 的構造方法,因此讓我們從這兒開(kāi)始分析代碼吧。HelloAgent構造方法的第一步是新建了一個(gè)MBean Server。
還記得第一章說(shuō)過(guò)的嗎?MBean Server是一個(gè)Java對象,它用來(lái)容納和操縱JMX MBean。MBean Server是一個(gè)標準的JMX類(lèi),它是JMX代理的核心。代理使用javax.management.MBeanServerFactory 得到一個(gè)MBeanServer 實(shí)例,MBeanServerFactory是一個(gè)實(shí)現為工廠(chǎng)模式的JMX類(lèi),它提供MBeanServer的具體實(shí)例。當你需要一個(gè)MBeanServer實(shí)例時(shí),就可以使用工廠(chǎng)對象來(lái)新建或是請求一個(gè)新實(shí)例。工廠(chǎng)可以管理很多MBeanServer實(shí)例,調用它就會(huì )返回一個(gè)已存在的或是新建的實(shí)例(如HelloAgent中那樣 )。
注意createMBeanServer()的參數“HelloAgent ”,它標識了這個(gè)代理的域名。域名是一個(gè)唯一的標識符,用于標識一組MBean,它唯一的標識了這個(gè)MBeanServer 以區別域其他的MBeanServer 。每一個(gè)MBeanServer都有一個(gè)域名,使得你可以一種有特定意義的方式來(lái)分組MBean。如果你再次使用相同的域名參數調用createMBeanServer()方法,那么它將會(huì )只 簡(jiǎn) 單的返回剛才創(chuàng )建的那個(gè)MBeanServer實(shí)例。(關(guān)于MBeanServer的,在第8章將會(huì )學(xué)到更多?,F在你只需要知道MBeanServer扮演了注冊表的角色,通過(guò)它可以存儲、查找、操縱MBean)
#2 下一步是為管理應用創(chuàng )建一種方式來(lái)和HelloAgent交互?;叵胍幌?,代理通過(guò)構造協(xié)議適配器和連接器來(lái)向管理應用暴露自己。適配器和連接器是JMX為啥如此有用的一個(gè)主要原因。適配器和連接器是Java對象,它使得應用可以使用特定的協(xié)議和JMX代理交互(在本書(shū)的后面,你將會(huì )學(xué)習到更多關(guān)于他們的信息,并創(chuàng )建更復雜的例子)。本章只會(huì )用到HTML適配器,只需要調用它的默認構造方法就可以新建一個(gè)適配器,如你在HelloAgent類(lèi)中看到的那樣。
MBean
#4 一旦適配器創(chuàng )建了,你需要在MBeanServer里注冊它。這說(shuō)明適配器(連接器)也是MBean,因此組成適配器和連接器的java類(lèi)被寫(xiě)成符合JMX規范的某一種類(lèi)型的MBean。因為它們也是Mbean,所以在運行期像其他的Mbean一樣可以被管理。但是在注冊一個(gè)Mbean之前,你需要確保你可以標識它和找到它,這就是javax.management.ObjectName的功能了。
現在為止,代理已經(jīng)創(chuàng )建了MBeanServer并注冊了HTML適配器。接下來(lái)需要知道MBeanServer是怎樣對注冊在它里面的對象保持追蹤了?;乜匆幌略贛BeanServer上注冊HelloWorld MBean的代碼(#4)。當注冊Mbean時(shí),需要使它和那些已經(jīng)注冊的MBean區別開(kāi)來(lái)。
要做到這點(diǎn),必須新建一個(gè)javax.management.ObjectName 實(shí)例。ObjectName是一個(gè)JMX類(lèi)用來(lái)為MBean提供命名服務(wù),用于唯一的標識每一個(gè)注冊在它里面的MBean。每個(gè)ObjectName包括兩部分:
§ 一 個(gè)域名—域名一般是MBean想注冊的那個(gè)MBeanServer 的域名。如果不是這樣,就意味著(zhù)想把這個(gè)MBean和其他的MBean隔離開(kāi)來(lái)。
§ 一 個(gè) 名值對屬性列表—名值對屬性用于唯一標識MBean,以及提供MBean的信息。對象名也許是用戶(hù)首先看到的關(guān)于MBean的信息??梢允敲祵傩蕴峁┲T如名字、端口、位置此類(lèi)的信息。
例子中 HelloWorld MBean 的ObjectName 如下所示
"HelloAgent :name=helloWorld1"
這樣你就有一個(gè)ObjectName 實(shí)例,一旦注冊就可以標識和查找到這個(gè)MBean了。
#5 如前所述, 代理為適配器新建了一個(gè) ObjectName 并且把它注冊在MBeanServer 里。因為它們是MBean,所以每個(gè)適配器都可以選擇向管理應用暴露一些可以配置的屬性。
盡管適配器已近創(chuàng )建和注冊了,管理應用仍然不可以和代理交互。要使客戶(hù)端可以使用適配器,必須調用它的start()方法。Start()方法告訴適配器MBean開(kāi)始在默認端口9092監聽(tīng)HTTP連接?,F在HelloAgent 已經(jīng)準備奧接受客戶(hù)段的請求了。
2.3.2 更多關(guān)于 object name
在前一節中我們簡(jiǎn)要的描述了ObjectName 。為了有一個(gè)全面的了解,讓我們回到ObjectName 列上。就如你注意的那樣,在創(chuàng )建ObjectName時(shí)必須有一個(gè)特定的結構。
圖 2.4顯示了 ObjectName 的結構:
圖 2.4 ObjectName 的結構
域名
域名提供了代理和其他代理聯(lián)系的上下文。例如,一個(gè)代理可能創(chuàng )建用來(lái)在一個(gè)特定的機器上容納MBean管理資源,這種情況下,域名可能是機器的hostname。一個(gè)域名不一定非得如hostname那樣有明確含義的,但一般來(lái)說(shuō),你應噶盡量提供一些有意的名字。如此,你就可以跟容易的了解ObjectName 和MBean更多的信息。
圖2.4中,域名甚至可以不指定,如果左邊是空的,MBean Server將會(huì )提供一個(gè)默認的域名。對MBeanServerFactory類(lèi)也是同樣的,如果使用不帶域名參數的createMBeanServer(),工廠(chǎng)類(lèi)將返回一個(gè)使用默認域名的MBeanServer 。
差不多現在你已經(jīng)注意到MbeanServer對象和Mbean是關(guān)聯(lián)在一個(gè)域中的(通過(guò)ObjectName)。但是實(shí)際上,某個(gè)域的MBean可以在另一個(gè)有不同域名的MBeanServer上注冊。這種情況也是可接受的,因為域名并沒(méi)有對注冊在一個(gè)MBeanServer上的Mbean附加任何規則和約束。
對象名的名值對屬性列表是一系列以逗號分隔的屬性值,它們提供了唯一標識MBean server中MBean的機制。屬性不要求是MBean的精確屬性,唯一要求是當它和ObjectName實(shí)例比較時(shí)必須是唯一的。對每一個(gè)ObjectName,你必須指定最少ige屬性值以使它和Mbean Server中的其他MBean可唯一區分。
ObjectName提供三個(gè)使用String參數的構造方法。在HelloAgent如下新建ObjectName :
helloWorldName = new ObjectName ( "HelloAgent :name=helloWorld1" );
這個(gè) ObjectName使用name=helloWorld 名值對來(lái)唯一標識HelloWorld MBean。如果你注冊其他的MBean,你不可以再使用這個(gè)名值對,你需要其他的屬性。
object name 沖突
為了注冊MBean,HelloAgent 類(lèi)調用MBeanServer #registerMBean()方法。如果ObjectName不是唯一的,MBeanServer將會(huì )拋出一個(gè)javax.management.InstanceAlreadyExistsException異常,來(lái)說(shuō)明已經(jīng)有一個(gè)相同ObjectName的MBean被注冊了。MBeanServer不會(huì )比較ObjectName關(guān)聯(lián)的對象時(shí)候相等,它只比較關(guān)聯(lián)它們的ObjectName。
運行代理
我們檢查一下到現在為止你做了那些事。首先你創(chuàng )建MBean,包含在HelloWorld類(lèi)中,它向外暴露一個(gè)屬性:greeting 作為被管理的資源。接著(zhù)創(chuàng )建了HelloAgent 類(lèi),它是一個(gè)簡(jiǎn)單的JMX代理。代理將會(huì )容納MBean并提供管理MBean的方式。剩下的工作就是:編譯、運行、和代理交互了。
2.4.1 編譯代理
為了運行代理,你需要編譯源碼然后運行HelloAgent 。為了編譯源碼,在確認你的環(huán)境(CLASSPATH和其他)設置正確后,然后執行如下的命令:
javac jmxbook/ch2/*.java
2.4.2 運行代理
下面的命令會(huì )運行 HelloAgent :
java jmxbook.ch2.HelloAgent
執行這個(gè)命令后,你的代理已經(jīng)啟動(dòng)了。 命令行將不會(huì )返回,因為HelloAgent 進(jìn)程沒(méi)有推出。你會(huì )看到:“HelloAgent is running,表示代理已經(jīng)開(kāi)始運行了。
2.4.3 連接代理
連接代理,你需要一個(gè)HTML 客戶(hù)端,任何瀏覽器都可以作為一個(gè)客戶(hù)端。例如,HelloAgent 的HTML 適配器默認在端口9092 監聽(tīng)連接。如果你的環(huán)境上這個(gè)端口不可以用,那么回到源碼適配器構造方法下面一行,修改成可用的端口:
adapter.setPort( [port value ] );
確保端口可用,本書(shū)的剩余部分我們一直在端口9092 上使用HTML 適配器。
一旦已經(jīng)有一個(gè)可用的端口了,打開(kāi)你的瀏覽器,輸入
http://localhost:9092 (如果你指定了不同的端口,請使用它替換9092 ).
運行在代理上的HTML 適配器現在指定的端口監聽(tīng)HTTP 請求,當你的瀏覽器連接時(shí),適配器將返回HTML 作為響應?,F在你可以管理代理,憑借HTML 和代理來(lái)進(jìn)行交互。
繼續之前,你可以恭喜自己了,你已經(jīng)成功的創(chuàng )建了你的第一個(gè)MBean 和代理,下一節你將會(huì )使用HTML 適配器和你的代理通信了。
使用 HTML 適配器
現在HTML 適配器已經(jīng)OK ,它運行在包含MBean 的代理上,是時(shí)候連上它,看看它都為你提供了些什么東東。HTML 適配器提供了通過(guò)HTML 客戶(hù)端(任何瀏覽器)侵入JMX 代理的方式,它包括三個(gè)主要頁(yè)面:
§ 代理視圖 — 代理視圖是你看見(jiàn)的第一個(gè)頁(yè)面,它上面有這個(gè)代理容納的MBean 的總結,從這個(gè)頁(yè)面你能過(guò)濾MBean 列表以提供更為精確的視圖。
§ MBean 視圖 — 這個(gè)頁(yè)面提供了一個(gè)特定MBean 的詳細信息。通過(guò)這個(gè)頁(yè)面,你可以設置、取得MBean 的屬性以及調用MBean 的方法。
§ 管理視圖 — 這個(gè)頁(yè)面使得你可以在代理中注冊一個(gè)新的MBean 。
2.5.1 代理視圖
瀏覽器連接代理后,代理視圖就出現了(圖2.5 ). 代理視圖是一個(gè)從代理的HTML 適配器得到的HTML 頁(yè)面,它向你展示了所有注冊在這個(gè)代理上的MBean ,以MBean 的ObjectName 的值顯示。從這個(gè)頁(yè)面,你可以遷移到MBean 視圖或管理視圖。在進(jìn)入其他頁(yè)面之前,注意頁(yè)面頂部的“Filter by object Name ”欄,現在顯示*:* ,它告訴代理返回所有的注冊在代理中的MBean 列表。頁(yè)面上MBean 數目顯示了每次查詢(xún)返回的MBean 的總數。
Figure 2.5 代理視圖
過(guò)濾器允許使用對象名的一部分或是全部來(lái)過(guò)濾MBean 列表。比如說(shuō),你輸入HelloAgent :name=helloWorld1 ,列表將只顯示HelloWorld MBean. 可以使用下述的規則來(lái)輸入對象名:
2.2 代理視圖過(guò)濾器規則
MBean filter rule
Example
使用 * 作為字母和數字的多個(gè)字符的通配符,可以作為名值對的通配符
*:name=helloWorld1,*
使用作為 單個(gè)字符的通配符
Agent:name=helloWorld1
如果不指定域名,過(guò)濾器將使用默認域名,你必須至少指定一個(gè)名值對(或使用 * )
*:*
域名的一部分是合法,但是不可以指定名值對的部分 ( 對 key 和 value 均如此 )
Agent:name=helloWorld1
所有的關(guān)鍵字都精確匹配或使用通配符
Agent:*
名值對可以以任意順序指定
試著(zhù)自己使用過(guò)濾器,這樣有助于你掌握ObjectName 的格式。試完之后,將filter 恢復為*:* ,你就可以看到所有的MBean 列表了。
注意MBean 列表中有一個(gè)在2.4 節沒(méi)有注冊過(guò)的MBean :MBeanServerDelegate ,它是一個(gè)由MBeanServer 創(chuàng )建的MBean ,用來(lái)處理特定的任務(wù)--- 特別地,是用來(lái)為MBeanServer 發(fā)送通知用的。MBeanServer 將這個(gè)MBean 注冊在一個(gè)獨立的域中,以區別其他注冊的MBean 。
所有的MBean 都是可見(jiàn)的,單擊MBeanServerDelegate 的連接,會(huì )跳轉到這個(gè)MBean 的視圖。
2.5.2 MBean 視圖
MBean 視圖是另一個(gè)從HTML 適配器接受到HTML 頁(yè)面,它顯示你點(diǎn)擊的MBean 的信息。視圖顯示了被選擇的MBean 的所有的包含表2.3 中的信息。
2.3 MBean 視圖的元素
MBean detail
Description or example
類(lèi)名
MBean 的類(lèi)名 , 比如 HelloWorld
對象名
MBean 的 ObjectName , 比如 HelloAgent: name=helloWorld1
描述
MBean 的描述。對于標準 MBean ,由 MBeanServer 創(chuàng )建這些描述
屬性表
MBean 暴露的屬性列表,包括屬性的類(lèi)型、侵入和值,如果有的話(huà)。屬性表運行你更改可寫(xiě)的屬性。
暴露的操作
MBean 暴露的操作列表,你可以觸發(fā)這些屬性
重新載入周期
告訴 MBeanServer 是否需要重新實(shí)例化 MBean 以及周期
解除注冊按鈕
告訴 MBeanServer 解除對這個(gè) Mbean 的注冊
Figure 2.6 MBeanServerDelegate MBean 的視圖
Figure 2.6 MBean 視圖
看一下圖2.6 的MBean 屬性表,注意Access 列,當前都是RO ,代表著(zhù)只讀(Read Only ),其他可能的值有:WO ( 只寫(xiě):Write Only) 和 RW ( 可讀寫(xiě):Read/Write) 。如你所猜想的那樣,RO 意味著(zhù)MBean 的接口為此屬性值提供了getter 方法,WO 意味只提供了setter 方法,RW 意味這提供了getter 和setter 方法。
HTML 適配器使用反射檢查接口的方法名,它去除了每一個(gè)方法前的get 和set ,使用剩下的部分作為屬性名,余下的方法(那些不一set 或get 開(kāi)頭的方法)被放進(jìn)MBean 視圖的操作部分(即:List of MBean operations )
回看圖 2.6 ,你可以看到MBeanServerDelegate 只暴露了可讀屬性,這些屬性描述了正在使用的JMX RI 以及它實(shí)現的是那個(gè)版本的JMX 規范。
讓我們回到代理視圖,點(diǎn)擊代理視圖,這次選擇HelloWorld MBean ,如圖2.7 看到的那樣:
Figure 2.7 H elloWorld MBean 視圖
除了兩個(gè)重要的區別外,HelloWorld MBean 視圖和MBeanServerDelegate MBean 是一樣的。HelloWorld MBean 暴露只暴露了一個(gè)屬性:greeting ,(并且它有g(shù)etter 和setter ),意味著(zhù)這個(gè)屬性是可讀可寫(xiě)的,所以視圖的Value 列是可以輸入值的。點(diǎn)擊Apply 按鈕來(lái)提交你做的任何改變,之后頁(yè)面會(huì )刷新,文本域顯示的當前值反應了你剛才做的改變。
現在看MBean 的操作(Operations ),你只看到一個(gè)可用的操作。MBean 視圖為每一個(gè)操作都構建了一個(gè)按鈕,按鈕的名字就是方法名。對于HelloWorld MBean ,你可以看到一個(gè)printGreeting 的按鈕,這是HelloWorldMBean 接口剩下的方法(除去getter 和setter )。按鈕前是void ,它是這個(gè)方法的返回值。如果方法有輸入,你將會(huì )看到每個(gè)輸入對應一個(gè)輸入文本框。
HTML 適配器只為輸入參數提供幾種特定的類(lèi)型,只支持String 、基本類(lèi)型、基本類(lèi)型的外包類(lèi)。
當你點(diǎn)擊printGreeting 按鈕后,可以看到發(fā)生了兩件事,第一: 頁(yè)面遷移到了另一個(gè)頁(yè)面,上面顯示了方法執行成功并且沒(méi)有返回值;第二:看看代理的輸出,你可以看到你剛才輸入的Greeting 屬性的值被打印了出來(lái)。
恭喜你已經(jīng)成功的管理了HelloWorld 。還剩下一個(gè)視圖:管理視圖。返回代理視圖,然后點(diǎn)擊Admin 按鈕,就可以進(jìn)入HelloAgent 的管理視圖了。
2.5.3 管理視圖
使用上兩節的HTML 頁(yè)面,你能配置和查詢(xún)注冊在代理中的MBean 。然而,如果你想不寫(xiě)任何代碼就向代理中添加一個(gè)MBean ,該怎么辦呢?Admin 視圖使得你可以侵入MBeanServer 以移除或添加MBean 。在頁(yè)面上,你可以指定一個(gè)ObjectName 值和一個(gè)MBean 的Java 類(lèi),MBeanServer 將根據你的輸入構建并注冊一個(gè)相應的MBean 。Admin 視圖顯示了4 個(gè)文本域(圖2.8 ):
§ Domain — HTML 適配器使用了代理的默認域。當前顯示HelloAgent ,這是對象名的第一部分。
§ Keys — 需要符合[name=value ],* 的要求 . 這個(gè)框框表示對象名的名值對部分。
§ Java Class — 表示你想創(chuàng )建的MBean 的類(lèi)的全限定名。
§ Class Loader — 是可選的。 你可以為MBeanServer 在試圖加載前面輸入的Java 類(lèi)時(shí),指定一個(gè)特定的類(lèi)加載器。
Figure 2.8 The Admin View presented by the HTML adapter
為了更進(jìn)一步理解這個(gè)頁(yè)面,讓我們創(chuàng )建更多的MBean 。在下一節你將會(huì )看到,在一個(gè)MBeanServer 中,你可以有同一種類(lèi)型的許多MBean ,只要他們的對象名不同。
2.5.4 在 HelloAgent 是注冊 / 解除注冊 MBean
讓我們通過(guò)Admin 視圖加載另一個(gè)HelloWorld MBean 的實(shí)例。域名使用默認的HelloAgent ,在Keys 中輸入name=helloWorld2 ,在Java Class 中輸入jmxbook.ch2.HelloWorld ,它實(shí)現了HelloWorldMBean 接口。
在Java Class 中指定的任何類(lèi)都必須對代理的MBeanServer 是可見(jiàn)的。對HelloAgent ,這就要求輸入的類(lèi)必須在CLASSPATH 中。
Class Loader 空著(zhù) , 告訴 HelloAgent MBeanServer 使用默認的類(lèi)加載器。代理將使用輸入的值構建ObjectName ,像這樣:
HelloAgent :name=helloWorld2
當這些都就緒后,你已經(jīng)準備好了新建一個(gè)HelloWorld 實(shí)例了。在A(yíng)ction選項的下拉框中有三個(gè)選項:
§ Create — 告訴 MBeanServer使用無(wú)參構造方法新建一個(gè)MBean
§ Unregister — 只在你指定的ObjectName 在代理中存在對應的MBean 時(shí)有效
§ Constructors — 向你顯示MBean的構造函數列表
選擇Create選項,點(diǎn)擊Send Request按鈕,你將會(huì )看到一個(gè)成功的消息,告訴你已經(jīng)成功的新建并注冊了一個(gè)新的MBean。返回Agent視圖,確認一下MBean列表。
使用有參數的構造方法
還記得HelloWorldMBean 的實(shí)現類(lèi)HelloWorld 有兩個(gè)構造方法嗎?一個(gè)默認構造方法和一個(gè)有參數的構造方法,參數代表Greeting屬性的初始值。讓我們使用第二個(gè)構造方法再注冊一個(gè)HelloWorld HelloWorld 的實(shí)例吧。
1. 返 回Admin視圖,輸入合適的值來(lái)創(chuàng )建一個(gè)新的HelloWorld MBean 。確保輸入一個(gè)唯一的值(例如name=helloWorld3)。
2. 這 次選擇從Action列表選擇Constructors,然后點(diǎn)擊Send Request按鈕,你會(huì )看到構造方法列表(有兩個(gè)),其中一個(gè)顯示了文本框用來(lái)輸入它的一個(gè)參數 。
3. 輸 Greeting屬性的值,然后點(diǎn)擊和上面的構造方法關(guān)聯(lián)的Create按鈕。如果對象名和類(lèi)名你都輸入對了,你將會(huì )再次看到“creation successful”消息。
4. 返 回代理視圖,確認MBean列表,這次有三個(gè)HelloWorld MBean 實(shí)例了。
使用 MBean 通知
通 過(guò)前面幾節,你已經(jīng)創(chuàng )建和注冊了自己的MBean,對使用JMX來(lái)開(kāi)始工作你已經(jīng)具備了足夠的知識。你已經(jīng)學(xué)習了怎樣創(chuàng )建一個(gè)標準MBean,怎樣把它添加進(jìn)JMX代理,怎樣通過(guò)HTML適配器管理它。但是,你仍然錯過(guò)了一個(gè)關(guān)鍵的部分:通知
JMX通知是Java對象,通過(guò)它可以從MBean和代理向那些注冊了接受通知的對象發(fā)送信息(如圖2.9)。對接受事件感興趣的對象是通知監聽(tīng)器,他們實(shí)現了javax.management.NotificationListener interface接口。
圖 2.9 從 MBean 向一個(gè)注冊的監聽(tīng)器發(fā)生通知
通知是JMX很重要的一部分,因為它們允許事件的傳輸。JMX事件可以是任何事情,從MBean的屬性變更到在MBeanServer注冊一個(gè)新MBean,都可以是JMX事件。
為了給你一個(gè)通知的簡(jiǎn)單介紹,本節將會(huì )將通知添加進(jìn)HelloWorld MBean。在第6章我們將深入學(xué)習通知機制。
2.6.1 在 HelloWorld MBean 中添加通知的代碼
HelloWorld MBean 發(fā)送通知,它得允許其他的對象注冊監聽(tīng)器來(lái)接受通知。JMX支持兩種機制來(lái)為MBean提供監聽(tīng)器以注冊來(lái)接受通知:
§ 實(shí)現 javax.management.NotificationBroadcaster 接口
§ 繼承 javax.management.NotificationBroadcaster Support 類(lèi) ( 它實(shí)現類(lèi)了 NotificationBroadcaster 接口 )
使用接口的好處是你的類(lèi)可以繼承其他的類(lèi),而使用繼承的好處是你不需要為 接口寫(xiě)額外的代碼。如果你的MBena不需要繼承其他的類(lèi),那么可以使用繼承以使用它里面的實(shí)現。HelloWorld 不需要繼承其他的類(lèi),所以就可以自由的使用繼承。如列表2.3所示:
Listing 2.3 HelloWorld .java
package jmxbook.ch2;
import java.io.*;
import javax.management.*;
public class HelloWorld extends NotificationBroadcasterSupport
implements HelloWorldMBean { |#1
public HelloWorld () { |#2
this.greeting = "Hello World! I am a Standard MBean ";
}
public HelloWorld ( String greeting ) { |#2
this.greeting = greeting;
}
public void setGreeting( String greeting ){
this.greeting = greeting;
Notification notification = new Notification(
"jmxbook.ch2.helloWorld.test", this, -1,
System.currentTimeMillis(), greeting ); |#3
sendNotification( notification ); |#4
}
public String getGreeting(){
return greeting;
}
public void printGreeting (){
System.out.println( greeting );
}
private String greeting;
}//class
( 注解 )<#1 繼承 NotificationBroadcasterSupport 類(lèi) >
( 注解 )<#2 定義兩個(gè)構造方法 >
( 注解 )<#3 創(chuàng )建 javax.management.Notification 對象 >
( 注解 )<#4 發(fā)送通知 >
#1 通過(guò)繼承NotificationBroadcasterSupport 類(lèi)改變了HelloWorld 類(lèi)的聲明,NotificationBroadcasterSupport 類(lèi)向它的子類(lèi)MBean提供了注冊監聽(tīng)器和發(fā)送通知的方法,它實(shí)現了javax.management.NotificationBroadcaster 接口。
#2 本例只是發(fā)送一個(gè)簡(jiǎn)單的通知,在這步使用構造方法新建了一個(gè)通知:
public Notification( java.lang.String type, java.lang.Object source,
long sequenceNumber,long timeStamp,
java.lang.String message )
構造方法的參數如表 2.4 所示:
Notification 構造方法參數
Parameter
Description
java.lang.String type
點(diǎn)分格式的 String 來(lái)標識通知,用于通知的簡(jiǎn)要描述
java.lang.Object source
產(chǎn)生通知的 MBean , 可以是 MBean 對象的引用或是它的 ObjectName
long sequenceNumber
在一個(gè)通知序列中表示本通知
long timestamp
創(chuàng )建通知 的時(shí)間戳
java.lang.String message
通知源的消息
#3 通過(guò)繼承NotificationBroadcasterSupport ,你不僅間接實(shí)現了NotificationBroadcaster 接口,還繼承了sendNotification() 方法,當MBean需要發(fā)送通知時(shí)就可以調用這個(gè)方法,父類(lèi)會(huì )將它發(fā)送給所有對應的監聽(tīng)者。對應的監聽(tīng)者是那些已經(jīng)在MBean上注冊并且它的過(guò)濾器接受特定類(lèi)型通知的一些監聽(tīng)器。
接口
前面的例子使用NotificationBroadcasterSupport 類(lèi) ,它提供了NotificationBroadcaster 接口子接口(NotificationEmitter)的一個(gè)實(shí)現 ,接口 NotificationBroadcaster如下:
public interface NotificationBroadcaster {
public void addNotificationListener(
NotificationListener listener,
NotificationFilter filter,
Object handback )
throws IllegalArgumentException;
public MBeanNotificationInfo [] getNotificationInfo() ;
public void removeNotificationListener(
NotificationListener listener )
throws ListenerNotFoundException;
}
實(shí)現這個(gè)接口的MBean通過(guò)addNotificationListener()方法為其他的對象提供了注冊機制,這個(gè)方法接受一個(gè)NotificationListener 對象、一個(gè)NotificationFilter 對象、和一個(gè)handback 對象作為參數。
NotificationListener 參數一個(gè)實(shí)現了NotificationListener 接口的對象,它指定一個(gè)handleNotification()方法。當通知被發(fā)送個(gè)一個(gè)監聽(tīng)器時(shí)這個(gè)方法會(huì )被回調。
NotificationFilter 參數是一個(gè)可選參數,它允許MBean來(lái)過(guò)濾發(fā)送那些消息給監聽(tīng)器。handback 參數在每次通知發(fā)送時(shí)都會(huì )被發(fā)送回客戶(hù)端。
注意通知注冊和傳輸機制和java事件模型的相似性。
2.6.2 修改 HelloAgent
為了發(fā)送通知,還需要一些對象來(lái)接受它們。本例將HelloAgent 作為通知監聽(tīng)器,只需要對代碼最很少改動(dòng)。首先,HelloAgent需要實(shí)現NotificationListener 接口,它還是會(huì )新建和注冊HTML適配器和HelloWorld MBean,創(chuàng )建完MBean后,它就可以為HelloWorld MBean注冊一個(gè)通知監聽(tīng)器來(lái)接受通知。HelloAgent的代碼變更部分在列表2.4中以粗體標注:
Listing 2.4 HelloAgent .java
package jmxbook.ch2;
import javax.management.*;
import com.sun.jdmk.comm.HtmlAdaptorServer;
public class HelloAgent implements NotificationListener { |#1
private MBeanServer mbs = null;
public HelloAgent () {
mbs = MBeanServerFactory .createMBeanServer( "HelloAgent " );
HtmlAdaptorServer adapter = new HtmlAdaptorServer();
HelloWorld hw = new HelloWorld();
ObjectName adapterName = null;
ObjectName helloWorldName = null;
try{
adapterName = new ObjectName (
"HelloAgent :name=htmladapter,port=9092" );
mbs.registerMBean( adapter, adapterName );
adapter.start();
helloWorldName = new ObjectName (
"HelloAgent :name=helloWorld1" );
mbs.registerMBean( hw, helloWorldName );
hw.addNotificationListener( this, null, null ); |#2
}catch( Exception e ) {
e.printStackTrace();
}
}//constructor
public void handleNotification( |#3
Notification notif, Object handback ) |#3
{
System.out.println( "Receiving notification ..." );
System.out.println( notif.getType() );
System.out.println( notif.getMessage() );
}
public static void main( String args[] ) {
HelloAgent agent = new HelloAgent();
}
}//class
( 注解 )<#1 實(shí)現 NotificationListener 接口 >
( 注解 )<#2 注冊接受通知 >
( 注解 )<#3 實(shí)現接口方法
#1 HelloAgent 代理實(shí)現NotificationListener 接口,接口中只有一個(gè)方法handleNotification(),在通知傳地到時(shí)它被調用。
#2 注冊MBean之后,HelloAgent 將自己作為一個(gè)listener 注冊在了HelloWorld MBean上,這是通過(guò)將自己作為NotificationListener 參數傳遞給MBean的addNotificationListener()(繼承自父類(lèi)NotificationBroadcasterSupport )來(lái)完成的
#3 前面提到過(guò),對象要想接收到通知,必須實(shí)現NotificationListener 接口,它只有一個(gè)方法handleNotification( )。本例中只是簡(jiǎn)單的打印了一些信息。
運行結果
要測試本例,首先需要編譯源碼然后執行HelloAgent(參考2.5節)。一旦代理開(kāi)始運行了,就可以打開(kāi)瀏覽器,輸入
http://localhost:9092 ,進(jìn)入Agent頁(yè)面,為測試通知,按照以下步驟進(jìn)行:
1. 遷移至 MBean 視圖,點(diǎn)擊 HelloWorld MBean
2. 當 HelloWorld MBean 的 greeting 改變時(shí)會(huì )發(fā)送一個(gè)通知,因此輸入一個(gè)新值,然后點(diǎn)擊 Apply
3. 查看 HelloAgent 控制臺,你會(huì )看到如下輸出:
Receiving notification ...
jmxbook.ch2.helloWorld.test
I have changed my greeting
輸出包括你的打印信息 ” Receiving notification … ” ,以及通知類(lèi)型和通知包含的消息。
總結
本章讓手把手教你學(xué)習了幾個(gè)JMX的例子。其中你開(kāi)發(fā)了一個(gè)可管理的資源,創(chuàng )建并運行了一個(gè)簡(jiǎn)單的JMX代理,我們討論了怎么注冊MBean,怎樣確保的對象名唯一,怎么創(chuàng )建MBeanServer等。
另外你還是使用了Sun提供JMX RI中的HTML適配器,通過(guò)HelloWorld例子,你應該明白了從JMX的角度來(lái)看MBean的開(kāi)發(fā)是很如此簡(jiǎn)單,MBean只通過(guò)簡(jiǎn)單的幾行代碼來(lái)暴露自己的資源。
最后,為了是JMX的介紹更全面,我們寫(xiě)了一個(gè)JMX通知的例子,在第6章我們會(huì )詳細討論通知,哪兒你會(huì )明白為啥通知是管理系統中的一個(gè)基本部分。
第三章,你將會(huì )開(kāi)發(fā)一個(gè)JMX代理,然后在本書(shū)的其余部分,你會(huì )加強它的功能,它將會(huì )被用于其他的很多章節。