JBoss JMX實現架構

2.2 JBoss JMX實現架構
2.2.1 JBoss類裝載器架構
JBoss 3.x實現了一種新的類裝載架構,即允許類跨部署單元使用.在JBoss 2.x中,很難實現MBean服務和動態部署的J2EE組件進行交互,並且MBean本身不具有熱部署能力.在JBoss 3.x中,任何東西都是熱部署的,因爲新的部署架構和類裝載架構使得它們成爲可能.本文在深入討論具體的JBoss類裝載模型之前,將給出Java的類型 系統特性及類裝載器介紹.
2.2.2 類裝載和Java中的類型
類裝載是所有服務器架構的基礎組成部分.具體服務及其支持服務的類必須裝 載到服務器框架中.Java的強類型化(typed)特性使得類裝載易於出現問題.大部分開發者知道,Java中類的類型是由類的全限定(fully qualified)名決定的.從Java 1.2開始,用於定義類的java.lang.ClassLoader也能夠決定類的類型.這樣做的理由在於確保從特定位置裝載類的環境是類型安全的 (type-safe).Vijay Saraswat於1997年發表的論文"Java is nottype-safe"證明了Java不是類型安全的.這使得應用能夠使用先前裝載的其他類實現版本而愚弄JVM,從而訪問到Java不應該訪問的類方法和 成員.這種對類型系統的欺騙的根源在於這些應用引入了那些能夠跨越正常委派模型的類裝載器.類裝載器使用委派模型搜索類和資源.在通常情況下,類裝載器實 例都有與之關聯的雙親類裝載器,它可能是在創建類裝載器時顯式設置的;如果沒有給出雙親類裝載器,則由JVM指定.如果需要尋找類或資源,則在類裝載器自 身去尋找它們之前,通常情況下都會將該搜索工作委派給其雙親類裝載器.JVM有根類裝載器,稱爲引導(bootstrap)類裝載器.引導類裝載器沒有雙 親類裝載器,但能夠成爲其他類裝載器實例的雙親.
爲了解決類型安全問題,Java類型系統除了通過類名完整地定義類型外,還引入了用於定義類的類 裝載器.其內容請參考由Sheng Liang和Gilad Bracha完成的論文《Dynamic Class Loading in the Java Virtual Machine》.同時,通過Web地址http://java.sun.com/people/sl/papers/oopsla98.ps.gz,開發者能夠獲得其原文.另外,在動態環境中,比如應用服務器(尤其是支持熱部署的JBoss),動態類裝載 方式有了更深入的發展.其中,ClassCastException,LinkageError及IllegalAccessError更能展示出靜態類 裝載上下文中所看不到的場景.本文接下來仔細研究上述各種異常的具體含義和發生方式.
1.ClassCastException——我不是你的類型
無 論在什麼情況下,只要將實例造型(cast)作爲其不兼容的類型,系統就會拋出java.lang.ClassCastException異常.比如,某 簡單實例如下:將java.net.URL放入java.util.ArrayList後,試圖獲得java.lang.String的代碼和異常信息如 下:
ArrayList array = new ArrayList();
array.add(new URL("file:/tmp"));
String url = (String) array.get(0);
java.lang.ClassCastException: java.net.URL
at org.jboss.chap2.ex0.ExCCEa.main(Ex1CCE.java:16)
開 發者可以從ClassCastException看出,將array元素造型作爲String類型失敗,因爲該元素的實際類型爲 java.net.URL.當然,開發者對這種試驗性的場景不會感興趣.讓我們考慮另一種場景:不同的URLClassLoader裝載了相同的jar文 件.從字節碼角度考慮,儘管通過不同URLClassLoader裝載的類是完全相同的,但它們被Java類型系統看做完全不同的類型.列表2-1,列表 2-2和列表2-3給出了相應的代碼實例.
列表2-1 用於證明由於不同類裝載器而觸發ClassCastException的ExCCEc類
1 package org.jboss.chap2.ex0;
2
3 import java.io.File;
4 import java.net.URL;
5 import java.net.URLClassLoader;
6 import java.lang.reflect.Method;
7
8 import org.apache.log4j.Logger;
9
10 import org.jboss.util.ChapterExRepository;
11 import org.jboss.util.Debug;
12
13 /** An example of a ClassCastException that results from classes loadedthrough
14 * different class loaders.
15 * @author [email protected]
16 * @version $Revision:$
17 */
18 public class ExCCEc
19 {
20 public static void main(String[] args) throws Exception
21 {
22 ChapterExRepository.init(ExCCEc.class);
23
24 String chapDir = System.getProperty("chapter.dir");
25 Logger ucl0Log = Logger.getLogger("UCL0");
26 File jar0 = new File(chapDir+"/j0.jar");
27 ucl0Log.info("jar0 path: "+jar0.toString());
28 URL[] cp0 = {jar0.toURL()};
29 URLClassLoader ucl0 = new URLClassLoader(cp0);
30 Thread.currentThread().setContextClassLoader(ucl0);
31 Class objClass = ucl0.loadClass("org.jboss.chap2.ex0.ExObj");
32 StringBuffer buffer = new StringBuffer("ExObj Info");
33 Debug.displayClassInfo(objClass, buffer, false);
34 ucl0Log.info(buffer.toString());
35 Object value = objClass.newInstance();
36
37 File jar1 = new File(chapDir+"/j0.jar");
38 Logger ucl1Log = Logger.getLogger("UCL1");
39 ucl1Log.info("jar1 path: "+jar1.toString());
40 URL[] cp1 = {jar1.toURL()};
41 URLClassLoader ucl1 = new URLClassLoader(cp1);
42 Thread.currentThread().setContextClassLoader(ucl1);
43 Class ctxClass2 = ucl1.loadClass("org.jboss.chap2.ex0.ExCtx");
44 buffer.setLength(0);
45 buffer.append("ExCtx Info");
46 Debug.displayClassInfo(ctxClass2, buffer, false);
47 ucl1Log.info(buffer.toString());
48 Object ctx2 = ctxClass2.newInstance();
49
50 try
51 {
52 Class[] types = {Object.class};
53 Method useValue = ctxClass2.getMethod("useValue", types);
54 Object[] margs = {value};
55 useValue.invoke(ctx2, margs);
56 }
57 catch(Exception e)
58 {
59 ucl1Log.error("Failed to invoke ExCtx.useValue", e);
60 throw e;
61 }
62 }
63 }
列表2-2 實例使用到的ExCtx類
64 package org.jboss.chap2.ex0;
65
66 import java.io.IOException;
67
68 import org.apache.log4j.Logger;
69
70 import org.jboss.util.Debug;
71
72 /** A classes used to demonstrate various class loading issues
73 * @author [email protected]
74 * @version $Revision: 1.2 $
75 */
76 public class ExCtx
77 {
78 ExObj value;
79
80 public ExCtx() throws IOException
81 {
82 value = new ExObj();
83 Logger log = Logger.getLogger(ExCtx.class);
84 StringBuffer buffer = new StringBuffer("ctor.ExObj");
85 Debug.displayClassInfo(value.getClass(), buffer, false);
86 log.info(buffer.toString());
87 ExObj2 obj2 = value.ivar;
88 buffer.setLength(0);
89 buffer = new StringBuffer("ctor.ExObj.ivar");
90 Debug.displayClassInfo(obj2.getClass(), buffer, false);
91 log.info(buffer.toString());
92 }
93 public Object getValue()
94 {
95 return value;
96 }
97 public void useValue(Object obj) throws Exception
98 {
99 Logger log = Logger.getLogger(ExCtx.class);
100 StringBuffer buffer = new StringBuffer("useValue2.arg class");
101 Debug.displayClassInfo(obj.getClass(), buffer, false);
102 log.info(buffer.toString());
103 buffer.setLength(0);
104 buffer.append("useValue2.ExObj class");
105 Debug.displayClassInfo(ExObj.class, buffer, false);
106 log.info(buffer.toString());
107 ExObj ex = (ExObj) obj;
108 }
109 void pkgUseValue(Object obj) throws Exception
110 {
111 Logger log = Logger.getLogger(ExCtx.class);
112 log.info("In pkgUseValue");
113 }
114 }
115
列表2-3 實例使用到的ExObj和ExObj2類
package org.jboss.chap2.ex0;
import java.io.Serializable;
/**
* @author [email protected]
* @version $Revision:$
*/
public class ExObj implements Serializable
{
public ExObj2 ivar = new ExObj2();
}
----------------------------------------------------------------------------
package org.jboss.chap2.ex0;
import java.io.Serializable;
/**
* @author [email protected]
* @version $Revision: 1.1$
*/
public class ExObj2 implements Serializable
{
}
ExCCEc.main方法使用反射將應用程序的類裝載器與URLClassLoader ucl0和ucl1各自裝載的類隔離開,而這些類都是從output/chap2/j0.jar文件中裝載的.j0.jar的具體內容如下:
[nr@toki examples]$ jar -tf output/chap2/j0.jar
...
org/jboss/chap2/ex0/ExCtx.class
org/jboss/chap2/ex0/ExObj.class
org/jboss/chap2/ex0/ExObj2.class
本文將通過運行實例來證明ClassCastException是如何出現的,然後再來研究問題所在.請參考附錄C提供的本書附帶實例的安裝指南.從光盤目錄使用如下命令運行該實例:
[nr@toki examples]$ ant -Dchap=chap2 -Dex=0c run-example
Buildfile: build.xml
...
[java] [ERROR,UCL1] Failed to invoke ExCtx.useValue
[java] java.lang.reflect.InvocationTargetException
[java] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
[java] atsun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
[java] atsun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.
java:25)
[java] at java.lang.reflect.Method.invoke(Method.java:324)
[java] at org.jboss.chap2.ex0.ExCCEc.main(ExCCEc.java:58)
[java] Caused by: java.lang.ClassCastException
[java] at org.jboss.chap2.ex0.ExCtx.useValue(ExCtx.java:44)
[java] ... 5 more
[java] Java Result: 1
上 述給出的只是異常信息,通過logs/chap2-ex0c.log文件能夠找到完整的輸出信息.其中,ExCCEc.java代碼使用 URLClassLoader ucl1裝載了ExCtx類,然後實例化該類(第37~48行).最後,調用了ExCtx.useValue(Object) 方法(第55行).另外,傳進來的ExObj實例(譯者注:value)是藉助於URLClassLoader ucl0(第25~35行)裝載的.當ExCtx.useValue代碼試圖將傳入參數造型作爲ExObj對象時,應用拋出了異常.爲更好地理解失敗的原 因,列表2-4給出了摘自chap2-ex0c.log文件的調試輸出信息.
列表2-4 用於ExObj類的chap2-ex0c.log調試輸出
[INFO,UCL0] ExObj Info
org.jboss.chap2.ex0.ExObj(113fe2).ClassLoader=java.net.URLClassLoader@6e3914
..java.net.URLClassLoader@6e3914
....file:/C:/Scott/JBoss/Books/AdminDevel/education/books/admin-devel/examples/output/
chap2/j0.jar
++++CodeSource:(file:/C:/Scott/JBoss/Books/AdminDevel/education/books/admin-devel/
examples/output/chap2/j0.jar )
Implemented Interfaces:
++interface java.io.Serializable(7934ad)
++++ClassLoader: null
++++Null CodeSource
[INFO,ExCtx] useValue2.ExObj class
org.jboss.chap2.ex0.ExObj(415de6).ClassLoader=java.net.URLClassLoader@30e280
..java.net.URLClassLoader@30e280
....file:/C:/Scott/JBoss/Books/AdminDevel/education/books/admin-devel/examples/output/
chap2/j0.jar
++++CodeSource:(file:/C:/Scott/JBoss/Books/AdminDevel/education/books/admin-devel/
examples/output/chap2/j0.jar )
Implemented Interfaces:
++interface java.io.Serializable(7934ad)
++++ClassLoader: null
++++Null CodeSource
帶 有前綴[INFO,UCL0]的第一條輸出表明,ExCCEc.java中第31行裝載的ExObj類的Hash碼爲113fe2,與之關聯的 URLClassLoader實例(ucl0)的Hash碼爲6e3941.傳遞給ExCtx.useValue方法的實例就是基於該ExOjb類創建 的.帶有前綴[INFO,ExCtx]的第二條輸出表明,ExCtx.userValue方法上下文中的ExObj實例的Hash碼爲415de6,與之 關聯的URLClassLoader實例(ucl1)的Hash碼爲30e280.儘管這兩個ExObj類是來自相同j0.jar中的同一字節碼文件,但 結果是兩者的散列碼和URLClassLoader都不同.因此,試圖將不同範圍的ExObj實例進行造型轉換,將會拋出 ClassCastException異常.
若開發者重新部署某應用,與此同時有其他的應用引用了該應用的類,則此時 ClassCastException異常經常出現,如單獨的Web應用(.war)訪問EJB.如果重新部署了某應用,所有依賴於它的應用必須刷新 (flush)它們的類引用.一般情況下,這要求依賴的應用程序能夠自動重新部署.
當出現了重新部署的情況時,爲實現獨立部署應用之間的交互,一種替代方式是通過配置EJB層以隔離不同的部署應用.其具體辦法是,將EJB層默認的引用訪問語義修改爲傳值訪問語義.因爲基於引用訪問語義時,JBoss設定部署在其上的組件默認運行在同一JVM中.
2.IllegalAccessException——做不應該做的
當 試圖訪問可見性限定符(visibility qualifier)不允許訪問的方法或成員時,應用會拋出java.lang.IllegalAccessException異常.常見的例子有:試圖 訪問私有或受保護的方法和實例變量.再比如,存在某例子,從表面上看是從正確的包訪問私有受保護的方法或成員,但實際上訪問者和被訪問者是由不同類裝載器 裝載的.列表2-5給出了代碼實例.
列表2-5 由於不同類裝載器引起IllegalAccessException的ExIAEd類
116 package org.jboss.chap2.ex0;
117
118 import java.io.File;
119 import java.net.URL;
120 import java.net.URLClassLoader;
121 import java.lang.reflect.Method;
122
123 import org.apache.log4j.Logger;
124
125 import org.jboss.util.ChapterExRepository;
126 import org.jboss.util.Debug;
127
128 /** An example of IllegalAccessExceptions due to classes loaded by twoclass
129 * loaders.
130 * @author [email protected]
131 * @version $Revision: 1.2$
132 */
133 public class ExIAEd
134 {
135 public static void main(String[] args) throws Exception
136 {
137 ChapterExRepository.init(ExIAEd.class);
138
139 String chapDir = System.getProperty("chapter.dir");
140 Logger ucl0Log = Logger.getLogger("UCL0");
141 File jar0 = new File(chapDir+"/j0.jar");
142 ucl0Log.info("jar0 path: "+jar0.toString());
143 URL[] cp0 = {jar0.toURL()};
144 URLClassLoader ucl0 = new URLClassLoader(cp0);
145 Thread.currentThread().setContextClassLoader(ucl0);
146
147 StringBuffer buffer = new StringBuffer("ExIAEd Info");
148 Debug.displayClassInfo(ExIAEd.class, buffer, false);
149 ucl0Log.info(buffer.toString());
150
151 Class ctxClass1 = ucl0.loadClass("org.jboss.chap2.ex0.ExCtx");
152 buffer.setLength(0);
153 buffer.append("ExCtx Info");
154 Debug.displayClassInfo(ctxClass1, buffer, false);
155 ucl0Log.info(buffer.toString());
156 Object ctx0 = ctxClass1.newInstance();
157
158 try
159 {
160 Class[] types = {Object.class};
161 Method useValue = ctxClass1.getDeclaredMethod("pkgUseValue",types);
162 Object[] margs = {null};
163 useValue.invoke(ctx0, margs);
164 }
165 catch(Exception e)
166 {
167 ucl0Log.error("Failed to invoke ExCtx.pkgUseValue", e);
168 }
169 }
170 }
應 用類裝載器將ExIAEd類裝載到應用中,但ExCtx類是ExIAEd.main方法通過反射並藉助於URLClassLoader ucl0裝載的.這裏將運行上述實例來證明如何拋出Illegal AccessException異常,並找到問題的原因.通過如下命令運行實例:
[orb@toki examples]$ ant -Dchap=chap2 -Dex=0d run-example
Buildfile: build.xml
...
[java] [ERROR,UCL0] Failed to invoke ExCtx.pkgUseValue
[java] java.lang.IllegalAccessException: Class org.jboss.chap2.ex0.ExIAEd cannot access a
member of class org.jboss.chap2.ex0.ExCtx with modifiers ""
[java] at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:57)
[java] at java.lang.reflect.Method.invoke(Method.java:317)
[java] at org.jboss.chap2.ex0.ExIAEd.main(ExIAEd.java:48)
上 述那條大塊輸出信息展示了IllegalAccessException異常的拋出.完整的輸出信息能在logs/chap2-ex0d.log文件中找 到.ExIAEd.java中的第48行,程序藉助於反射調用了ExCtx.pkgUseValue(Object)方法.其中,pkgUseValue 方法具有包受保護訪問限定,儘管調用類ExIAEd和方法被調用的ExCtx類都位於包org.jboss.chap2.ex0中,但由於這兩個類被不同 類裝載器裝載,因此調用無效.通過查看chap2-ex0d.log文件,能夠找到重要的幾行輸出信息:
[INFO,UCL0] ExIAEd Info
org.jboss.chap2.ex0.ExIAEd(65855a).ClassLoader=sun.misc.Launcher$AppClassLoader@3f52a5
..sun.misc.Launcher$AppClassLoader@3f52a5
...
[INFO,UCL0] ExCtx Info
org.jboss.chap2.ex0.ExCtx(70eed6).ClassLoader=java.net.URLClassLoader@113fe2
..java.net.URLClassLoader@113fe2
...
通 過上述內容可以看出,ExIAEd類是由默認的應用類裝載器實例,即sun.misc. Launcher$AppClassLoader@3f52a2裝載的,而ExCtx類是通過類裝載器實例java.net.URLClassLoader@113FE2裝載的.由於不同的類裝載器裝載了這些類,訪問包受保護方法便成了安全侵害.因此,類的全 限定名和類裝載器不僅能夠決定類的類型,包範圍(package scope)也是如此.
比如,在實際工作中,如果將相同的類放置在兩個不同的 SAR(譯者注:JBoss Service Archive,JBoss服務存檔)部署文件中就會出現這種異常.如果SAR部署文件中的類有包受保護關係,則使用SAR服務的用戶可能從某個SAR裝 載某類,而後又去另一個SAR文件中裝載另一個類.如果這兩個類有受保護關係,則會拋出IllegalAccessError異常.解決的辦法可以是:開 發者需要將SAR引用的類單獨放在某個jar中,或者將這些SAR合併成單個部署文件.因此,有可能是單個的SAR,或者將這兩個SAR包含在EAR(譯 者注:Enterprise ApplicationArchive,企業應用存檔)中.
3.LinkageError——確保你就是聲稱的你
爲 解決早期Java虛擬機中的類型安全問題,Java語言規範第1.2版引入了裝載約束.當某X類涉及到多個類裝載器時,爲保證它的一致性,通過裝載約束能 夠在類裝載器範圍的上下文內驗證這種類型是否是預期的.由於Java允許用戶自定義類裝載器,同時LinkageError還擴展了 ClassCastException類,而且裝載和使用類的過程中都需要完成裝載約束,因此裝載約束是很重要的.
爲了能夠理解裝載約束的內容和 它是如何保證類型安全的,下面首先介紹Liang和Bracha論文中的術語和例子.當前存在兩種類裝載器,即初始和定義類裝載器.其中,初始類裝載器是 通過調用ClassLoader.loadClass方法以初始裝載命名類的類裝載器.而定義類裝載器是通過調用 ClassLoader.defineClass方法將類字節碼轉換成類實例的類裝載器.類的完整表達式爲:
其中,爲全限定類名,Ld爲定義類裝 載器,Li爲初始類裝載器.當初始類裝載器不重要時,類型可以表示爲;當定義類裝載器不重要時,類型可以表示爲.對於第二種情形,並不是不存在定義類裝載 器,而只是標識它並不重要而已.同時,完全定義了類型.僅僅在驗證裝載約束時,初始裝載器纔會對產生關聯.接下來,請開發者瀏覽列表2-6.
列表2-6 用於證明裝載約束的類
class
{
void f()
{
x = g();
x.secret_value = 1 ; // Should not be allowed
}
}
------------------------------------------------------------------------------------------------
class
{
static g() {}
}
------------------------------------------------------------------------------------------------
class
{
public int secret_value;
}
------------------------------------------------------------------------------------------------
class
{
private int secret_value;
}
其 中:類由定義,因此用於初始裝載類及引用的類.由定義,由定義(由委派給).既然由定義,則將負責方法上下文中類的初始裝入.本實例中的和定義了不同版本 的(列表2-6後續內容指出了).另外,由於1.1及先前版本的JVM並沒有將類的全限定名和定義類裝載器,這兩者一同決定類的類型,因此既然認定x是實 例,所以x能夠訪問返回中的私有成員secret_value.
從Java 1.2以後,對於從不同定義類裝載器中使用類型的情形,通過生成裝載約束以驗證類型一致性來解決其存在的上述問題.對於列表2-6所示的實例,當執行到第 一行時,JVM生成 = 約束,從而確保和初始裝載的類型都是相同的.問題的根本不在於或,或其他類裝載器定義,而是無論使用任何類裝載器完成初始裝載,僅僅能夠存在單個類定義. 如果或已經定義了不同版本的,只要觸發該裝載約束檢查,JVM會立即拋出.否則,該記錄將約束,在執行時試圖裝入重複版本的也將拋出.
接下來,本文結合具體實例來體會是如何出現的.列表2-7給出了實例主類和使用的自定義類裝載器.
列表2-7 LinkageError具體實例
171 package org.jboss.chap2.ex0;
172
173 import java.io.File;
174 import java.net.URL;
175
176 import org.apache.log4j.Logger;
177 import org.jboss.util.ChapterExRepository;
178 import org.jboss.util.Debug;
179
180 /** An example of a LinkageError due to classes being defined by more than
181 * one class loader in a non-standard class loading environment.
182 * @author [email protected]
183 * @version $Revision: 1.1$
184 */
185 public class ExLE
186 {
187 public static void main(String[] args) throws Exception
188 {
189 ChapterExRepository.init(ExLE.class);
190
191 String chapDir = System.getProperty("chapter.dir");
192 Logger ucl0Log = Logger.getLogger("UCL0");
193 File jar0 = new File(chapDir+"/j0.jar");
194 ucl0Log.info("jar0 path: "+jar0.toString());
195 URL[] cp0 = {jar0.toURL()};
196 Ex0URLClassLoader ucl0 = new Ex0URLClassLoader(cp0);
197 Thread.currentThread().setContextClassLoader(ucl0);
198 Class ctxClass1 = ucl0.loadClass("org.jboss.chap2.ex0.ExCtx");
199 Class obj2Class1 = ucl0.loadClass("org.jboss.chap2.ex0.ExObj2");
200 StringBuffer buffer = new StringBuffer("ExCtx Info");
201 Debug.displayClassInfo(ctxClass1, buffer, false);
202 ucl0Log.info(buffer.toString());
203 buffer.setLength(0);
204 buffer.append("ExObj2 Info, UCL0");
205 Debug.displayClassInfo(obj2Class1, buffer, false);
206 ucl0Log.info(buffer.toString());
207
208 File jar1 = new File(chapDir+"/j1.jar");
209 Logger ucl1Log = Logger.getLogger("UCL1");
210 ucl1Log.info("jar1 path: "+jar1.toString());
211 URL[] cp1 = {jar1.toURL()};
212 Ex0URLClassLoader ucl1 = new Ex0URLClassLoader(cp1);
213 Class obj2Class2 = ucl1.loadClass("org.jboss.chap2.ex0.ExObj2");
214 buffer.setLength(0);
215 buffer.append("ExObj2 Info, UCL1");
216 Debug.displayClassInfo(obj2Class2, buffer, false);
217 ucl1Log.info(buffer.toString());
218
219 ucl0.setDelegate(ucl1);
220 try
221 {
222 ucl0Log.info("Try ExCtx.newInstance()");
223 Object ctx0 = ctxClass1.newInstance();
224 ucl0Log.info("ExCtx.ctor succeeded, ctx0: "+ctx0);
225 }
226 catch(Throwable e)
227 {
228 ucl0Log.error("ExCtx.ctor failed", e);
229 }
230 }
231 }
----------------------------------------------------------------------------
232 package org.jboss.chap2.ex0;
233
234 import java.net.URLClassLoader;
235 import java.net.URL;
236
237 import org.apache.log4j.Logger;
238
239 /** A custom class loader that overrides the standard parent delegationmodel
240 * @author [email protected]
241 * @version $Revision:$
242 */
243 public class Ex0URLClassLoader extends URLClassLoader
244 {
245
246 private static Logger log = Logger.getLogger(Ex0URLClassLoader.class);
247 private Ex0URLClassLoader delegate;
248
249 public Ex0URLClassLoader(URL[] urls)
250 {
251 super(urls);
252 }
253
254 void setDelegate(Ex0URLClassLoader delegate)
255 {
256 this.delegate = delegate;
257 }
258
259 protected synchronized Class loadClass(String name, boolean resolve)
260 throws ClassNotFoundException
261 {
262 Class clazz = null;
263 if( delegate != null )
264 {
265 log.debug(Integer.toHexString(hashCode())+"; Asking delegate toloadClass:
"+name);
266 clazz = delegate.loadClass(name, resolve);
267 log.debug(Integer.toHexString(hashCode())+"; Delegate returned:"+clazz);
268 }
269 else
270 {
271 log.debug(Integer.toHexString(hashCode())+"; Asking super toloadClass:
"+name);
272 clazz = super.loadClass(name, resolve);
273 log.debug(Integer.toHexString(hashCode())+"; Super returned:"+clazz);
274 }
275 return clazz;
276 }
277
278 protected Class findClass(String name)
279 throws ClassNotFoundException
280 {
281 Class clazz = null;
282 log.debug(Integer.toHexString(hashCode())+"; Asking super tofindClass:
"+name);
283 clazz = super.findClass(name);
284 log.debug(Integer.toHexString(hashCode())+"; Super returned:"+clazz);
285 return clazz;
286 }
287 }
本 實例中最重要的組件是URLClassLoader的子類Ex0URLClassLoader.該類裝載器實現重載了默認的雙親委派模型,使得ucl0和 ucl1實例都能裝載ExObj2類,並從ucl0到ucl1設置了委派關係.在ExLE.main方法中,第28~29 行,Ex0URLClassLoader類型的ucl0裝載了ExCtx和ExObj2類.第43行,Ex0URLClassLoader類型的ucl1 再次裝載了ExObj2類.這時,兩個類裝載器都定義了ExObj2類.第49行,藉助於ucl0.setDelegate(ucl1)方法訪問使得從 ucl0到ucl1設置了委派關係.最後,第53行,藉助於ucl0裝載的類(譯者注:ctxClass1)創建了ExCtx實例.其中,這裏的 ExCtx類和列表2-2中的一樣,其構建器如下:
288 public ExCtx() throws IOException
289 {
290 value = new ExObj();
291 Logger log = Logger.getLogger(ExCtx.class);
292 StringBuffer buffer = new StringBuffer("ctor.ExObj");
293 Debug.displayClassInfo(value.getClass(), buffer, false);
294 log.info(buffer.toString());
295 ExObj2 obj2 = value.ivar;
296 buffer.setLength(0);
297 buffer = new StringBuffer("ctor.ExObj.ivar");
298 Debug.displayClassInfo(obj2.getClass(), buffer, false);
299 log.info(buffer.toString());
300 }
現在,既然ucl0類裝載器定義了ExCtx類,同時執行了ExCtx構建器,並將ucl0委派給了ucl1.其中,在ExCtx.java文件中,第24行,基於裝載約束規範,給出了完整的類型表達式如下:
=
既 然必須保證ucl0和ucl1類裝載器實例裝載ExObj2類型的一致性,因此生成了裝載約束Exobj2ucl0=Exobj2uc/1.另外,由於在 設置委派關係前,使用ucl0和ucl1裝載了ExObj2 ,從而破壞了約束,進而在運行過程中拋出了LinkageError異常.使用如下命令行運行實例:
[nr@toki examples]$ ant -Dchap=chap2 -Dex=0e run-example
Buildfile: build.xml
...
[java] java.lang.LinkageError: loader constraints violated when linkingorg/jboss/chap2/ex0/
ExObj2 class [java] at org.jboss.chap2.ex0.ExCtx.(ExCtx.java:24)
[java] at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
[java] atsun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructor
AccessorImpl.java:39) [java] atsun.reflect.DelegatingConstructorAccessorImpl.newInstance
(DelegatingConstructorAccessorImpl.java:27) [java] atjava.lang.reflect.Constructor.
newInstance(Constructor.java:274)
[java] at java.lang.Class.newInstance0(Class.java:308)
[java] at java.lang.Class.newInstance(Class.java:261)
[java] at org.jboss.chap2.ex0.ExLE.main(ExLE.java:53)
正如預期的一樣,在ExCtx構建器中,第24行的代碼破壞了裝載約束,從而拋出了LinkageError異常.
4.調試類裝載問題
爲獲得類裝載的位置信息,本文引入調試類裝載問題.本書附帶的例子:org.jboss.util.Debug類(見列表2-8),就是這樣一個非常實用的工具.
列表2-8 獲得類的調試信息
Class clazz = ...;
StringBuffer results = new StringBuffer();
ClassLoader cl = clazz.getClassLoader();
results.append("\n"+clazz.getName()+"("+Integer.toHexString(clazz.hashCode())+").Class
Loader="+cl);
ClassLoader parent = cl;
while( parent != null )
{
results.append("\n.."+parent);
URL[] urls = getClassLoaderURLs(parent);
int length = urls != null urls.length : 0;
for(int u = 0; u < length; u ++)
{
results.append("\n...."+urls[u]);
}
if( showParentClassLoaders == false )
break;
if( parent != null )
parent = parent.getParent();
}
CodeSource clazzCS = clazz.getProtectionDomain().getCodeSource();
if( clazzCS != null )
results.append("\n++++CodeSource: "+clazzCS);
else
results.append("\n++++Null CodeSource");
其 中,粗體的內容項很重要.首先,藉助於getClassLoader方法能夠獲得各個Class對象的定義類裝載器.這實際上定義了Class類型的範 圍,正如前面有關ClassCastException,IllegalAccessException及LinkageError的內容介紹的.通過類 裝載器,能夠瀏覽到組成雙親委派鏈的ClassLoader的層次圖.如果某ClassLoader爲URLClassLoader,則開發者還能看到用 於類和資源裝載的URL.
ClassLoader並不能通過Class的定義獲得被裝載Class的存放位置,然而通過java.security.ProtectionDomain和java.security.CodeSource卻可以.其中,CodeSource 能夠給出類的原始URL位置.但並不是每個Class都有CodeSource,比如如果類是通過引導類裝載器裝載的,則CodeSource爲空.總而 言之,JVM實現中的java.*和javax.*包都屬於這種情形.
進一步而言,查看裝載到JBoss服務器中的類細節可能更爲有用.開發者能夠通過使用Log4j配置文件(見列表2-9給出的配置片段)配置JBoss類裝載層的日誌輸出詳細程度.
列表2-9 爲實現詳細的類裝載日誌服務的log4j.xml配置實例
其 中,該配置將org.jboss.mx.loading包的類輸出信息存儲到服務器配置的log目錄的ucl.log文件中.雖然查看類裝載代碼並沒有實 際意義,但是它對於提交bug報告或有關的類裝載問題起到很重要的作用.如果開發者遇到可能是bug的類裝載問題,請提交給JBoss項目(位於 SourceForge),並附上該.log文件.如果.log文件太大,請壓縮後發到[email protected]信箱.
5.深入JBoss類裝載架構
到目前爲止,本書已經闡述了Java定義的類型系統中類裝載器的作用,接下來探討JBoss 3.x的類裝載架構.圖2-3描述了類裝載架構核心中的基本組件.
圖2-3 JBoss 3.x核心類裝載組件
中 心組件是org.jboss.mx.loading.UnifiedClassLoader3(UCL)類裝載器.它擴展了標準的 java.net.URLClassLoader,其覆蓋了標準的雙親委派模型以使用類和資源的共享庫.其中,共享庫爲org.jboss.mx.loading.UnifiedLoaderRepository3.另外,各個UCL只和單個 UnifiedLoaderRepository3關聯,但是通常情況下UnifiedLoaderRepository3具有多個UCL.從JBoss 3.0.5RC1開始,UCL可能和多個URL關聯以實現類和資源的裝載.在這之前,UCL總是和單個的URL關聯,因此由於存在包問題而易於出現 IllegalAccessException和LinkageError異常.正如上節討論的情形,即當存在多個類裝載器時,這些錯誤是如何觸發的一 樣.從JBoss 3.0.5RC1開始,部署器使用頂層部署UCL作爲共享的類裝載器,並將所有的部署存檔都提交給該類裝載器."2.4.2 JBoss MBean服務"有這方面的進一步闡述.
當觸發UCL裝載類時,它首先去庫緩存區(repository cache)尋找是否已經裝載了它.僅當在該庫中找不到該類時,UCL纔會將它裝載到庫中.在默認情況下,僅存在單個的 UnifiedLoaderRepository3,它供所有的UCL實例共享使用,其意味着所有的UCL形成了單平面型(flat)的類裝載器命名空 間.當調用方法UnifiedClassLoader3.loadClass(String,boolean)時,完整的執行步驟如下:
(1)檢查與UnifiedClassLoader3關聯的UnifiedLoaderRepository3類緩存區,尋找是否存在目標類緩存.如果找到,則將該緩存返回.
(2) 否則,要求UnifiedClassLoader3去裝載該類(如果它能夠裝載).其中,必然會調用其雙親URLClassLoader.loadClass(String,boolean)方法,從而能夠判斷該類是否處於該類裝載器關聯的URL中,或對其雙 親類裝載器可見.一旦尋找到,該類將被放置到類緩存區並返回給調用者.
(3)如果還是無法尋找到,則查詢庫以獲得所有滿足條件的UCL,即那些能 夠基於庫包名到UCL映射方式提供類的UCL.當往庫中添加UCL時,將創建與該UCL關聯的URL中的包名之間的關聯,從包名到UCL的映射也將再次更 新.這使得能夠快速定位哪個UCL能夠裝載該類.從而根據UCL添加到庫的順序,查找各個UCL.一旦發現某個UCL能夠裝載該類,則將結果返回,否則將 拋出java.lang.ClassNotFoundException異常.
(1)瀏覽裝載庫中的類
另外,能提供類信息的實用方式是 UnifiedLoaderRepository3本身.它是MBean服務,包含了展示類和包信息的操作.通過標準的JMX 名"JMImplementation:name=Default,service=LoaderRepository",能夠定位到默認庫.同時,藉助於JMX控制檯能夠訪問到它,即通 過"http://localhost:8080/jmx-console/HtmlAdaptoraction=inspectMBean&name=JMImplementation%3Aservice%3DLoaderRepository%2Cname%3DDefault"能夠瀏覽到JMX控制檯視圖 (見圖2-4).下面的內容,即"2.3 連接到JMX服務器"一節將討論JMX控制檯.
圖2-4 JMX控制檯中默認類LoaderRepository MBean視圖
圖 2-4提供了兩個實用的操作:getPackageClassLoaders(String)和displayClassInfo (String).其中,getPackageClassLoaders操作返回類裝載器集合,這些裝載器建立了到指定包名中包含類或資源的映射.包名最 後必須包含一個".".如果敲入包名"org.jboss.ejb.",則結果如下:
[org.jboss.mx.loading.UnifiedClassLoader3@166a22b{url=file:/C:/tmp/JBoss/jboss-3.0.5RC2/server
/default/tmp/deploy/server/default/conf/jboss-service.xml/1.jbossservice.xml,addedOrder=2}]
這 是返回集合的字符串表示.它表明存在UnifiedClassLoader3實例,其主要URL指向"default/conf/jboss- service.xml"描述符."addedOrder=2"表明它是第二個添加到庫的類裝載器.它持有服務器配置lib目錄(如 server/default/lib)下的所有jar文件.如果敲入包名,則結果如下:
[org.jboss.mx.loading.UnifiedClassLoader3@47393f{url=file:/C:/tmp/JBoss/jboss-3.0.5RC2/server/
default/tmp/deploy/server/default/conf/jboss-service.xml/1.jbossservice.xml,addedOrder=2},org.
jobss.mx.loading.UnifiedClassLoader3@156e5ed{url=file:/C:/tmp/JBoss/jboss-3.0.5RC2/server/
default/deploy/jmx-rmi-adaptor.sar/,addedOrder=6}]
這次存在兩個UnifiedClassLoader3實例,一個爲default/conf/jboss-service.xml,另一個爲default/deploy/jmx-rmi-adaptor.sar.
通過傳遞類的全限定名給displayClassInfo的操作,開發者能夠瀏覽特定類信息.比如,如果敲入上述實例包中的"org.jboss.jmx.adaptor.rmi.RMIAdaptorImpl",將顯示出如下結果:
org.jboss.jmx.adaptor.rmi.RMIAdaptorImpl Information
Repository cache version:
org.jboss.jmx.adaptor.rmi.RMIAdaptorImpl(11bd9c9).ClassLoader=org.jboss.mx.loading.Unified
ClassLoader3@166a22b{url=file:/C:/tmp/JBoss/jboss-3.0.5RC2/server/default/tmp/deploy/server/
default/conf/jboss-service.xml/1.jboss-service.xml ,addedOrder=2}
..org.jboss.mx.loading.UnifiedClassLoader3@166a22b{ url=file:/C:/tmp/JBoss/jboss-3.0.5RC2/
server/default/tmp/deploy/server/default/conf/jboss-service.xml/1.jbossservice.xml,addedOrder=2}
..org.jboss.system.server.NoAnnotationURLClassLoader@1bc4459
..sun.misc.Launcher$AppClassLoader@12f6684
....file:/C:/tmp/JBoss/jboss-3.0.5RC2/bin/
....file:/C:/usr/local/Java/j2sdk1.4.1_01/lib/tools.jar
....file:/C:/tmp/JBoss/jboss-3.0.5RC2/bin/run.jar
..sun.misc.Launcher$ExtClassLoader@f38798
....file:/C:/usr/local/Java/j2sdk1.4.1_01/jre/lib/ext/dnsns.jar
....file:/C:/usr/local/Java/j2sdk1.4.1_01/jre/lib/ext/ldapsec.jar
....file:/C:/usr/local/Java/j2sdk1.4.1_01/jre/lib/ext/localedata.jar
....file:/C:/usr/local/Java/j2sdk1.4.1_01/jre/lib/ext/sunjce_provider.jar
++++CodeSource: (file:/C:/tmp/JBoss/jboss-3.0.5RC2/server/default/lib/jboss.jar)
Implemented Interfaces:
++interface org.jboss.jmx.adaptor.rmi.RMIAdaptor(98f192)
++++ClassLoader: org.jboss.mx.loading.UnifiedClassLoader3@e31e33{url=file:/C:/tmp/
JBoss/jboss-3.0.5RC2/server/default/deploy/jmx-rmi-adaptor.sar/ ,addedOrder=6}
++++CodeSource: (file:/C:/tmp/JBoss/jboss-3.0.5RC2/server/default/deploy/jmx-rmiadaptor.
sar/ )
### Instance0 found in UCL: org.jboss.mx.loading.UnifiedClassLoader3@166a22b{
url=file:/C:/tmp/JBoss/jboss-3.0.5RC2/server/default/tmp/deploy/server/default/conf/
jboss-service.xml/1.jboss-service.xml ,addedOrder=2}
其內容爲裝載庫中特定Class實例信息的詳細清單(前提是它已經被裝載),後面的內容給出了存在該Class文件的類裝載器.如果某類和多個類裝載器關聯,則存在類裝載錯誤隱患.
(2)範圍類
如 果需要部署某應用的多個版本,JBoss 3.x默認的類裝載模型則要求將各個應用部署在單獨的JBoss服務器中.爲獲得安全性和資源監控的更多控制,往往需要這樣做,但管理多個服務器實例並不 是一件容易的事情.現存的一種替換機制是,使用基於範圍(scoping)部署的方式來實現某應用多版本的部署.
對於基於範圍部署的情形,各個部 署創建HeirarchicalLoaderRepository3形式的,各自的類裝載庫.其中,JBoss服務器在將包含在EAR中的部署單元實例委 派給默認UnifiedLoaderRepository3前,UnifiedClassLoader3會首先將其裝載到自己的 HeirarchicalLoaderRepository3中.爲了實現EAR特定裝載庫,需要創建列表2-10所示的內容,並將其存放到META- INF/jboss-app.xml配置描述符中.
列表2-10 爲實現EAR級範圍類的jboss-app.xml實例
some.dot.com:loader=webtest.ear
其中,loader-repository元素的取值是JMX ObjectName,從而爲EAR創建自身的庫.該值必須是惟一有效的JMX ObjectName,但實際上不是很重要.
(3)完整的類裝載模型
本 書上面的內容在討論核心類裝載組件時,仔細介紹了自定義的UnifiedClassLoader3和UnifiedLoaderRepository3 類,從而形成了共享類裝載空間.完整的類裝載描述必須包括UnifiedClassLoader3的雙親類裝載器及其他用於範圍和特殊用途類裝載目的的類 裝載器.圖2-5給出了用於包含EJB和WAR的EAR部署的類層次綱要.
圖2-5 完整的類裝載視圖
圖2-5相關內容的詳細解釋如下.
系統類裝載器:系統類裝載器節點指JVM主線程,或者運行嵌入式JBoss服務器的應用程序線程的線程上下文類裝載器(Thread Context ClassLoader,TCL).
ServerLoader:ServerLoader節點是指將類裝載任務委派給系統類裝載器的URLClasserLoader.它包含如下一些引導URL:
借 助於jboss.boot.library.list系統屬性而指定的所有URL.目前,存在相對於由jboss.lib.url屬性定義的 libraryURL的路徑約定.如果沒有指定jboss.lib.url屬性,則默認情況下爲jboss.home.url + /lib/.如果沒有指定jboss.boot.library屬性,則默認情況下指jaxp.jar,log4j-boot.jar,jboss-common.jar及jboss-system.jar.
JAXP jar的具體取值依賴於主入口的-j選項.它或者是crimson.jar,或者是xerces.jar.在默認情況下爲crimson.jar.
JBoss JMX jar(jboss-jmx.jar)和GNU regexjar(gnu-regexp.jar).
Oswego的並行jar,concurrent.jar.
藉助於-L命令行選項指定的任何jar庫文件.
藉助於-C命令行選項指定的任何其他的jar文件或目錄.
Server:Server 節點表示由org.jboss.system.server.Server接口實現創建的UnifiedClassLoader3集合.默認的接口實現除 了爲服務器conf目錄創建UCL外,也爲補丁目錄(patchDir)入口創建UCL.其中,創建的最後一個UCL作爲JBoss主線程上下文類裝載 器.將來,JBoss會將這些UCL合併到單個UCL中.現在,各個UCL支持多個URL.
所有UnifiedClassLoader3:UnifiedClassLoader3節點表示由部署器創建的UCL.其中,凡是部署掃描器能夠瀏覽到的內 容,比如EAR,JAR,WAR,SAR,目錄,manifest文件中引用的jar文件及包含的嵌入式部署單元,都被包含到這裏的UCL中.由於形成的 名字空間爲平面類型,因此不會存在不同部署jar文件中類的多個實例.如果存在多個實例,則僅僅會使用到初次裝載的類實例,但結果可能會和預期的存在差 別.本文在"範圍類"一節討論了基於EAR部署單元的範圍可見性機制.如果在特定的JBoss服務器部署某類的多個版本,則需要使用這種機制.
EJB DynClassLoader:EJB DynClassLoader節點繼承於URLClassLoader中,它藉助於簡單的HTTP WebService提供RMI動態類裝載.它指定了空的URL[],並將它委派給TCL,將其作爲雙親類裝載器.如果配置的HTTP WebService允許裝載系統級類,則藉助於HTTP能夠定位到UnifiedLoaderRepository3及系統classpath中所有的 類.
EJB ENCLoader:EJB ENCLoader節點爲URLClassLoader類型.其存在的目的只是爲EJB部署單元的java:comp JNDI上下文提供惟一的上下文.它指定了空的URL[],並將它委派給EJB DynClassLoader,將其作爲雙親類裝載器.
Web ENCLoader:Web ENCLoader節點爲URLClassLoader類型.其存在的目的只是爲Web部署單元的java:comp JNDI上下文提供惟一的上下文.它指定了空的URL[],並將它委派給TCL,將其作爲雙親類裝載器.
WAR 裝載器:WAR裝載器是具體Servlet容器提供的類裝載器.該類裝載器委派給Web ENCLoader,並將其作爲雙親類裝載器.其默認行爲是,首先從雙親類裝載器裝載類,然後再去WEB-INF/{classes,lib}目錄.如果 開發者使用Servlet 2.3類裝載模型,則首先從WAR的WEB-INF/{classes,lib}目錄裝載類,然後再去其雙親類裝載器.
JBoss 3.x的類裝載架構存在很多優缺點.其優點如下:
爲了能夠在不同部署單元實現類訪問,不用再去不同部署單元中放置重複類.
JBoss後續版本可能會對庫做新的劃分,比如劃分爲域(domain),依賴及衝突檢測,等等.
與此同時,相應的缺點如下:
爲避免重複類,可能需要對現有的部署程序重新打包.位於裝載庫中的重複類依據類裝載方式的不同,可能導致ClassCastException和LinkageError異常.從JBoss 3.0.5RC1發佈版開始,這種情況一般不會發生,但還是可能發生.
需要隔離不同EAR中相同類的不同版本的部署,並通過jboss-app.xml配置描述符定義惟一的HeirarchicalLoaderRepository3.
2.2.3 JBoss XMBeans
XMBean 是JMX ModelMBean的JBoss JMX實現版本.XMBean具有豐富的DynamicMBean元數據,而且並不需要通過乏味的編程以直接實現DynamicMBean接口所要求的開 發內容.JBoss的模型MBean實現允許通過XML描述符指定組件的管理接口.其中,XMBean中的"X"即代表這裏使用XML配置描述符的實現方 式.XMBean除了提供簡單機制來描述DynamicMBean所要求的元數據外,它還能夠實現屬性持久化,緩存行爲,甚至提供高級定製功能,比如 MBean實現攔截器.圖2-6展示了用於XMBean描述符的jboss_xmbean_1_0.dtd文件中的高層元素.該文件的詳細展開細節請參考 圖2-12.
圖2-6 JBoss 1.0 XMBean DTD概述(jboss_xmbean_1_0.dtd)
圖中,mbean是 文檔的根元素,它包含了用於描述MBean(constructor,attribute,operation及notification)管理接口所要 求的元素.同時,它還包括可選description元素以描述MBean的目的,可選descriptors元素以描述屬性持久化策略,屬性緩存等內 容.
1.descriptors
descriptors元素含有所含元素及子元素的所有描述符.無論是JMX規範建議的,還是JBoss 使用的descriptors元素都存在預定義的元素和屬性.其中,自定義descriptors存在普通descriptor元素,該元素具有name 和value屬性.圖2-7展示了descriptors元素的內容模型.
圖2-7 descriptors元素的內容模型
descriptors元素中的主要子元素如下.
interceptors:interceptors 元素用於定製攔截器棧以替代默認棧.當前,這種做法只能在MBean級使用,但將來能夠在自定義屬性或操作級使用攔截器棧.interceptors元素 的內容指定自定義攔截器棧.如果沒有指定interceptors元素,JBoss將使用標準的模型MBean攔截器.其中,標準的攔截器有:
org.jboss.mx.interceptor.PersistenceInterceptor
org.jboss.mx.interceptor.MBeanAttributeInterceptor
org.jboss.mx.interceptor.ObjectReferenceInterceptor
當指定自定義的攔截器棧時,通常都會包括標準的攔截器,除非開發者打算替代它們.
每 個interceptors元素的取值需要給出攔截器實現的全限定類名.其中,該類必須實現org.jboss.mx.interceptor.Interceptor接口.它的構建器或者爲無參,或接受(javax.management.MBeanInfo,org.jboss.mx.server.MBeanInvoker)參數對.
interceptors元素可能存在若干屬 性.其中,這些屬性對應於攔截器類實現中以JavaBean風格實現的屬性.對於各個指定的interceptor元素屬性,JBoss將查詢攔截器類以 尋找到匹配它的setter方法.通過使用與類型關聯的java.beans.PropertyEditor能夠將屬性值轉換爲攔截器類屬性的實際類型. 如果不存在屬性對應的setter方法或與之關聯的PropertyEditor時,在interceptors元素中指定該屬性將觸發錯誤出現.
persistence:persistence元素包含了JMX規範建議的persistPolicy,persistPeriod,persistLocation及persistName屬性.其各自的含義如下.
persistPolicy:該屬性定義了何時持久化屬性,而且其取值範圍如下:
Never:表明屬性從不持久化.
OnUpdate:當屬性值發生變化時,持久它.
OnTimer:基於persistPeriod指定的時間實現屬性的持久化.
NoMoreOftenThan:一旦屬性值發生變化,便持久它.但前提是,持久的頻率不能夠超過persistPeriod指定的時間.
persistPeriod:當persistPolicy屬性值爲OnTimer或NoMoreOftenThan時,persistPeriod指定了屬性的更新頻率(單位:毫秒).
persistLocation:persistLocation屬性指定持久源的位置,其具體的表達方式取決於JMX持久化實現.目前,如果使用JBoss默認持久化管理器,屬性將被序列化到某個指定目錄.
persistName:該屬性與persistLocation一起使用,從而進一步指定持久源的位置.比如,如果persistLocation屬性取值爲目錄,則persistName將指定文件名,以在該目錄下存儲屬性.
currencyTimeLimit:currencyTimeLimit 元素指定被緩存屬性值(單位:秒)的有效時限.如果currencyTimeLimit取值爲0,則表明總是應該從MBean中獲得屬性值,而不去緩存 它.如果currencyTimeLimit取值爲–1,則表明被緩存的屬性值從不失效.
state-action-on-update:state-action-on-update元素給出當更新任一屬性時MBean會採取的行動,即MBean生命週期狀態的改變.行動的內容由該屬性值指定,其具體取值範圍爲:
keep-running
restart
reconfigure
reinstantiate
然而,JBoss當前並未使用該元素.
display-name:display-name元素描述某項內容,以便於人們更好地理解其含義.
default:當未指定某屬性時,default元素給出其默認取值.有一點請注意,即在啓動MBean時,該值並不會像jboss-service.xml中的元素內容取值而保留在MBean中.該屬性值僅僅用於沒有定義訪問屬性的入口方法及value元素的場景.
value:value元素指定管理屬性的當前值.同default元素不同,在啓動MBean時,它的取值能夠通過定義的setter方法寫入到MBean中.
persistence- manager:persistence-manager元素給出類名,作爲持久化管理器.其中,該類名實現了org.jboss.mx.persistence.PersistenceManager接口.當前,JBoss僅提供org.jboss.mx.persistence.ObjectStreamPersistenceManager持久化管理器,它使用Java序列化 將ModelMBeanInfo內容存儲到文件中.
descriptor:descriptor元素指定JBoss之外的值對.其name屬性指明該descriptor元素類型,value屬性指明該descriptor元素值.descriptor元素用於提供附加的管理元數據.
請注意,constructor,attribute,operation及notification元素可能都含有descriptors元素,它指定了規範要求的descriptors內容和期望的描述符擴展設置.
2.管理類
class元素用於指定管理對象的全限定名,其管理接口通過XMBean描述符給出.
3.構建器
爲創建管理對象實例,constructor元素指明瞭可用的構建器.圖2-8給出了constructor元素及其內容模型.
圖2-8 XMBean constructor元素及其內容模型
它包含的幾個較重要子元素如下:
description:構建器的描述.
name:構建器的名字,其內容必須和實現類相同.
parameter:描述構建器參數.其中,參數值具備如下屬性:
description:參數的可選描述.
name:要求的參數變量名.
type:要求的參數類型全限定類名.
descriptors:與構建器元數據關聯的任意descriptors.
4.屬性
attribute元素指定MBean暴露的管理屬性.圖2-9描述了attribute元素及其內容模型.
圖2-9 XMBean attribute元素及其內容模型
attribute元素支持的屬性如下:
access:它爲可選元素,用於定義屬性的讀/寫模式.其具體取值範圍如下:
read-only:只讀屬性.
write-only:只寫屬性.
read-write:默認情況下,該屬性爲可讀,可寫.
getMethod:該元素定義了方法名,以讀取命名方法名.如果需要從MBean實例獲得該管理屬性,則必須指定該元素.
setMethod:該元素定義了方法名,以寫入命名方法名.如果需要從MBean實例獲得該管理屬性,則必須指定該元素.
attribute元素其餘較重要的子元素如下:
description:atttribute元素的描述.
name:attribute的名稱,供MBeanServer.getAttribute()操作使用.
type:屬性類型的全限定類名.
descriptors:其他任意descriptors,比如屬性持久化,緩存,默認值,等等.
5.操作
XMBean需要藉助於一個或多個operation元素暴露其管理操作.圖2-10展示了operation元素及其內容模型.
圖2-10 XMBean operation元素及其內容模型
其中,impact屬性定義對操作執行的影響,其具體取值範圍如下:
ACTION:該操作改變MBean組件的狀態(寫操作).
INFO:該操作不改變MBean組件的狀態(讀操作).
ACTION_INFO:類似於讀,寫操作.
operation的子元素如下:
description:description元素爲operation元素給出便於開發者理解的描述信息.
name:name元素給出operation名.
parameter:parameter元素給出operation的參數定義.
return-type:return-type元素定義操作返回類型的全限定類名.如果沒有指定,則爲void類型.
descriptors:同operation元數據關聯的任意descriptors.
6.通知
notification元素描述XMBean提交的管理通知.圖2-11給出了notification元素及其內容模型.
圖2-11 XMBean notification元素及其內容模型
其子元素如下:
description:description元素爲notification元素給出便於開發者理解的描述信息.
name:name元素含有notification類的全限定類名.
notification-type:notification-type元素含有以"."隔開的字符串,它用於描述通知類型.
descriptors:同notification元數據關聯的任意descriptors.
圖2-12給出了jboss_xmbean_1_0.dtd的完整內容模型.在2.4.3中的"2.XMBean實例"一節,在討論JBoss MBean服務的過程中,將給出創建XMBean實例的完整過程.
圖2-12 jboss_xmbean_1_0.dtd的展開視圖
2.3 連接到JMX服務器
爲從運行JBoss服務器虛擬機外部實現對JMX MBeanServer的訪問,JBoss提供了相應的適配器.目前,JBoss提供的適配器包括HTML,RMI接口及EJB等內容.
2.3.1 瀏覽服務器——JMX控制檯Web應用
從JBoss 3.0.1開始,JBoss提供了其實現的JMX HTML適配器,即允許通過標準Web瀏覽器查看MBeanServer中的MBean.控制檯Web應用的默認URL地址爲 http://localhost:8080/jmx-console/.用戶打開該URL後,將出現類似於圖2-13所示的結果.
圖2-13 JBoss JMX控制檯Web應用代理視圖
上 述視圖稱爲代理視圖.它提供了所有註冊到MBeanServer的MBean列表集合,並且根據MBean ObjectName的域劃分順序排列.各個域底下給出了相應的MBean列表.當選中某MBean時,瀏覽器將打開該MBean視圖,從而實現 MBean屬性查看和編輯,並能夠調用方法.比如,圖2-14給出了對應於"jboss.system:type=Server"的MBean視圖.
圖2-14"jboss.system:type=Server"MBean的視圖
JMX 控制檯Web應用的源代碼位於jboss-all/varia模塊的src/main/org/jboss/jmx目錄.另外,其Web頁面位於jboss-all/varia/src/resources/jmx目錄.該應用藉助於MBeanServer,實現了基於JSP視圖和Servlet 的MVC模式實現.
1.保護JMX控制檯
由於JMX控制檯Web應用只是標準的Servlet,因此開發者能夠使用基於安全性的標準 J2EE角色來保護它.同時,該控制檯Web應用自從JBoss 3.0.1發佈版開始就包括在JBoss中.部署的jmx-console.war並沒有打包在.war中,使得開發者能夠快速地編輯簡單用戶名和密碼, 以獲得安全性約束功能.如果開發者查看server/default/deploy目錄中的jmx-console.war,將在Web-INF目錄找到 web.xml和jboss-web.xml描述符,同時還能夠在Web-INF/classes目錄找到roles.properties和 users.properties文件.具體操作如下:
[nr@toki jboss-3.2.3]$ ls server/default/deploy/jmx-console.war/WEB-INFclassesjboss-web.xml
web.xml
[nr@toki jboss-3.2.3]$ lsserver/default/deploy/jmx-console.war/WEB-INF/classesorg roles.
properties users.properties
開 發者在解開web.xml和jboss-web.xml描述符中安全性部分的註釋後(見列表2-11), HTTP BASIC認證便生效了,即僅僅能夠通過用戶名admin,密碼admin實現jmx-console應用程序的約束訪問.其中,上述用戶名是由Web-INF/classes/users.properties文件中的admin=admin行決定的.
列表2-11 jmx-console.war中解開安全性元素後的web.xml和jboss-web.xml描述符



HtmlAdaptor
An example security config that only allows users with the
role JBossAdmin to access the HTML JMX console web application

/*
GET
POST


JBossAdmin




BASIC
JBoss JMX Console


JBossAdmin


java:/jaas/jmx-console
然後,開發者需要保存相應的修改,並啓動服務器(如果沒有啓動),試試訪問jmx-console URL.最後,開發者能看到類似於圖2-15所示的對話框.
圖2-15 保存列表2-11後展示出jmx-console basic HTTP登錄對話框
通常情況下,採用.properties文件來保護JMX控制檯應用的做法很不安全.本書第8章將詳細闡述如何正確地配置Web應用的安全性設置.
2.3.2 使用RMI連接到JMX
JBoss提供了RMI接口,以連接到JMX MBeanServer.其接口爲(見列表2-12):org.jboss.jmx.adaptor.rmi.RMIAdaptor.
列表2-12 RMIAdaptor接口
/*
* JBoss, the OpenSource J2EE webOS
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jboss.jmx.adaptor.rmi;
import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.ObjectName;
import javax.management.QueryExp;
import javax.management.ObjectInstance;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;
import javax.management.MBeanInfo;
import javax.management.AttributeNotFoundException;
import javax.management.InstanceAlreadyExistsException;
import javax.management.InstanceNotFoundException;
import javax.management.IntrospectionException;
import javax.management.InvalidAttributeValueException;
import javax.management.ListenerNotFoundException;
import javax.management.MBeanException;
import javax.management.MBeanRegistrationException;
import javax.management.NotCompliantMBeanException;
import javax.management.OperationsException;
import javax.management.ReflectionException;
public interface RMIAdaptor
extends java.rmi.Remote
{
public ObjectInstance createMBean(String pClassName, ObjectName pName)
throws ReflectionException,
InstanceAlreadyExistsException,
MBeanRegistrationException,
MBeanException,
NotCompliantMBeanException,
RemoteException;
public ObjectInstance createMBean(String pClassName, ObjectName pName,
ObjectName pLoaderName)
throws ReflectionException,
InstanceAlreadyExistsException,
MBeanRegistrationException,
MBeanException,
NotCompliantMBeanException,
InstanceNotFoundException,
RemoteException;
public ObjectInstance createMBean(String pClassName, ObjectName pName,
Object[] pParams, String[] pSignature)
throws ReflectionException,
InstanceAlreadyExistsException,
MBeanRegistrationException,
MBeanException,
NotCompliantMBeanException,
RemoteException;
public ObjectInstance createMBean(String pClassName, ObjectName pName,
ObjectName pLoaderName, Object[] pParams, String[] pSignature)
throws ReflectionException,
InstanceAlreadyExistsException,
MBeanRegistrationException,
MBeanException,
NotCompliantMBeanException,
InstanceNotFoundException,
RemoteException;
public void unregisterMBean(ObjectName pName)
throws InstanceNotFoundException,
MBeanRegistrationException,
RemoteException;
public ObjectInstance getObjectInstance(ObjectName pName)
throws InstanceNotFoundException,
RemoteException;
public Set queryMBeans(ObjectName pName, QueryExp pQuery)
throws RemoteException;
public Set queryNames(ObjectName pName, QueryExp pQuery)
throws RemoteException;
public boolean isRegistered(ObjectName pName)
throws RemoteException;
public boolean isInstanceOf(ObjectName pName, String pClassName)
throws InstanceNotFoundException,
RemoteException;
public Integer getMBeanCount()
throws RemoteException;
public Object getAttribute(ObjectName pName, String pAttribute)
throws MBeanException,
AttributeNotFoundException,
InstanceNotFoundException,
ReflectionException,
RemoteException;
public AttributeList getAttributes(ObjectName pName, String[] pAttributes)
throws InstanceNotFoundException,
ReflectionException,
RemoteException;
public void setAttribute(ObjectName pName, Attribute pAttribute)
throws InstanceNotFoundException,
AttributeNotFoundException,
InvalidAttributeValueException,
MBeanException,
ReflectionException,
RemoteException;
public AttributeList setAttributes(ObjectName pName, AttributeList pAttributes)
throws InstanceNotFoundException,
ReflectionException,
RemoteException;
public Object invoke(ObjectName pName, String pActionName,
Object[] pParams, String[] pSignature)
throws InstanceNotFoundException,
MBeanException,
ReflectionException,
RemoteException;
public String getDefaultDomain()
throws RemoteException;
public void addNotificationListener(ObjectName pName, ObjectName pListener,
NotificationFilter pFilter, Object pHandback)
throws InstanceNotFoundException,
RemoteException;
public void removeNotificationListener(ObjectName pName, ObjectName pListener)
throws InstanceNotFoundException,
ListenerNotFoundException,
RemoteException;
public MBeanInfo getMBeanInfo(ObjectName pName)
throws InstanceNotFoundException,
IntrospectionException,
ReflectionException,
RemoteException;
}
其 中,RMIAdaptor接口被org.jboss.jmx.adaptor.rmi.RMIAdaptorService MBean服務綁定到JNDI中.儘管從JBoss 3.2.2發佈版開始,已經將該服務從deploy目錄刪除,但是通過docs/examples/jmx目錄還是能夠找到該服務.同時,由於JBoss 推薦使用通過Invoker適配器服務連接到JMX MBeanServer,因此這種方法已經被JBoss丟棄.當然,考慮到與現有客戶的兼容性,Invoker適配器服務還是支持RMIAdaptor接 口,並將其綁定到默認位置"jmx/rmi/RMIAdaptor".另外,"2.7.1 分離式Invoker實例:MBeanServer Invoker適配器服務"一節詳細討論了Invoker適配器服務.RMIAdaptorService仍然能夠爲那些需要接收JMX通知的遠程客戶而 提供服務.由於Invoker適配器服務並不具備這項功能,因此如果需要,則必須將jmx-rmi-adaptor.sar(位於examples目錄) 替代jmx-invoker-adaptor-server.sar存檔.
RMIAdaptorService以jmx-rmi-adaptor.sar的形式部署,而且支持下列屬性.
JndiName: 綁定RMIAdaptor接口的JNDI名字.默認的名字爲jmx/rmi/ RMIAdaptor.在JBoss 3.0.4之前,該JNDI名字硬編碼(hard-coded)成"jmx:" + + ":rmi".其中,取值可以通過InetAddress.getLoalHost().getHostName()獲得.考慮到後向兼容性,目前該綁定仍然存在,在後續版本中有可能會被丟棄.
RMIObjectPort:用於導出(exported)RMI對象的服務器端監聽端口號.在默認情況下,其取值爲0,即允許JBoss開發者選擇匿名的可用端口.
ServerAddress:與RMIObjectPort對應,用於導出RMI對象的服務器接口名或IP地址.在默認情況下爲空值,即允許綁定到所有可用的接口.
BackLog:在連接錯誤出現前,RMI對象服務器Socket還未處理完成的客戶連接請求.
列表2-13給出了某客戶應用,它利用RMIAdaptor接口實現JNDIView MBean中的MBeanInfo信息查詢.該應用也調用了MBean的list(boolean)方法,並將結果顯示出來.
列表2-13 使用RMIAdapter的JMX客戶
package org.jboss.chap2.ex4;
import javax.management.MBeanInfo;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanParameterInfo;
import javax.management.ObjectName;
import javax.naming.InitialContext;
import org.jboss.jmx.adaptor.rmi.RMIAdaptor;
/** A client that demonstrates how to connect to the JMX server using the RMI
adaptor.
@author [email protected]
@version $Revision: 1.2 $
*/
public class JMXBrowser
{

/**
* @param args the command line arguments
*/
public static void main(String[] args) throws Exception
{
InitialContext ic = new InitialContext();
RMIAdaptor server = (RMIAdaptor) ic.lookup("jmx/rmi/RMIAdaptor");
// Get the MBeanInfo for the JNDIView MBean
ObjectName name = new ObjectName("jboss:service=JNDIView");
MBeanInfo info = server.getMBeanInfo(name);
System.out.println("JNDIView Class: "+info.getClassName());
MBeanOperationInfo[] opInfo = info.getOperations();
System.out.println("JNDIView Operations: ");
for(int o = 0; o < opInfo.length; o ++)
{
MBeanOperationInfo op = opInfo[o];
String returnType = op.getReturnType();
String opName = op.getName();
System.out.print(" + "+returnType+""+opName+"(");
MBeanParameterInfo[] params = op.getSignature();
for(int p = 0; p < params.length; p ++)
{
MBeanParameterInfo paramInfo = params[p];
String pname = paramInfo.getName();
String type = paramInfo.getType();
if( pname.equals(type) )
System.out.print(type);
else
System.out.print(type+" "+name);
if( p < params.length-1 )
System.out.print(',');
}
System.out.println(")");
}
// Invoke the list(boolean) op
String[] sig = {"boolean"};
Object[] opArgs = {Boolean.TRUE};
Object result = server.invoke(name, "list", opArgs, sig);
System.out.println("JNDIView.list(true) output:\n"+result);
}
}
爲測試RMIAdaptor客戶應用,運行如下命令:
[orb@toki examples]$ ant -Dchap=chap2 -Dex=4 run-example
Buildfile: build.xml
validate:
[java] ImplementationTitle: JBoss [WonderLand]
[java] ImplementationVendor: JBoss.org
[java] ImplementationVersion: 3.2.3 (build: CVSTag=JBoss_3_2_3date=200311301445)
[java] SpecificationTitle: JBoss
[java] SpecificationVendor: JBoss (http://www.jboss.org/)
[java] SpecificationVersion: 3.2.3
[java] JBoss version is: 3.2.3
fail_if_not_valid:
init:
[echo] Using jboss.dist=/Users/orb/java/jboss-3.2.3
compile:
[javac] Compiling 3 source files to /Users/orb/Desktop/jboss/AdminDevel2/examples/output/classes
run-example:
run-example4:
[java] JNDIView Class: org.jboss.mx.modelmbean.XMBean
[java] JNDIView Operations:
[java] + java.lang.String list(boolean jboss:service=JNDIView)
[java] + java.lang.String listXML()
[java] + void create()
[java] + void start()
[java] + void stop()
[java] + void destroy()
[java] + java.lang.String getName()
[java] + int getState()
[java] + java.lang.String getStateString()
[java] JNDIView.list(true) output:
[java] Ejb Module: ClusteredHttpSessionEB.jar
[java] java:comp namespace of the ClusteredHTTPSession bean:
[java]
[java] +- env (class: org.jnp.interfaces.NamingContext)
[java]
[java] java: Namespace
[java]
[java] +- jaas (class: javax.naming.Context)
[java] | +- jbossmq-httpil (class:org.jboss.security.plugins.SecurityDomainContext)
[java] | +- other (class: org.jboss.security.plugins.SecurityDomainContext)
[java] | +- JmsXARealm (class:org.jboss.security.plugins.SecurityDomainContext)
[java] | +- jbosstest-web (class: org.jboss.security.plugins.SecurityDomainContext)
[java] | +- http-invoker (class:org.jboss.security.plugins.SecurityDomainContext)
[java] | +- jbossmq (class: org.jboss.security.plugins.SecurityDomainContext)
[java] | +- secure-jndi (class: org.jboss.security.plugins.SecurityDomainContext)
[java] | +- HsqlDbRealm (class:org.jboss.security.plugins.SecurityDomainContext)
[java] +- TransactionPropagationContextImporter (class:org.jboss.tm.TransactionPropagation
ContextImporter)
[java] +- JmsXA (class: org.jboss.resource.adapter.jms.JmsConnectionFactoryImpl)
[java] +- JBossCorbaNaming (class: org.omg.CosNaming.NamingContextExt)
[java] +- DefaultDS (class: org.jboss.resource.adapter.jdbc.WrapperDataSource)
[java] +- StdJMSPool (class: org.jboss.jms.asf.StdServerSessionPoolFactory)
[java] +- TransactionManager (class: org.jboss.tm.TxManager)
[java] +- JBossCorbaPOA (class: org.omg.PortableServer.POA)
[java] +- TransactionPropagationContextExporter (class:org.jboss.tm.TransactionPropagation
ContextFactory) [java] +- ConnectionFactory (class:org.jboss.mq.SpyConnectionFactory)
[java] +- DefaultJMSProvider (class: org.jboss.jms.jndi.JBossMQProvider)
[java] +- XAConnectionFactory (class: org.jboss.mq.SpyXAConnectionFactory)
[java] +- JBossCorbaInterfaceRepositoryPOA (class: org.omg.PortableServer.POA)
[java] +- Mail (class: javax.mail.Session)
[java] +- JBossCorbaORB (class: org.omg.CORBA.ORB)
[java] +- cmrTransactionTest (class: org.jnp.interfaces.NamingContext)
[java] +- timedCacheFactory (class: javax.naming.Context)
[java] Failed to lookup: timedCacheFactory, errmsg=null
[java] +- SecurityProxyFactory (class:org.jboss.security.SubjectSecurityProxyFactory)
[java] +- comp (class: javax.naming.Context)
[java]
[java] Global JNDI Namespace
[java]
[java] +- OIL2ConnectionFactory (class: org.jboss.mq.SpyConnectionFactory)
[java] +- hello (class: org.jnp.interfaces.NamingContext)
[java] +- HAPartition (class: org.jnp.interfaces.NamingContext)
[java] | +- DefaultPartition (class: org.jboss.ha.framework.server.HAPartitionImpl)
[java] +- helloworld (class: org.jnp.interfaces.NamingContext)
[java] +- jbosstest (class: org.jnp.interfaces.NamingContext)
[java] | +- ejbs (class: org.jnp.interfaces.NamingContext)
[java] | | +- local (class: org.jnp.interfaces.NamingContext)
[java] +- XAConnectionFactory (class: org.jboss.mq.SpyXAConnectionFactory)
[java] +- queue (class: org.jnp.interfaces.NamingContext)
[java] | +- D (class: org.jboss.mq.SpyQueue)
[java] | +- C (class: org.jboss.mq.SpyQueue)
[java] | +- B (class: org.jboss.mq.SpyQueue)
[java] | +- A (class: org.jboss.mq.SpyQueue)
[java] | +- testQueue (class: org.jboss.mq.SpyQueue)
[java] | +- ex (class: org.jboss.mq.SpyQueue)
[java] | +- testObjectMessage (class: org.jboss.mq.SpyQueue)
[java] | +- DLQ (class: org.jboss.mq.SpyQueue)
[java] +- test (class: org.jnp.interfaces.NamingContext)
[java] | +- entity (class: org.jnp.interfaces.NamingContext)
[java] +- RMIConnectionFactory (class: org.jboss.mq.SpyConnectionFactory)
[java] +- exception (class: org.jnp.interfaces.NamingContext)
[java] +- UUIDKeyGeneratorFactory (class:org.jboss.ejb.plugins.keygenerator.uuid.UUIDKey
GeneratorFactory)
[java] +- testTCF[link -> ConnectionFactory] (class: javax.naming.LinkRef)
[java] +- ENCTests (class: org.jnp.interfaces.NamingContext)
[java] | +- ejbs (class: org.jnp.interfaces.NamingContext)
[java] +- idgen (class: org.jnp.interfaces.NamingContext)
[java] +- ejbcts2 (class: org.jnp.interfaces.NamingContext)
[java] +- commerce (class: org.jnp.interfaces.NamingContext)
[java] +- UIL2ConnectionFactory (class: org.jboss.mq.SpyConnectionFactory)
[java] +- UserTransactionSessionFactory (proxy: $Proxy10 implements interfaceorg.jboss.tm.
usertx.interfaces.UserTransactionSessionFactory)
[java] +- UILXAConnectionFactory[link -> UIL2XAConnectionFactory]
(class: javax.naming.LinkRef)
[java] +- relation (class: org.jnp.interfaces.NamingContext)
[java] | +- manyToMany (class: org.jnp.interfaces.NamingContext)
[java] | | +- bidirectional (class: org.jnp.interfaces.NamingContext)
[java] | | +- unidirectional (class: org.jnp.interfaces.NamingContext)
[java] | +- manyToOne (class: org.jnp.interfaces.NamingContext)
[java] | | +- unidirectional (class: org.jnp.interfaces.NamingContext)
[java] | | | +- table (class: org.jnp.interfaces.NamingContext)
[java] | | | +- fk (class: org.jnp.interfaces.NamingContext)
[java] | +- oneToMany (class: org.jnp.interfaces.NamingContext)
[java] | | +- bidirectional (class: org.jnp.interfaces.NamingContext)
[java] | | | +- table (class: org.jnp.interfaces.NamingContext)
[java] | | | +- fk (class: org.jnp.interfaces.NamingContext)
[java] | | +- unidirectional (class: org.jnp.interfaces.NamingContext)
[java] | | | +- table (class: org.jnp.interfaces.NamingContext)
[java] | | | +- fk (class: org.jnp.interfaces.NamingContext)
[java] | +- oneToOne (class: org.jnp.interfaces.NamingContext)
[java] | | +- bidirectional (class: org.jnp.interfaces.NamingContext)
[java] | | | +- table (class: org.jnp.interfaces.NamingContext)
[java] | | | +- fk (class: org.jnp.interfaces.NamingContext)
[java] | | +- unidirectional (class: org.jnp.interfaces.NamingContext)
[java] | | | +- table (class: org.jnp.interfaces.NamingContext)
[java] | | | +- fk (class: org.jnp.interfaces.NamingContext)
[java] +- console (class: org.jnp.interfaces.NamingContext)
[java] | +- PluginManager (proxy: $Proxy26 implements interface
org.jboss.console.manager.PluginManagerMBean)
[java] +- HTTPXAConnectionFactory (class: org.jboss.mq.SpyXAConnectionFactory)
[java] +- topic (class: org.jnp.interfaces.NamingContext)
[java] | +- testDurableTopic (class: org.jboss.mq.SpyTopic)
[java] | +- testTopic (class: org.jboss.mq.SpyTopic)
[java] | +- securedTopic (class: org.jboss.mq.SpyTopic)
[java] +- testQCF[link -> ConnectionFactory] (class: javax.naming.LinkRef)
[java] +- HAILXAConnectionFactory (class: org.jboss.mq.SpyXAConnectionFactory)
[java] +- ejb (class: org.jnp.interfaces.NamingContext)
[java] | +- local (class: org.jnp.interfaces.NamingContext)
[java] | +- jca (class: org.jnp.interfaces.NamingContext)
[java] | +- remote (class: org.jnp.interfaces.NamingContext)
[java] +- cmrTransactionTest (class: org.jnp.interfaces.NamingContext)
[java] +- UserTransaction (class: org.jboss.tm.usertx.client.ClientUserTransaction)
[java] +- HTTPConnectionFactory (class: org.jboss.mq.SpyConnectionFactory)
[java] +- arrays (class: org.jnp.interfaces.NamingContext)
[java] +- RMIXAConnectionFactory (class: org.jboss.mq.SpyXAConnectionFactory)
[java] +- HAILConnectionFactory (class: org.jboss.mq.SpyConnectionFactory)
[java] +- anotherContext (class: org.jnp.interfaces.NamingContext)
[java] | +- TopicInADifferentContext[link -> topic/myMDBTopic] (class:javax.naming.LinkRef)
[java] | +- QueueInADifferentContext[link -> queue/myMDBQueue] (class:javax.naming.LinkRef)
[java] +- psuedo-url: (class: org.jnp.interfaces.NamingContext)
[java] | +- ejb (class: org.jnp.interfaces.NamingContext)
[java] +- local (class: org.jnp.interfaces.NamingContext)
[java] +- eardeployment (class: org.jnp.interfaces.NamingContext)
[java] +- OIL2XAConnectionFactory (class: org.jboss.mq.SpyXAConnectionFactory)
[java] +- HASessionState (class: org.jnp.interfaces.NamingContext)
[java] | +- Default (class: org.jboss.ha.hasessionstate.server.HASessionStateImpl)
[java] +- ejbcts (class: org.jnp.interfaces.NamingContext)
[java] +- UIL2XAConnectionFactory (class: org.jboss.mq.SpyXAConnectionFactory)
[java] +- naming (class: org.jnp.interfaces.NamingContext)
[java] | +- local (class: org.jnp.interfaces.NamingContext)
[java] +- invokers (class: org.jnp.interfaces.NamingContext)
[java] | +- toki.local (class: org.jnp.interfaces.NamingContext)
[java] | | +- iiop (class: org.jboss.invocation.iiop.IIOPInvoker)
[java] | | +- http (class: org.jboss.invocation.http.interfaces.HttpInvokerProxy)
[java] | +- 0.0.0.0 (class: org.jnp.interfaces.NamingContext)
[java] | | +- pooled (class:org.jboss.invocation.pooled.interfaces.PooledInvokerProxy)
[java] +- UILConnectionFactory[link -> UIL2ConnectionFactory] (class:javax.naming.LinkRef)
[java] +- jmx (class: org.jnp.interfaces.NamingContext)
[java] | +- invoker (class: org.jnp.interfaces.NamingContext)
[java] | | +- RMIAdaptor (proxy: $Proxy25 implements interface org.jboss.
jmx.adaptor.rmi.RMIAdaptor) [java] | +- rmi (class:org.jnp.interfaces.NamingContext)
[java] | | +- RMIAdaptor[link -> jmx/invoker/RMIAdaptor] (class:javax.naming.LinkRef)
[java] +- clustering (class: org.jnp.interfaces.NamingContext)
[java] | +- HTTPSession (proxy: $Proxy31 implements interface org.jboss.ha.
httpsession.beanimpl.interfaces.ClusteredHTTPSessionHome,interfacejavax.ejb.Handle)
[java] | +- LocalHTTPSession (proxy: $Proxy28 implements interfaceorg.jboss.ha.httpsession.
beanimpl.interfaces.LocalClusteredHTTPSessionHome)
[java] +- ConnectionFactory (class: org.jboss.mq.SpyConnectionFactory)
[java] +- cmp2 (class: org.jnp.interfaces.NamingContext)
[java] | +- perf (class: org.jnp.interfaces.NamingContext)
[java] | +- audit (class: org.jnp.interfaces.NamingContext)
[java] | +- readonly (class: org.jnp.interfaces.NamingContext)
[java] | +- simple (class: org.jnp.interfaces.NamingContext)
[java] | +- lob (class: org.jnp.interfaces.NamingContext)
[java] +- v2 (class: org.jnp.interfaces.NamingContext)
[java] | +- local (class: org.jnp.interfaces.NamingContext)
[java] +- v1 (class: org.jnp.interfaces.NamingContext)
[java] | +- local (class: org.jnp.interfaces.NamingContext)
[java]
2.3.3 命令行方式訪問JMX
從JBoss 3.2.1發佈版開始,JBoss就提供了初始版本的命令行工具,以獲得和遠程JMX服務器實例的簡單交互.該工具稱爲"twiddle"(位於發佈版的 bin目錄下),即一個輕量級,藉助於JMX與MBean交互的工具.twiddle是命令行執行工具,而不是一般性的命令Shell.通過 twiddle.sh或twiddle.bat腳本能夠運行twiddle.其中,以-h(--help)參數形式運行twiddle能夠獲得基本語法, 以--help-commands參數形式運行能夠獲得該工具提供的具體功能.具體用法如下.
[nr@toki bin]$ ./twiddle.sh -h
A JMX client to 'twiddle' with a remote JBoss server.
usage: twiddle [options] [command_arguments]
options:
-h, --help Show this help message
--help-commands Show a list of commands
-H= Show command specific help
-c=command.properties Specify the command.properties file to use
-D[=] Set a system property
-- Stop processing options
-s, --server= The JNDI URL of the remote server
-a, --adapter= The JNDI name of the RMI adapter to use
[nr@toki bin]$ ./twiddle.sh --help-commands
twiddle.sh commands:
get Get the values of one or more MBean attributes
invoke Invoke an operation on an MBean
unregister Unregister one or more MBeans
create Create an MBean
serverinfo Get information about the MBean server
query Query the server for a list of matching MBeans
info Get the metadata for an MBean
1.爲twiddle設置類路徑
在JBoss 3.2.3中,爲使用twiddle工具以RMI方式連接到JBoss服務器,開發者需要將jbossall-client.jar添加到JBOSS_CLASSPATH中.如果遇到如下錯誤:
[nr@toki bin]$ ./twiddle.sh serverinfo –list
twiddle.sh: org.jboss.util.NestedRuntimeException: - nested throwable:(javax.naming. CommunicationException [Root exception isjava.lang.ClassNotFoundException: org.jboss.proxy.Client Container (no securitymanager: RMI class loader disabled)])
則需要開發者將上述jar文件添加到JBOSS_CLASSPATH中,命令行運行如下:
[nr@toki bin]$ export JBOSS_CLASSPATH=../client.jbossall-client.jar
2.連接twiddle到遠程服務器
在 默認情況下,twiddle命令會連接到位於端口1099的localhost,以查詢默認RMIAdaptor服務的"jmx/rmi /RMIAdaptor"綁定.其中,RMIAdaptor服務在與JMX服務器通信的過程中,充當了連接器的作用.爲連接到不同的服務器/端口的組合, 開發者可以使用-s(--server)選項:
[nr@rubik bin]$ ./twiddle.sh -s toki serverinfo –d jboss
[nr@rubik bin]$ ./twiddle.sh -s toki:1099 serverinfo –d jboss
開發者使用-a(--adapter)選項能夠實現到不同RMIAdaptor綁定的連接:
[nr@rubik bin]$ ./twiddle.sh -s toki -a jmx/rmi/RMIAdaptor serverinfo –d jboss
[nr@rubik bin]$ ./twiddle.sh -s toki --adapter=jmx/rmi/RMIAdaptor serverinfo –djboss
3.twiddle命令用法示例
爲訪問服務器的基本信息,twiddle可使用serverinfo選項.目前支持的語法如下:
[nr@toki bin]$ ./twiddle.sh -H serverinfo
Get information about the MBean server
usage: serverinfo [options]
options:
-d, --domain Get the default domain
-c, --count Get the MBean count
-l, -- list List the MBeans
-- Stop processing options
[nr@rubik bin]$ ./twiddle.sh --server=toki serverinfo –count 385
[nr@rubik bin]$ ./twiddle.sh --server=toki serverinfo –domain jboss
爲根據特定模式獲得MBean名字,twiddle可以使用query選項以查詢服務器.目前支持如下語法:
[nr@rubik bin]$ ./twiddle.sh -H query
Query the server for a list of matching MBeans
usage: query [options]
options:
-c, --count Display the matching MBean count
-- Stop processing options
Examples:
query all mbeans: query '*:*'
query all mbeans in the jboss.j2ee domain: query 'jboss.j2ee:*'
[nr@rubik bin]$ ./twiddle.sh -s toki query 'jboss:service=invoker,*'
jboss:readonly=true,service=invoker,target=Naming,type=http
jboss:service=invoker,type=jrmp
jboss:service=invoker,type=httpHA
jboss:service=invoker,type=jrmpha
jboss:service=invoker,type=local
jboss:service=invoker,type=pooled
jboss:service=invoker,type=iiop
jboss:service=invoker,type=http
jboss:service=invoker,target=Naming,type=http
爲獲得MBean的屬性,twiddle可以使用get選項:
[nr@toki bin]$ ./twiddle.sh get jboss:service=invoker,type=jrmp
Get the values of one or more MBean attributes
usage: get [options] [+]
If no attribute names are given all readable attributes are gotten
options:
--noprefix Do not display attribute name prefixes
-- Stop processing options
[orb@toki bin]$ ./twiddle.sh get jboss:service=invoker,type=jrmp RMIObjectPort
StateString
RMIObjectPort=4444
StateString=Started
[nr@toki bin]$ ./twiddle.sh get jboss:service=invoker,type=jrmp
ServerAddress=0.0.0.0
StateString=Started
State=3
EnableClassCaching=false
SecurityDomain=null
RMIServerSocketFactory=null
Backlog=200
RMIObjectPort=4444
Name=JRMPInvoker
RMIClientSocketFactory=null
爲查詢MBean的MBeanInfo,twiddle可以使用info選項.具體如下:
[nr@toki bin]$ ./twiddle.sh -H info
Get the metadata for an MBean
usage: info
Use '*' to query for all attributes
[nr@toki bin]$ ./twiddle.sh info jboss:service=invoker,type=jrmp
Description: Management Bean.
+++ Attributes:
Name: ServerAddress
Type: java.lang.String
Access: rw
Name: StateString
Type: java.lang.String
Access: r-
Name: State
Type: int
Access: r-
Name: EnableClassCaching
Type: boolean
Access: rw
Name: SecurityDomain
Type: java.lang.String
Access: rw
Name: RMIServerSocketFactory
Type: java.lang.String
Access: rw
Name: Backlog
Type: int
Access: rw
Name: RMIObjectPort
Type: int
Access: rw
Name: Name
Type: java.lang.String
Access: r-
Name: RMIClientSocketFactory
Type: java.lang.String
Access: rw+++
Operations:
void start()
void create()
void stop()
void destroy()
爲調用MBean上的操作,twiddle可以使用invoke選項:
[nr@toki bin]$ ./twiddle.sh -H invoke
Invoke an operation on an MBean
usage: invoke [options] ()*
options:
-q, --query-type[=] Treat object name as a query
-- Stop processing options
query type:
f[irst] Only invoke on the first matching name [default]
a[ll] Invoke on all matching names
[nr@toki bin]$ ./twiddle.sh invoke jboss:service=JNDIView list true
Ejb Module: ClusteredHttpSessionEB.jar
java:comp namespace of the ClusteredHTTPSession bean:

+- env (class: org.jnp.interfaces.NamingContext)
java: Namespace

+- jaas (class: javax.naming.Context)
| +- jbossmq-httpil (class: org.jboss.security.plugins.SecurityDomainContext)
| +- other (class: org.jboss.security.plugins.SecurityDomainContext)
| +- JmsXARealm (class: org.jboss.security.plugins.SecurityDomainContext)
| +- jbosstest-web (class: org.jboss.security.plugins.SecurityDomainContext)
| +- http-invoker (class: org.jboss.security.plugins.SecurityDomainContext)
| +- jbossmq (class: org.jboss.security.plugins.SecurityDomainContext)
| +- secure-jndi (class: org.jboss.security.plugins.SecurityDomainContext)
| +- HsqlDbRealm (class: org.jboss.security.plugins.SecurityDomainContext)
+- TransactionPropagationContextImporter (class:org.jboss.tm.TransactionPropagation
ContextImporter)
+- JmsXA (class: org.jboss.resource.adapter.jms.JmsConnectionFactoryImpl)
+- JBossCorbaNaming (class: org.omg.CosNaming.NamingContextExt)
+- DefaultDS (class: org.jboss.resource.adapter.jdbc.WrapperDataSource)
...
2.3.4 使用任何協議連接到JMX
在 提供分離式Invoker和具備創建代理工廠能力(一定程度上)的前提下,開發者使用InvokerAdaptorService和代理工廠服務,並通過 任意協議而暴露的RMIAdaptor接口或類似接口,能夠實現與JMX服務器的通信.在"2.7 遠程訪問服務——分離式Invoker"一節的內容有分離式Invoker和代理工廠的介紹.其中,"2.7.1 分離式Invoker實例:MBeanServer Invoker適配器服務"一節有這方面的實例介紹.該實例展示了這樣一種情況,即如果存在代理工廠服務,則允許客戶應用基於任意協議而使用 RMIAdaptor接口,從而實現對MBeanServer的訪問.
譯者注:論文全文請參考http://matrix.research.att.com/vj/bug.html.
JBoss管理與開發

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章