用各種 AP servers 時大家都習慣在 web console 設定各種的 resources
但自己架 RMI server 或 socket server 時通常也希望有一個 console 可以設定管理
在以前要額外寫一個管理介面非常的麻煩不過 JDK 1.5 之後整合了 JMX,可以透過註冊 mbean 到 mbean server,並由 JMX console 管理
這篇文章不講複雜的 JMX 定義,而是以 spring 設定 c3p0 datasource、log4j 和 quartz schedule jobs 的 mbeans
讓大家實際體驗一下 JMX 的好處

1. 設定 log4j mbean
<bean id="log4jMBean"
    class="org.apache.log4j.jmx.HierarchyDynamicMBean" />
非常簡單,應該不用解釋了

2. 設定 c3p0 datasource
先看一下 datasource 在 spring 的設定
<bean id="dataSource"
    class="com.mchange.v2.c3p0.ComboPooledDataSource"
    destroy-method="close">
    <property name="driverClass" value="com.mysql.jdbc.Driver" />
    <property name="jdbcUrl"
        value="jdbc:mysql://localhost:3306/test" />
    <property name="user" value="root" />
    <property name="password" value="fenriswolf" />
    <property name="initialPoolSize" value="1" />
    <property name="minPoolSize" value="1" />
    <property name="maxPoolSize" value="10" />
</bean>

接下來是 mbean 的設定
但有一點要注意,雖然 c3p0 有支援 JMX,不過只支援 local mbean server
意思是如果要註冊自己的 datasource mbean 到 JBoss、Weblogic 等 mbean server 上是不行的
少了整合的彈性
要解決這個問題有兩個步驟:
a. 新增 c3p0.properties 到根目錄,加入
com.mchange.v2.c3p0.management.ManagementCoordinator=com.mchange.v2.c3p0.management.
NullManagementCoordinator
意思是不使用 JMX 設定,因為要由 spring 控管

b. 加入以下 xml 到 spring 設定檔
<bean
    class="com.mchange.v2.c3p0.management.DynamicPooledDataSourceManagerMBean">
    <constructor-arg ref="dataSource" />
    <constructor-arg value="server:name=c3p0" />
    <constructor-arg ref="mbeanServer" />
</bean>
constructor第一個參數是 datasource 的 reference
第二個參數是 mbean object name
第三個參數是 mbean server 的 reference

3. 設定 quartz schedule jobs
jobdetails 跟 triggers 的設定有點多就不列出來了,請看程式下載區的完整範例
quartz 1.6 也支援 JMX,可透過 org.quartz.scheduler.jmx.export=true 來啟動
不過支援並不完整,只能看到 scheduler 的資訊,triggers 和 jobdetails 的資訊看不到
我自定 com.fw.jmx.quartz.SchedulerMBean 來解決這個問題(請下載quartzmbean.jar)
為了系統的穩定性,故除了 scheduler 可以執行 start, standby, shutdown 之外
triggers 和 jobdetails 都是不可更改及執行的,有需要的人可自行更改
下面是 mbean 的設定
<bean id="schedulerMBean" class="com.fw.jmx.quartz.SchedulerMBean">
    <constructor-arg ref="scheduler" />
    <constructor-arg ref="triggers" />
</bean>
constructor第一個參數是 scheduler 的 reference
第二個參數是 trigger list 的 reference

4. export mbeans
<bean id="mbeanServer"
    class="org.springframework.jmx.support.MBeanServerFactoryBean">
    <property name="locateExistingServerIfPossible" value="true" />
</bean>

<bean id="exporter"
    class="org.springframework.jmx.export.MBeanExporter">
    <property name="beans">
        <map>
            <entry key="server:name=log4j" value-ref="log4jMBean" />
            <entry key="server:name=FWScheduler"
                value-ref="schedulerMBean" />
        </map>
    </property>
    <property name="server" ref="mbeanServer" />
</bean>
mbean server 由 java.lang.management.ManagementFactory.getPlatformMBeanServer() 取得
MBeanExporter 則是註冊 mbeans 到 mbean server
entry key 是 mbean object name
value-ref 是上面定義的 mbean bean name
這邊註冊的 mbeans 只有兩個,因為 c3p0 會由 DynamicPooledDataSourceManagerMBean 來註冊

5. 執行程式
為了可以監控 mbeans 要在 java 指令後加上 -Dcom.sun.management.jmxremote 的參數
如 : java -Dcom.sun.management.jmxremote com.fw.jmx.TestMain
注意 : 這個參數是為了在 local 監控才需要,如用 mx4j HttpAdaptor(請看附錄)則不需要

6. 執行 jmx console
在命令列模式執行 jconsole

jconsole 抓到剛執行的程式,直接點選 Connect



選取 MBeans tab 會列出所有已註冊的 mbeans,上方有兩個 tabs,Attributes 和 Operations
以 FWScheduler 為例,可以看執行的狀況及啟動或停止所有的 schedules

附錄1
執行 jconsole 一定要在 local 裝 JDK 並不是那麼方便
想提供網頁供 administrator 設定可用 mx4j HttpAdaptor
方法很簡單
a. 多加兩個 bean 到 spring 設定檔
<bean id="xsltProcessor"
    class="mx4j.tools.adaptor.http.XSLTProcessor" />

<bean id="httpAdaptor"
    class="mx4j.tools.adaptor.http.HttpAdaptor">
    <property name="processor" ref="xsltProcessor" />
    <property name="port" value="9001" />
</bean>

b. 在 MBeanExporter 裡註冊
<entry key="server:name=HttpAdaptor"
    value-ref="httpAdaptor" />
<entry key="server:name=XSLTProcessor"
    value-ref="xsltProcessor" />

c. 更改啟動的 main method 加入
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
        "appContext.xml");
HttpAdaptor httpAdaptor = (HttpAdaptor) context.getBean("httpAdaptor");
httpAdaptor.start();

d. 連到 http://localhost:9001 就可以看到設定頁面啦

有點醜,有需要可自行更改 XSLTProcessor

附錄2
第二個選擇是用 JDMK HtmlAdaptorServer(需下載 jdmkrt.jar)
a. 加入 spring 設定檔
<bean id="htmlAdapter"
    class="com.sun.jdmk.comm.HtmlAdaptorServer">
    <property name="port" value="9001" />
</bean>

b. 在 MBeanExporter 裡註冊
<entry key="server:name=htmlAdapter"
    value-ref="htmlAdapter" />

c. 更改啟動的 main method 加入
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
        "appContext.xml");
HtmlAdaptorServer htmlAdapter = (HtmlAdaptorServer) context
        .getBean("htmlAdapter");
htmlAdapter.start();

d. 連到 http://localhost:9001 就可以看到設定頁面啦

這比 mx4j 好看多了

延伸閱讀
JMX(2) - 自訂 mbeans
JMX(3) - 與 weblogic 9 整合

執行環境
JDK 1.5.0_11(因為 1.6 的 jconsole 太醜了)
spring 2.5
log4j 1.2.14
c3p0 0.9.1.2
quartz 1.6
mx4j 3.0.2

參考資料
JMX Tutorial
Spring JMX
log4j 1.2
c3p0
Quartz JMX Management
MX4J
Java Dynamic Management Kit

程式下載
appContext.xml
c3p0.properties
MyBean.java
TestMain.java
quartzmbean.jar(原始檔在此)
jdmkrt.jar