Tomcat源码阅读之JMX部分的设计与实现
来源:互联网 发布:可编程控制器软件 编辑:程序博客网 时间:2024/04/28 04:16
在继续分析Tomcat的源码之前,先来分析一下Tomcat对JMX的支持,以及在Tomcat中的一些代码实现细节。。。
这里来看看Tomcat都通过JMX托管了那些对象吧:
嗯,从图上就可以看出,托管的东西还挺多的。。。其实感觉通过JMX这种方式还挺高端的,起码可以实现远程对Tomcat服务器的管理和配置。。。还挺方便的。。。
好啦,接下来就来看看Tomcat中是如何通过JMX来托管自己的对象的吧。。。
先来看一张比较重要的图:
这里是Tomcat中JMX部分比较重要的几个基本类型吧,首先是BaseModelMBean类型:
首先它实现了DynamicMBean接口,嗯,这个接口是啥呢?如果对JMX有稍微多一些了解的话,就会知道了,实现了这个接口,代表这个类型的对象可以被注册到MBServer上面去,这与前面的自己的一一篇文章里面介绍的注册到MBServer上面的对象少有不懂,Dynamic意味着这个对象是动态的,JMX通过调用一些方法可以动态的来获取这个对象的信息,它的属性,它暴露的方法什么的,以及进行动态的调用。。。这里具体的内容就不详细说了,去稍微了解一下JMX的部分就很容易明白了。。
另外它还是先了ModelMBeanNotificationBroadcaster,代表当前对象可以作为NotificationListener的容器,可以在这个对象里面添加listener,当当前托管的对象的属性发生改变了之后会通知这些注册的listener进行一些处理,例如写log啥的。。。
好啦,介绍完了BaseModelMBean类型,接下来来看看ManagedBean类型吧,它是干嘛的呢,刚开始这名字把我给糊弄了。。。。在介绍它之前先来看一段XML配置文件:
<mbeans-descriptors><mbean name="StandardServer" description="Standard Server Component" domain="Catalina" group="Server" type="org.apache.catalina.core.StandardServer"><attribute name="address" description="The address on which we wait for shutdown commands." type="java.lang.String"/><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="serverInfo" description="Tomcat server release identifier" type="java.lang.String" writeable="false"/><attribute name="serviceNames" description="Object names of all services we know about" type="[Ljavax.management.ObjectName;" writeable="false"/><attribute name="shutdown" description="Shutdown password" type="java.lang.String"/><attribute name="stateName" description="The name of the LifecycleState that this component is currently in" type="java.lang.String" writeable="false"/><operation name="await" description="Wait for the shutdown message" impact="ACTION" returnType="void"/><operation name="storeConfig" description="Save current state to server.xml file" impact="ACTION" returnType="void"></operation></mbean></mbeans-descriptors>
这个是在Tomcat代码中,core包里面的mbeans-descriptors.xml里面的内容,看内容就知道这个是对StanderServer对象的描述,例如属性,方法啥的。。。
那么这里就可以开始说ManagedBean是干啥的了,这里,一个<mbean></mbean>元素就会对应生成一个ManagedBean对象,用于保存和记录XML关于对象的描述。。。
好啦,到这里如果有一定基础的话就已经知道ManagedBean与BaseModelMBean之间的关系了,以及是如何运行的了。。
上图中还有一个MBean对象。。。那么它是干啥的呢。。?就拿StanderServer来举例子吧,
(1)首先Tomcat会通过读取刚刚说的xml文件,这些文件分散在Tomcat的许多源码包中
(2)通过里面元素的声明,创建相应的ManagedBean对象,用于维护这些信息。。
(3)当创建了一个StanderServer对象之后,会先找到它对应的ManagedBean对象,然后用这两个对象来生成相应的BaseModelMBean对象,然后将这个对象注册到MBServer上面去。。。
这样也就动态的将StanderServer通过XML文件的描述注册到了JMX上去,生成的BaseModelMBean对象通过调用StanderServer的ManagedBean对象,将需要的数据暴露给JMX就可以了。。。。
如下图:
可以看到这里暴露出来的属性以及方法与XML文件里面描述的是相同的。。。
这样一来,就算是基本上搞清楚了Tomcat如何通过xml文件来动态的生成对一些对象在JMX的动态注册。。
好了,有了上面的内容,接下来再来看看Tomcat中如何具体使用这些类型,这里就有两个重要的类型要介绍了:
首先是MBeanUtils类型,它是一个工具类,主要是进行一些初始化。。。。
我们来看他的两个重要的类属性:
/** * The configuration information registry for our managed beans. */ private static Registry registry = createRegistry(); //创建registry对象 /** * The <code>MBeanServer</code> for this application. */ private static MBeanServer mserver = createServer(); //创建mbeanserver
这里首先创建了Registry对象,接着创建了MBeanServer对象,先来看看createRegistry方法吧:
public static synchronized Registry createRegistry() { if (registry == null) { registry = Registry.getRegistry(null, null); //这里会创建Registry对象 ClassLoader cl = MBeanUtils.class.getClassLoader(); //用当前的应用程序classLoader来家在这些包里面的mbeans xml,并且用Registry对象来加载这些文件里面定义的mbean,生成ManagedBean //这里managedbean的作用是保存管理那些mbean的描述信息,待会用这些信息来构造属于这个mbean的DynamicMBean,然后保存在rregistry对象里面 registry.loadDescriptors("org.apache.catalina.mbeans", cl); registry.loadDescriptors("org.apache.catalina.authenticator", cl); registry.loadDescriptors("org.apache.catalina.core", cl); registry.loadDescriptors("org.apache.catalina", cl); registry.loadDescriptors("org.apache.catalina.deploy", cl); registry.loadDescriptors("org.apache.catalina.loader", cl); registry.loadDescriptors("org.apache.catalina.realm", cl); registry.loadDescriptors("org.apache.catalina.session", cl); registry.loadDescriptors("org.apache.catalina.startup", cl); registry.loadDescriptors("org.apache.catalina.users", cl); registry.loadDescriptors("org.apache.catalina.ha", cl); registry.loadDescriptors("org.apache.catalina.connector", cl); registry.loadDescriptors("org.apache.catalina.valves", cl); registry.loadDescriptors("org.apache.catalina.storeconfig", cl); registry.loadDescriptors("org.apache.tomcat.util.descriptor.web", cl); } return (registry); }
这里其实做的事情首先由创建Registry对象,通过Registry类型的静态方法来创建的。。。接着做的事情其实分别加载下面的包里面的mbeans-descriptors.xml文件,然后创建相应的ManagedBean对象。。。。
public static synchronized MBeanServer createServer() { if (mserver == null) { mserver = Registry.getRegistry(null, null).getMBeanServer(); //从registry对象中获取mbserver对象 } return (mserver); }
这里创建mbserver其实也是调用的Registry的静态方法来做的。。。
好了。。对于MBeanUtils类型,就先介绍到这里吧。。。
接下来来看看Registry的定义:
private MBeanServer server = null; //用于注册的mbserver /** * The set of ManagedBean instances for the beans this registry * knows about, keyed by name. */ private HashMap<String,ManagedBean> descriptors = new HashMap<>(); //key是定义的mbean的名字,value是生成的ManagedBean对象,他保存的有mbean的具体信息 /** List of managed beans, keyed by class name */ private HashMap<String,ManagedBean> descriptorsByClass = new HashMap<>(); //其实key是className,value是为其生成的ManagedBean(mbean里面定义的type) // map to avoid duplicated searching or loading descriptors private HashMap<String,URL> searchedPaths = new HashMap<>(); //用于索引包里面的mbean xml ,key是包的名字
首先是比较重要的属性的定义,这里具体他们是干嘛用的后面的注释应该说的还蛮清楚的。。。
//用于返回用到的Registry对象 public static synchronized Registry getRegistry(Object key, Object guard) { Registry localRegistry; if( perLoaderRegistries!=null ) { if( key==null ) key=Thread.currentThread().getContextClassLoader(); if( key != null ) { localRegistry = perLoaderRegistries.get(key); if( localRegistry == null ) { localRegistry=new Registry();// localRegistry.key=key; localRegistry.guard=guard; perLoaderRegistries.put( key, localRegistry ); return localRegistry; } if( localRegistry.guard != null && localRegistry.guard != guard ) { return null; // XXX Should I throw a permission ex ? } return localRegistry; } } // static if (registry == null) { registry = new Registry(); } if( registry.guard != null && registry.guard != guard ) { return null; } return (registry); }
getRegistry方法,可以看到这里其实用了一个单例的Registry对象,
接下来我们来看一下最重要的Registry的registerComponent方法吧,它用于在mbserver上面注册对象:
//注册一个组件 public void registerComponent(Object bean, ObjectName oname, String type) throws Exception { if( log.isDebugEnabled() ) { log.debug( "Managed= "+ oname); } if( bean ==null ) { log.error("Null component " + oname ); return; } try { if( type==null ) { //如果没有名字,那么获取类型的名字 type=bean.getClass().getName(); } ManagedBean managed = findManagedBean(null, bean.getClass(), type); //获取这个对象所用的ManagedBean // The real mbean is created and registered DynamicMBean mbean = managed.createMBean(bean); //通过ManagedBean创建一个DynamicMBean if( getMBeanServer().isRegistered( oname )) { if( log.isDebugEnabled()) { log.debug("Unregistering existing component " + oname ); } getMBeanServer().unregisterMBean( oname ); //如果这个已经注册了,那么取消这个名字的注册 } getMBeanServer().registerMBean( mbean, oname); //将刚刚创建的ManagedBean注册起来 } catch( Exception ex) { log.error("Error registering " + oname, ex ); throw ex; } }
其实这里跟上面分析的内容是一直的,首先获取要注册对象的ManagedBean对象,然后通过它来创建一个DynamicMBean对象,这里一般都是BaseModelMBean类型的对象(有的时候可以通过在xml文件中指定,为对象创建其他的类型),然后在将刚刚创建的DynamicMBean对象注册到mbserver上面去。。。。
好啦,总的来说Tomcat这部分做的还挺好的,通过XML文件描述来动态的在mbserver上面注册对象。。。
而且可以很方便的使用。。如下代码,利用tomcat的代码来动态的注册自己定义的对象:
package registTest;import javax.management.Notification;import javax.management.NotificationListener;import javax.management.ObjectName;import org.apache.tomcat.util.modeler.BaseModelMBean;import org.apache.tomcat.util.modeler.ManagedBean;import org.apache.tomcat.util.modeler.Registry;public class Fjs {private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}public void hello() {System.out.println("hello : " + name);}public static void main(String args[]) throws Exception {Registry registry = new Registry();registry.loadDescriptors("registTest", Fjs.class.getClassLoader());ObjectName name = new ObjectName("fjs:type=hello");Fjs fjs = new Fjs();ManagedBean manager = registry.findManagedBean(Fjs.class.getName());BaseModelMBean dmb = (BaseModelMBean)manager.createMBean(fjs);dmb.addAttributeChangeNotificationListener(new NotificationListener(){@Overridepublic void handleNotification(Notification notification,Object handback) {// TODO Auto-generated method stubSystem.out.println(notification);}}, null, null);registry.getMBeanServer().registerMBean(dmb, name);Thread.currentThread().sleep(Long.MAX_VALUE);}}
嗯,Fjs类型的描述文件如下:
<mbeans-descriptors><mbean name="Fjs" description="Standard Server Component" domain="Catalina" group="Server" type="registTest.Fjs"><attribute name="name" description="名字" type="java.lang.String"/><operation name="hello" description="Wait for the shutdown message" impact="ACTION" returnType="void"/></mbean></mbeans-descriptors>
简单吧。。没做多少工作就实现了通过xml文件描述动态的注册对象到mbserver上面去。。。。
- Tomcat源码阅读之JMX部分的设计与实现
- Tomcat源码阅读之Connector设计与实现
- Tomcat源码阅读之Container与Pipeline的设计
- Tomcat源码阅读之StandardEngine分析与Valve的设计
- 《Redis设计与实现》[第二部分]单机数据库的实现-C源码阅读(一)
- 《Redis设计与实现》[第二部分]单机数据库的实现-C源码阅读(二)
- 《Redis设计与实现》[第二部分]单机数据库的实现-C源码阅读(三)
- 《Redis设计与实现》[第二部分]单机数据库的实现-C源码阅读(四)
- Tomcat源码阅读之Bootstrap启动流程与classLoader设计
- Tomcat源码阅读之闭锁的实现与连接数量的控制
- Tomcat源码分析之ClassLoader部分的设计详细分析
- Tomcat源码分析之ClassLoader部分的设计详细分析
- 《Redis设计与实现》[第一部分]数据结构与对象-C源码阅读(一)
- 《Redis设计与实现》[第一部分]数据结构与对象-C源码阅读(二)
- Tomcat源码阅读之StandardHost与HostConfig的分析
- Tomcat源码分析之Server与Lifecycle的设计与实现
- Tomcat源码阅读之StandardService与MapperListener分析
- Tomcat源码阅读之过滤器
- Android多媒体MediaStore运用
- sqlite 如何限制某个表中的数据总数
- Java 理论与实践: 用弱引用堵住内存泄漏
- 分析入侵检测系统漏洞的黑客手法
- 进制转换
- Tomcat源码阅读之JMX部分的设计与实现
- 揭秘杀毒软件的救星:云安全解决方案
- PHP系列学习之AOP
- PHPCurl的毫秒超时的一个”Bug”
- 用抓包的方法解决ARP病毒欺骗攻击
- 如何配置JBOSS
- 拒绝恶意程序 WinRAR使用中的三种安全战术
- android中关于弱引用,This Handler class should be static or leaks might occur
- HTML5 canvas中的3D地表