《深入剖析Tomcat 》第20章 基于JMX的管理
浏览(2309)|评论(1) 交流分类:Java|笔记分类: tomcat
19章中讨论了manager应用,它展示了如何使用实现了ContainerServlet接口的ManagerServlet类来访问catalina的内部类。本章将说明如何使用jmx管理tomcat。本章会先简要介绍jmx,然后讨论Common Modeler库,tomcat使用该库对Managed Bean进行管理。
20.1 jmx简介
既然ContainerServlet已经可以管理tomcat,为啥要用jmx?因为jmx更灵活。许多基于服务器的应用程序,如tomcat,jboss,jonas,geromino等,使用jmx管理其资源。
jmx规范(书中指规范1.2.1版本)定义了管理java对象的公开标准。例如,tomcat4和5使用jmx来启动servlet容器中的各种对象(如server,host,context,valve等),这样更灵活,更易于管理。tomcat的开发者也编写了admin应用来管理其他web应用。
由基于jmx的管理应用程序管理的java对象成为jmx托管资源。事实上,一个jmx托管资源也可以是一个应用程序,一个service实现,一个设备,一个用户等等。jmx托管资源由java编写,提供了相应的包装。
要想将一个java对象转换为jmx托管资源,你需要另外创建一个名为MBean的对象。org.apache.catalina.mbeans包下,包含有一些MBean,例如,ConnectorMBean,StandardEngineMBean,StandardHostMBean,StandardContextMBean。由名字可猜想,ConnectorMBean用于管理connector,StandardContextMBean用于管理org.apache.catalina.core.StandardContext实例。你也可以编写自己的MBean管理你自己的java对象。
MBean类暴露出了它所管理的java对象的属性和方法。管理应用程序本身并不直接访问它所管理的java对象,因此,你可以选择性的暴露出需要的属性和方法。
拥有的MBean类后,你将它实例化,并将其注册到MBean服务器中。MBean服务器是一个应用中所有MBean注册的中心地。管理应用程序通过MBean服务器访问MBean。若是将jmx和servlet开发相比较的话,管理应用程序则等同于web浏览器,MBean服务器等同于servlet容器,它提供了托管资源的访问。MBean则等同于servlet/jsp。web浏览器并不直接访问servlet/jsp,而是通过servlet容器来访问,管理应用程序也是通过MBean服务器访问MBean的。
有四种类型MBean:standard,dynamic,open,model。standard Mbean是四种类型中最容易编写的,但灵活性最差。这里重点讨论tomcat所使用的model MBean。standard MBean会用来写一个小例子来展示如何使用jmx。
从结构上,jmx分为三个层次,表现层,代理层,分布式服务层。MBean服务器位于代理层,MBean位于表现层。而分布式服务层将在以后的jmx的版本中涉及。
jmx规范的表现层定义了编写jmx托管资源的标准方法,例如定义了如何编写一个MBean。代理层定义了创建代理的规范。jmx的代理包括一个MBean服务器,用于处理MBean服务。一般情况下,代理和MBean是位于同一个jvm中的。jmx规范中有一个参考实现,因此,你不必自己实现MBean服务器,参考实现给出了实现MBean服务器的方法。
jmx的参考实现可以从这里下载:
http://www.oracle.com/technetwork/java/javase/tech/javamanagement-140525.html
20.2 jmx api
jmx的参考实现中包含了一个核心java库,位于javax.management包和其他一些包下(用于jmx的其他方方面)。
20.2.1 MBeanServer
javax.management.MBeanServer接口表示一个MBean服务器。要创建一个MBean服务器,只需要调用javax.management.MBeanServerFactory类的createMBean即可。要将一个MBean注册到MBean服务器中,可以调用MBeanServer的registerMBean方法。下面是registerMBean方法的签名:
- public ObjectInstance registerMBean(java.lang.Object object,
- ObjectName name) throws InstanceAlreadyExistsException,
- MBeanRegistrationException, NotCompliantMBeanException
要注册一个MBean,需要传入一个MBean对象,和一个ObjectName对象。一个ObjectName实例类似于HashMap中的key,可以唯一标识一个MBean。registerMBean方法返回一个ObjectInstance实例。javax.management.ObjectInstance类包装了一个MBean和它的类名。
MBeanServer接口提供了两个方法queryNames和queryMBeans用于获取某个MBean或符合某个匹配模式的一组MBean。queryNames方法返回java.util.Set对象,包含了匹配某个模式的一组MBean的名字。方法签名为:
- public java.util.Set queryNames(ObjectName name, QueryExp query)
参数query执行过滤表达式。若参数name为null,或者没有域,而且指定了key属性,则会返回所有已经注册的MBean的ObjectName。若query为null,则不会进行过滤。
queryMBeans方法与queryNames方法类似,它返回的也是java.util.Set,包含了针对所要查找的MBean的ObjectInstance。方法签名为:
- public java.util.Set queryMBeans(ObjectName name, QueryExp query)
如果你想要获得MBean的对象名称,你可以操作托管资源的属性或者调用MBean中暴露出的方法。通过调用MBeanServer的invoke方法,你可以调用已注册的MBean所暴露出的任何方法。MBeanServer的getAttribute和setAttribute方法用于对已注册的MBean的属性进行操作。
20.2.2 ObjectName
MBean注册于MBeanServer,类似于HashMap中的key,每个MBean通过一个对象名称进行唯一标识。对象名称由javax.management.ObjectName类表示。一个ObjectName包含两部分,一个域(domain)和一个key/value集合。domain是一个字符串,可以为空。在一个对象名称中,domain后面跟着一个冒号和一个或多个key/value对。key是一个非空字符串,且不能包含下列字符:等号,逗号,冒号,问号或星号。一个对象名称中key是唯一的。 key与value通过等号连接,key/value对之间由逗号分隔。例如:
- myDomain:type=Car,color=blue
在MBeanServer中查找MBean时,ObjectName实例也可以表示属性模式。作为匹配模式的ObjectName可以在其domain和key/value中使用通配符。一个作为匹配模式的ObjectName可以有0个或多个key。
20.3 Standard MBeans
standard MBean是最简单的MBean,要想通过standard MBean管理java对象,需要完成一些步骤。这里假设你要管理的java对象的类名为Car。步骤如下:
(1)创建一个接口,该接口名规范为:你的java对象类名+MBean。例如,你想要管理的java对象的类名为Car,则需要创建的接口名为CarMBean;
(2)修改你的java类,让其实现CarMBean接口;
(3)创建一个代理,该代理中必须包含有MBeanServer;
(4)为你的MBean创建ObjectName实例;
(5)实例化MBeanServer;
(6)将MBean注册到MBeanServer中。
standard MBean很容易编写,使用MBean需要修改你的java类(如需要实现创建的MBen接口),在某些工程里,这是不可行的。MBean的其他类型则不需要这样的限制。
下面代码完成Car的MBean的创建:
你需要在接口中声明,你需要暴露出哪些需要操作的属性和方法。在本例中,CarMBean接口暴露出了Car的所有方法。接下来,需要定义一个代理,StandardAgent,用来创建MBean,管理Car对象。实现代码如下:
StandardAgent类是代理层,负责实例化MBeanServer,使用MBeanServer对象注册MBean:
- public StandardAgent() {
- mBeanServer = MBeanServerFactory.createMBeanServer();
- }
createMBeanServer方法返回一个jmx参考实现的默认的MBeanServer对象。你有兴趣的话,可以自己实现一个MBeanServer。
createObjectName方法返回一个ObjectName对象。createStandardMBean方法调用MBeanServer类的createMBean方法,createMBean方法接收一个托管资源的类名和一个ObjectName实例来创建托管资源的MBean对象。createMBean方法还会将创建的MBean注册到MBeanServer中。由于standard MBean的遵循了固定的命名约定,因此在createMBean方法中你不需要提供MBean的类型。
StandardAgent的main方法会先创建一个StandardAgent对象,调用getMBeanServer方法获取一个MBeanServer对象引用:
- StandardAgent agent = new StandardAgent();
- MBeanServer mBeanServer = agent.getMBeanServer();
然后为CarMBean创建ObejctName,MBeanServer的默认domain被用于ObejctName的domain,ObejctName的key/value使用了type作为key,托管资源的全类名作为value:
- String domain = mBeanServer.getDefaultDomain();
- String managedResourceClassName = "ex20.pyrmont.standardmbeantest.Car";
- ObjectName objectName = agent.createObjectName(domain + ":type=" + managedResourceClassName);
然后,main方法调用createStandardBean方法,传入ObjectName对象和托管资源的类名:
- agent.createStandardBean(objectName, managedResourceClassName);
接下来,main方法通过CarMBean来管理Car对象。它首先创建了一个Attribute对象colorAttribute来表示Car的color属性,并设置值为blue。然后,它调用setAttribute方法,传入objectName和colorAttribute。最后又通过MBeanServer的invoke方法调用了Car的drive方法。
- // manage MBean
- try {
- Attribute colorAttribute = new Attribute("Color","blue");
- mBeanServer.setAttribute(objectName, colorAttribute);
- System.out.println(mBeanServer.getAttribute(objectName, "Color"));
- mBeanServer.invoke(objectName,"drive",null,null);
- }
20.4 Model MBeans
相比于standard MBean,model MBean更具灵活性。在编码上,model MBean难度更大一下,但你也不再需要修改要管理的java类了(在standard MBean中,你必须修改要管理的java类,使其实现MBean接口)。
使用model MBean,你不在需要创建MBean接口,相反,javax.management.modelmbean.ModelMBean接口就是用了表示一个model MBean,你只需要实现这个接口。jmx也提供了对这个接口的默认实现,javax.management.modelmbean.RequiredModelMBean。你只需要实例化这个类或其子类。
注意,使用ModelMBean接口的其他实现也是可以的。例如,在下面章节讨论的Commons Modeler库就提供了自定义的实现。
使用model MBean的难点在于告诉ModelMBean托管资源的哪些属性和方法可以暴露给代理层。这里,你需要创建一个javax.management.modelmbean.ModelMBeanInfo对象。ModelMBeanInfo对象描述了要暴露给代理层的构造器,属性,操作和监听器。创建ModelMBeanInfo对象的工作单调乏味,但是一旦拥有,别无所求,只要将其与ModelMBean相关联即可。
使用RequiredModelMBean作为ModelMBean的实现,有两个方法将ModelMBean和ModelMBeanInfo相关联:
(1)在RequiredModelMBean的构造函数中传入ModelMBeanInfo对象;
(2)调用RequiredModelMBean对象的setModelMBeanInfo方法,传入ModelMBeanInfo对象。
在创建了ModelMBean之后,必须要调用其setManagedResource方法与托管资源相关联,该方法的签名如下:
- public void setManagedResource(java.lang.Object managedResource,
- java.lang.String managedResourceType) throws MBeanException,
- RuntimeOperationsException, InstanceNotFoundException,
- InvalidTargetObjectTypeException
参数managedResourceType的值可以是下面的值之一,ObjectReference,Handle,IOR,EJBHandle或RMIReference。当前只支持ObjectReference。
当然,你还需要创建ObjectName,并将MBean注册到MBeanServer中。
20.4.1 MBeanInfo与ModelMBeanInfo
- javax.management.mbean.ModelMBeanInfo接口描述了要通过ModelMBean暴露给代理层的构造器,属性,操作和监听器。构造器由javax.management.modelmbean.ModelMBeanConstructorInfo表示。属性由javax.management.modelmbean.ModelMBeanAttributeInfo表示。操作由javax.management.modelmbean.ModelMBeanOperationInfo表示,监听器由javax.management.modelmbean.ModelMBeanNotificationInfo表示。本章只讨论属性和操作。
- jmx提供了javax.management.modelmbean.ModelMBeanInfoSupport的默认实现ModelMBeanInfo。下面是ModelMBeanInfoSupport的构造器:
- public ModelMBeanInfoSupport(java.lang.String className,
- java.lang.String description, ModelMBeanAttributeInfo[] attributes,
- ModelMBeanConstructorInfo[] constructors,
- ModelMBeanOperationInfo[] operations,
- ModelMBeanNotificationInfo[] notifications)
ModelMBeanAttributeInfo的构造器如下:
- public ModelMBeanAttributeInfo(java.lang.String name,
- java.lang.String type, java.lang.String description,
- boolean isReadable, boolean isWritable,
- boolean isIs, Descriptor descriptor)
- throws RuntimeOperationsException
下面是参数列表说明:
l name,属性名;
l type,属性的类型;
l description,属性描述说明;
l isReadable,若属性有getter方法,则为true,否则为false;
l isWritable,参考isReadable;
l isIs,参考isReadable;
l descriptor,Descriptor对象,包含了Attribute对象的元数据,若置为null,则会创建默认的descriptor。
ModelMBeanOperationInfo的构造器如下:
- public ModelMBeanOperationInfo(java.lang.String name,
- java.lang.String description, MBeanParameterInfo[] signature,
- java.lang.String type, int impact, Descriptor)
- throws RuntimeOperationsException
下面是参数列表说明:
l name,方法名;
l description,方法描述;
l signature,MBeanParameterInfo数组,保存方法的参数;
l type,方法返回值的类型;
l impact,方法的影响(大概是这个意思),可选值如下,INFO,ACTION,ACTION_INFO,UNKNOWN;
l descriptor,Descriptor对象,包含了MBeanOperationInfo对象的元数据,若置为null,则会创建默认的descriptor。
20.4.2 ModelMBean实例
使用model MBean管理Car对象,你不需要再创建MBean接口,只需要实例化RequiredMBean类。下面的代码提供了代理层ModelAgent,用于创建MBean来管理Car对象。ModelAgent类实现代码如下:
如代码所示,使用model MBean,你需要多做一些工作,尤其是要声明所有要暴露出的属性,方法。但灵活性更好。下一节将介绍Commons Modeler库,该库可以帮助你更快的编写model MBean。
20.5 Commons Modeler
Commons Modeler库是apache Jakarta项目的一部分,目的是使编写model MBeans更加方便。最打的帮助是你不需要在写代码创建ModelMBeanInfo对象了。
回忆前面的章节,创建RequiredModelMBean对象时,你需要传入RequiredModelMBean的构造函数,创建一个ModelMBeanInfo对象:
- ModelMBeanInfo mBeanInfo = createModelMBeanInfo(objectName, mbeanName);
- RequiredModelMBean modelMBean = null;
- try {
- modelMBean = new RequiredModelMBean(mBeanInfo);
- }
ModelMBeanInfo描述了要暴露出的属性和方法,实现createModelMBeanInfo方法是枯燥的,因为你不得不列出所有要暴露的属性和方法并将它们传给ModelMBeanInfo对象。
而使用Commons Modeler库,你就不再需要创建ModelMBeanInfo对象,对MBean的描述被封装在org.apache.catalina.modeler.ManagedBean对象中。你只需要编写一个简单你的描述文件(xml格式),列出你想要创建的MBean。对每个MBean,你要列出他的全限定名,包括MBean的名字和托管资源的类名,还要写出要暴露出的属性和方法的名称。然后,使用org.apache.commons.modeler.Registry对象读取该xml文件,依照配置创建MBeanServer和所有的ManagedBean。
然后,你就可以调用ManagedBean的createMBean方法创建MBean了。之后的事一切照旧。你需要创建一个ObejctName对象,将MBean注册到MBeanServer中。
下面,要说明下MBean的描述文件,然后讨论三个比较重要的类Registry,ManagedBean和BaseModelMBean。
注意,tomcat4中仍然使用了老版本的Modeler,使用一些deprecated的方法。
20.5.1 MBean描述符
MBean的描述符是是一个xml文件,该文件对有MBeanServer管理的model MBean进行描述。MBean描述符以下面的代码开始:
- <?xml version="1.0"?>
- <!DOCTYPE mbeans-descriptors PUBLIC
- "-//Apache Software Foundation//DTD Model MBeans Configuration File"
- "http://jakarta.apache.org/commons/dtds/mbeans-descriptors.dtd">
接下来是根元素:
- <mbeans-descriptors>
- ...
- </mbeans-descriptors>
mbeans-descriptors标签中间部分mbean标签,每个mbean标签表示一个model MBean,mbean元素下的元素表示属性,方法,构造器,监听器。
20.5.1.1 mbean
mbean元素描述了一个model MBean,包含了创建ModelMBeanInfo对象的信息。mbean元素有如下的定义:
- <!ELEMENT mbean (descriptor?, attribute*, constructor*, notification*, operation*)>
mbean元素定义了具体的规范,mbean标签下可以有一个可选的descriptor标签,0个或多个attribute标签,constructor标签,notification标签,operation标签。
mbean标签可以有如下的属性:
l className,ModelMBean的实现类的全名,若该属性未赋值,则默认使用org.apache.commons.modeler.BaseModelMBean类;
l description,该model MBean的简单描述;
l domain,MBeanServer的domain值,由MBeanServer创建的ModelMBean会属于这个domain;
l group,即“grouping classification”,用来选择MBean相似实现的的组;
l name,model MBean的唯一标识;
l type,托管资源实现类的全名。
20.5.1.2 attribute
使用attribute标签描述MBean的属性。attribute标签有一个可选的descriptor元素,descriptor元素有如下可选属性:
l description,该属性的简单描述;
l displayName,该属性的显示名称;
l getMethod,该属性的getter方法;
l is,一个bool值,指明该bool值是否有getter方法,默认为false;
l name,属性名;
l readable,一个bool值,指明该属性是否可读,默认为true;
l setMethod,该属性的setter方法;
l type,该属性的Java类的全名;
l writeable,一个bool值,表明该属性是否有setter方法,默认为true。
20.5.1.3 operation
operation标签描述了mdel MBean中要暴露给管理应用程序的公开方法,可以有0个或多个parameter元素和如下的属性:
l description,方法的简单描述;
l impact,指明方法的影响力,可选值为,ACTION (write like),ACTION-INFO (write+read like),INFO(read like)或UNKNOWN;
l name,方法名;
l returnType,方法返回值的java类的全名。
20.5.1.4 parameter
l parameter标签描述了传给构造函数或方法的参数。可以有如下属性:
l description,该参数的简单描述;
l name,参数名;
l type,参数的类型(java全类名)。
20.5.2 mbean标签实例
在catalina的mbean-descriptors.xml文件中声明了一系列model MBean,该文件位于org.apache.catalina.mbeans包下,下面的代码是对托管资源StandardServer进行包装的MBean的声明:
- <mbean name="StandardServer" className="org.apache.catalina.mbeans.StandardServerMBean"
- description="Standard Server Component" domain="Catalina" group="Server"
- type="org.apache.catalina.core.StandardServer">
- <attribute name="debug" description="The debugging detail level for this component" type="int"/>
- <attribute name="managedResource" description="The managed resource this MBean is associated with"
- type="java.lang.Object"/>
- <attribute name="port" description="TCP port for shutdown messages" type="int"/>
- <attribute name="shutdown" description="Shutdown password" type="java.lang.String"/>
- <operation name="store" description="Save current state to server.xml file" impact="ACTION"
- returnType="void">
- </operation>
- </mbean>
mbean元素声明了一个model MBean,其唯一标识是StandardServer。该MBean是一个org.apache.catalina.mbeans.StandardServerMBean的对象,负责管理org.apache.catalina.core.StandardServer对象。domain是Catalina,group是Server。
20.5.3 自己编写一个model MBean
使用Commons Modeler库,需要在mbean元素的className属性中指明自定义的model MBean的全类名。默认情况下,Commons Modeler使用org.apache.commons.modeler.BaseModelMBean类。有以下两种情况,你可能需要对其进行扩展:
l 你需要覆盖托管资源的属性;
l 你需要为托管资源添加属性或方法。
20.5.4 注册
org.apache.commons.modeler.Registry类中有一些方法用于注册MBean,下面是你可以使用该类做的事情:
l 获取一个javax.management.MBeanServer对象(你不在需要调用javax.management.MBeanServerFactory的createMBeanServer方法);
l 使用loadRegistry方法读取描述符文件;
l 创建一个ManagedBean对象,用于创建model MBean。
20.5.5 ManagedBean
ManagedBean对象描述了一个model MBean,该类用于取代javax.management.MBeanInfo类。
20.5.6 BaseModelMBean
org.apache.commons.modeler.BaseModelMBean实现了javax.management.modelmbean.ModelMBean接口,使用这个类,就不需要用javax.management.modelmbean.RequiredModelMBean类了。
该类用一个比较有用的属性是resource属性。resource表明了该model MBean管理的托管资源对象,resource声明如下:
- protected java.lang.Object resource;
20.5.7 使用Modeler API
有一个类Car是要管理的, ,现在使用Commons Modeler库,你不再需要在代码中纠结于Car类中众多的属性和方法。你需要将属性和方法写到xml文件中,例如下面的文件:
然后你需要编写一个代理类ModelAgent,代码如下:
20.6 Catalian中的MBean
正如本章开头所说,Catalina的org.apache.catalina.mbeans包中提供了一系列MBean,这些MBean都直接或间接的继承自org.apache.commons.modeler.BaseModelMBean。本节会讨论tomcat4中几个比较重要的MBean,ClassNameMBean,StandardServerMBean和MBeanFactory。
20.6.1 ClassNameMBean
org.apache.catalina.mbeans.ClassNameMBean类继承自org.apache.commons.modeler.BaseModelMBean。它提供了一个只写的属性className,用于表示托管资源的类名。该类定义如下:
- package org.apache.catalina.mbeans;
- import javax.management.MBeanException;
- import javax.management.RuntimeOperationsException;
- import org.apache.commons.modeler.BaseModelMBean;
- public class ClassNameMBean extends BaseModelMBean {
- public ClassNameMBean() throws MBeanException, RuntimeOperationsException {
- super();
- }
- public String getClassName() {
- return (this.resource.getClass().getName());
- }
- }
ClassNameMBean类是BaseModelMBean的子类,其只写属性className在托管资源中不可见。mbeans-descriptors.xml文件中的很多mbean元素使用该类作为其model MBean。
20.6.2 StandardServerMBean
StandardServerMBean类继承自org.apache.commons.modeler.BaseModelMBean类,用于管理org.apache.catalina.core.StandardServer。StandardServerMBean类覆盖了托管资源的一些方法,例如store方法。当管理应用程序调用store方法时,实际上会执行StandardServerMBean的store方法,而不是StandardServer的store方法。
- package org.apache.catalina.mbeans;
- import javax.management.InstanceNotFoundException;
- import javax.management.MBeanException;
- import javax.management.MBeanServer;
- import javax.management.RuntimeOperationsException;
- import org.apache.catalina.Server;
- import org.apache.catalina.ServerFactory;
- import org.apache.catalina.core.StandardServer;
- import org.apache.commons.modeler.BaseModelMBean;
- public class StandardServerMBean extends BaseModelMBean {
- private static MBeanServer mserver = MBeanUtils.createServer();
- public StandardServerMBean() throws MBeanException, RuntimeOperationsException {
- super();
- }
- public synchronized void stored throws InstanceNotFoundException, MBeanException,
- RuntimeOperationsException {
- Server server = ServerFactory.getServer();
- if (server instanceof StandardServer) {
- try {
- ((StandardServer) server).store();
- } catch (Exception e) {
- throw new MBeanException(e, "Error updating conf/server.xml");
- }
- }
- }
- }
20.6.3 MBeanFactory
MBeanFactory类表示一个工程对象,用于创建管理tomcat资源的model Mbean。MBeanFactory类还提供了删除MBean的方法。createStandardContext方法用于创建托管资源StandardContext的model MBean:
- public String createStandardContext(String parent, String path, String docBase) throws Exception {
- // Create a new StandardContext instance
- StandardContext context = new StandardContext();
- path = getPathStr(path);
- context.setPath(path);
- context.setDocBase(docBase);
- ContextConfig contextConfig = new ContextConfig();
- context.addLifecycleListener(contextConfig);
- // Add the new instance to its parent component
- ObjectName pname = new ObjectName(parent);
- Server server = ServerFactory.getServer();
- Service service = server.findService(pname.getKeyProperty("service"));
- Engine engine = (Engine) service.getContainer();
- Host host = (Host) engine.findChild(pname.getKeyProperty("host"));
- // Add context to the host
- host.addChild(context);
- // Return the corresponding MBean name
- ManagedBean managed = registry.findManagedBean("StandardContext");
- ObjectName oname = MBeanUtils.createObjectName(managed.getDomain(), context);
- return (oname.toString());
- }
20.6.4 MBeanUtil
org.apache.catalina.mbeans.MBeanUtil类是一个工具类,提供了一些静态方法用于创建各种管理Cataliana对象的MBean,还有一些静态方法用于删除MBean,创建ObjectName等。例如,使用createMBean方法创建创建一个管理org.apache.catalina.Server的MBean。createMBean的实现方法如下:
- public static ModelMBean createMBean(Server server) throws Exception {
- String mname = createManagedName(server);
- ManagedBean managed = registry.findManagedBean(mname);
- if (managed == null) {
- Exception e = new Exception( "ManagedBean is not found with "+mname);
- throw new MBeanException(e);
- }
- String domain = managed.getDomain();
- if (domain == null)
- domain = mserver.getDefaultDomain();
- ModelMBean mbean = managed.createMBean(server);
- ObjectName oname = createObjectName(domain, server);
- mserver.registerMBean(mbean, oname);
- return (mbean);
- }
20.7 创建Catalian的MBean
前面的章节说明了catalina中的一些model MBean,现在来说明下这些mbean的创建。
在server.xml中定义了如下的监听器:
- <Server port="8005" shutdown="SHUTDOWN" debug="0">
- <Listener c1assName="org.apache.catalina.mbeans.ServerLifecycleListener" debug="0"/>
- ...
这里为StandardServer添加了一个监听器,org.apache.catalina.mbeans.ServerLifecycleListener。当StandardServer启动时,会触发START_EVENT事件:
- public void start() throws LifecycleException {
- ...
- lifecycle.fireLifecycleEvent(START_EVENT, null);
- ...
- }
而当StandardServer关闭时,会触发STOP_EVENT事件:
- public void stop() throws LifecycleException {
- ...
- lifecycle.fireLifecycleEvent(STOP_EVENT, null);
- ...
- }
这些事件会执行ServerLifecycleListener的lifecycleEvent方法,下面是lifecycleEvent方法的实现:
- public void lifecycleEvent(LifecycleEvent event) {
- Lifecycle lifecycle = event.getLifecycle();
- if (Lifecycle.START_EVENT.equals(event.getType())) {
- if (lifecycle instanceof Server) {
- // Loading additional MBean descriptors
- loadMBeanDescriptors();
- createMBeans();
- }
- } else if (Lifecycle.STOP_EVENT.equals(event.getType())) {
- if (lifecycle instanceof Server) {
- destroyMBeans();
- }
- } else if (Context.RELOAD_EVENT.equals(event.getType())) {
- if (lifecycle instanceof StandardContext) {
- StandardContext context = (StandardContext)lifecycle;
- if (context.getPrivileged()) {
- context.getServletContext().setAttribute (Globals.MBEAN_REGISTRY_ATTR,
- MBeanUtils.createRegistry());
- context.getServletContext().setAttribute (Globals.MBEAN_SERVER_ATTR,
- MBeanUtils.createServer());
- }
- }
- }
- }
其中,createMBeans方法用于创建MBean,该方法会先创建MBeanFactory实例,然后在创建相应的MBean。ServerLifecycleListener类中的createMBeans方法实现如下:
- protected void createMBeans() {
- try {
- MBeanFactory factory = new MBeanFactory();
- createMBeans(factory);
- createMBeans(serverFactory.getserver());
- } catch (MBeanException t) {
- Exception e = t.getTargetException();
- if (e == null)
- e = t;
- log("createMBeans: MBeanException", e);
- } catch (Throwable t) {
- log("createMBeans: Throwable", t);
- }
- }
第一个createMBeans方法会使用MBeanUtil类为MBeanFactory创建一个ObejctName,然后将其注册到MBeanServer中。第二个createMBeans方法获取一个org.apache.catalina.Server对象,为其创建model MBean。下面的代码展示例如如何为Server对象创建一个MBean:
- protected void createMBeans(Server server) throws Exception {
- // Create the MBean for the Server itself
- if (debug >= 2)
- log("Creating MBean for Server " + server);
- MBeanUtils.createMBean(server);
- if (server instanceof StandardServer) {
- ((StandardServer) server).addPropertyChangeListener(this);
- }
- // Create the MBeans for the global NamingResources (if any)
- NamingResources resources = server.getGlobalNamingResources();
- if (resources != null) {
- createMBeans(resources);
- }
- // Create the MBeans for each child Service
- Service services[] = server.findServices();
- for (int i = 0; i < services.length; i++) {
- // FIXME - Warp object hierarchy not currently supported
- if (services[i].getContainer().getClass().getName().equals
- ("org.apache.catalina.connector.warp.WarpEngine")) {
- if (debug >= 1) {
- log("Skipping MBean for Service " + services[i]);
- }
- continue;
- }
- createMBeans(services[i]);
- }
- }
注意,createMBeans方法调用下面的语句
- createMBeans(services[i]);
遍历所有的StandardServer对象中所有的Service。该方法为每个service对象创建MBean,然后,为每个service对象中所有的connector和engine对象调用createMBeans方法。
创建service mbean的createMBeans方法实现如下:
- protected void createMBeans(Service service) throws Exception {
- // Create the MBean for the Service itself
- if (debug >= 2)
- log("Creating MBean for Service " + service);
- MBeanUtils.createMBean(service);
- if (service instanceof StandardService) {
- ((StandardService) service).addPropertyChangeListener(this);
- }
- // Create the MBeans for the corresponding Connectors
- Connector connectors[] = service.findConnectors();
- for (int j = 0; j < connectors.length; j++) {
- createMBeans(connectors[j]);
- }
- // Create the MBean for the associated Engine and friends
- Engine engine = (Engine) service.getContainer();
- if (engine != null) {
- createMBeans(engine);
- }
- }
同样的,对每个engine对象,使用createMBeans方法为每个host创建mbean:
- protected void createMBeans(Engine engine) throws Exception {
- // Create the MBean for the Engine itself
- if (debug >= 2) {
- log("Creating MBean for Engine " + engine);
- }
- MBeanUtils.createMBean(engine);
- ...
- Container hosts[] = engine.findChildren();
- for (int j = 0; j < hosts.length; j++) {
- createMBeans((Host) hosts[j]);
- }
- ...
- }
接着为每个host,使用createMBeans方法,为每个context创建mbean:
- protected void createMBeans(Host host) throws Exception {
- ...
- MBeanUtils.createMBean(host);
- ...
- Container contexts[] = host.findChildren();
- for (int k = 0; k < contexts.length; k++) {
- createMBeans((Context) contexts[k]);
- }
- ...
- }
以此类推,createMBeans (context)方法实现如下:
- protected void createMBeans(Context context) throws Exception {
- ...
- MBeanUtils.createMBean(context);
- ...
- context.addContainerListener(this);
- if (context instanceof StandardContext) {
- ((StandardContext) context).addPropertyChangeListener(this);
- ((StandardContext) context).addLifecycleListener(this);
- }
- // If the context is privileged, give a reference to it
- // in a servlet context attribute
- if (context.getPrivileged()) {
- context.getServletContext().setAttribute
- (Globals.MBEAN_REGISTRY_ATTR, MBeanUtils.createRegistry());
- context.getServletContext().setAttribute
- (Globals.MBEAN_SERVER_ATTR, MBeanUtils.createServer());
- }
- ...
- }
如果context的privileged属性为true,则会为context添加两个属性,两个属性的key分别是Globals.MBEAN_REGISTRY_ATTR和Globals.MBEAN_SERVER_ATTR。下面是org.apache.catalina.Globals类的实现片段:
- /**
- * The servlet context attribute under which the managed bean Registry
- * will be stored for privileged contexts (if enabled).
- */
- public static final String MBEAN_REGISTRY_ATTR = "org.apache.catalina.Registry";
- /**
- * The servlet context attribute under which the MBeanServer will be
- * stored for privileged contexts (if enabled).
- */
- public static final String MBEAN_SERVER_ATTR = "org.apache.catalina.MBeanServer";
MBeanUtils.createRegistry方法返回一个Registry实例,MBeanUtils.createServer方法返回一个javax.management.MBeanServer实例,catalina的mbean就注册于此。换句话说,当privileged属性为true时,你就可以从一个web应用中获取Registry和MBeanServer对象。
20.8 应用程序
该应用程序用来管理tomcat,虽然简单,但说明了如何在tomcat中使用mbean。你可以列出catalina中所有的ObjectName对象,以及当前正在运行的context,并删除其中的任意一个。
首先要为web应用创建描述符,该描述文件需要放到%CATALINA_HOME%/webapps目录下:
- <Context path="/myadmin" docBase="../server/webapps/myadmin" debug="8"
- privileged="true" reloadable="true">
- </Context>
注意,这里privileged属性的值必须为true,docBase指定了web应用的具体路径。该web应用中包含了一个servlet,实现代码如下:
下面编写一个web.xml文件:
- <?xml version="1.0" encoding="ISO-8859-1"?>
- <!DOCTYPE web-app
- PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
- "http://java.sun.com/dtd/web-app_2_3.dtd">
- <web-app>
- <servlet>
- <servlet-name>myAdmin</servlet-name>
- <servlet-class>myadmin.MyAdminServlet</servlet-class>
- </servlet>
- <servlet-mapping>
- <servlet-name>myAdmin</servlet-name>
- <url-pattern>/myAdmin</url-pattern>
- </servlet-mapping>
- </web-app>
执行后,要想列出所有的ObjectName对象,可以使用如下的url:
http://localhost:8080/myadmin/myAdmin?action=listAllMBeans
列出所有的web应用可以使用如下的url:
http://localhost:8080/myadmin/myAdmin?action=listAllContexts
转载请注明出处【http://sishuok.com/forum/blogPost/list/4202.html】
源码下载地址:【http://sishuok.com/forum/blogPost/list/0/4061.html】
pdf下载地址:【http://sishuok.com/forum/blogPost/list/0/4061.html】
译者博客:曹旭东