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 Scott.Stark@jboss.org
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 Scott.Stark@jboss.org
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 Scott.Stark@jboss.org
* @version $Revision:$
*/
public class ExObj implements Serializable
{
public ExObj2 ivar = new ExObj2();
}
----------------------------------------------------------------------------
package org.jboss.chap2.ex0;
import java.io.Serializable;
/**
* @author Scott.Stark@jboss.org
* @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 Scott.Stark@jboss.org
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 Scott.Stark@jboss.org
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 Scott.Stark@jboss.org
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文件太大,请压缩后发到scott.stark@jboss.org信箱.
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 Scott.Stark@jboss.org
@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管理与开发

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值