Tomcat 有一个核心的配置文件,位于 TOMCAT_HOME/conf/server.xml。这个每个Tomcat的用户都了解,无论你是要去新增Connector,改端口号,配置虚拟主机,还是要进行自定义部署,都离不开它。
在这个核心文件中,包含对整个 Web 容器的配置,在容器启动时通过对配置的解析,生成各个层级的容器,并最终协同完成对请求的响应。
配置的解析我们前面文章介绍过, Tomcat 内部是通过 Digester 来实现的(Tomcat配置文件解析与Digester)。但是这里有几个问题:
首先,目前 Tomcat默认提供的Manager应用,除了运行时对应用进行生命周期周期的管理外,只有部署应用,内存诊断等少数几个功能。当然 PSI-probe 比 Manager的功能更丰富(一款功能强大的Tomcat 管理监控工具),But 依然没有提供运行时对容器内组件进行操作的功能,其实这些真的「可以有」。
比如像我之前给PSI 发的Pull Request(怎样参与到全世界优秀的开源项目中?),增加的功能就是对 Tomcat 内部 Connector 进行启动,停止的操作,同时将 Connector 的状态在列表中显示出来。
而这些可以有,但是没有的功能,在 J2EE应用服务器 里,相当于已经做为默认的 Feature了,运行时增加 Connector, 创建数据源,创建集群,部署集群应用 ...
就 Tomcat 而言,如何能让他支持运行时增加、修改容器的组件呢?
其实,多篇文章都说过默认 Tomcat 就支持对于组件的操作,但「缺少」一个「壳子」来调用功能。
内置提供对于组件操作的功能,大部分可以通过 JMX 来操作MBean Server实现。关于 JMX ,可以参考之前这篇文章(你了解JMX在Tomcat的应用吗?)
所以我们如果要让 Tomcat 支持,可以自已打造一个壳。造壳的方式有以下两种:
1。通过JMX 操作MBean Server,调用MBean 上的各种对外提供的操作
2。深入到 Tomcat 内部组件类,例如要添加Context,通过 Host.addChild来完成, Context 自己通过传入的属性来生成 StandardContext。
如果这些功能都已经实现了,那此时我们相当于把内存中的 Tomcat 的「Server」给改了,一切运行都按照改好的在运行。但是当有需要重启 Tomcat时,这些改动就丢了。在启动完成后,还需要再重来一次。
我们心想:如果能「保存」就好了。想曹操,曹操真来了。
在 Tomcat 内部,提供了一个名为 「StoreConfigLifeCycleListener」的 Listener,和以前说的那些内存检查、ThreadLocal检查之类的一样,添加到server.xml里就能用。默认是没有的,需要手动添加进去。
这个 Listener 是用来干嘛的呢?
我们来看代码:
public voidlifecycleEvent(LifecycleEvent event) {
if(Lifecycle.AFTER_START_EVENT.equals(event.getType())) {
if(event.getSource()instanceofServer) {
createMBean((Server) event.getSource());
}else{
log.warn(sm.getString("storeConfigListener.notServer"));
}
}else if(Lifecycle.AFTER_STOP_EVENT.equals(event.getType())) {
if(oname!=null) {
registry.unregisterComponent(oname);
oname=null;
}
}
}
功能就是在容器启动后创建并注册一个StoreConfig 的 MBean。
有这个MBean 能干嘛呢?它提供了这样一个功能供JMX调用:
/**
* Store current Server.
*/
@Override
public void storeConfig() {
store(server);
}
这个方法的逻辑注释写的明白:把当前整个 Server的信息保存下来。
/**
* Write the configuration information for this entire Server
* out to the server.xml configuration file.
* @param aServer Server instance
*/
@Override
public synchronized boolean store(Server aServer) {
StoreFileMover mover = new StoreFileMover(System
.getProperty("catalina.base"), getServerFilename(),
getRegistry().getEncoding());
// Open an output writer for the new configuration file
try {
try (PrintWriter writer = mover.getWriter()) {
store(writer, -2, aServer);
}
mover.move();
return true;
} catch (Exception e) {
log.error(sm.getString("config.storeServerError"), e);
}
return false;
}
从上面代码看,实现也和我们预期类似,写配置,备份当前配置内容...
对于配置,是通过StoreDescription来表示,配置内容的写是通过StoreFactory来完成。
对于这种配置的增改和保存,我们也可以通过JAXB、Dom4j 之类的来自己手写一套,来完成这个功能。我上次大致看了下 PSI-probe的代码,目前并没有对于配置的修改支持,这里可以做为一个Pull Request 的点。
Have fun!
领取专属 10元无门槛券
私享最新 技术干货