但是,能不能不重启tomcat就有效果呢?
首先需要重启的原因是因为修改了context.xml后,tomcat会自动redeploy app,首先就造成了之前的session都不可用,其次,classloader也发生了变化(由于再次使用到dom4j的SAXReader去读取xml文件时候,会报类型转换异常,但明明是同样的类型,其实原因是虽然类名相同,但是在不同的classloader下,造成了这样的错误,中间我还替换了一个别人自己hack的dom4j.jar但是,虽然解决了这个错,还是有其他一些错误,头晕中,还是换条路吧),同时,我们也知道,如果app的web.xml发生了修改,tomcat也会自动redeploy响应的app,于是乎,就找找tomcat在哪里去监控这些文件是否发生变化吧。
试试看吧,开始看tomcat的source............(看的头晕眼花)
.
.
.
还在看,继续找~~~.......
终于有一天,发现Context接口中有个findWatchedResources,removeWatchedResource这样的方法,看似有点像,那就试试看调用一下removeWatchedResource吧,怎么才能获取到context对象呢,发现tomcat默认实现都在
org.apache.catalina.core包下,自然Context接口的默认实现叫做StandardContext,怎么获取到这个对象的实例呢?
这里就涉及到tomcat的整体架构了,网上好多相关介绍,说的都很详细,我这里涉及到的就是Server->Service->Engin->Host->Context,这样一个架构:
通过Server server = ServerFactory.getServer();这个静态方法就可以获取到最外层的Server对象;server中可以有多个service,因此需要通过domain来匹配,默认domian就是Catalina;service.getContainer()获取到engine;engine中又多个host,需要通过findChild(String name)来找到对应的host,这里我们的name是localhost;host再调用findChildren就可以获得到所有的StandardContext对象了。
Container[] containers=(Container[])host.findChildren();
for(int i=0;i<containers.length;i++){
if(containers[i] instanceof StandardContext){
StandardContext context = (StandardContext)containers[i];
String[] watchedResources=context.findWatchedResources();
for(String watchedResource : watchedResources){
if(watchedResource.indexOf("conf\\context.xml")>0){
log.debug("Remove watchedResource " + watchedResource + " from "+context.getName());
context.removeWatchedResource(watchedResource);
}
}
}
}
这段代码就是获取到StandardContext后,去调用removeWatchedResource方法,然后放在我自己的tomcat admin中的一个servlet的init方法中去调用一下,看看结果,哇,貌似可以了,commit change后,tomcat admin的session还在,我可以继续操作,回头看看console,发现还是有app被redeploy了,啥情况,一下就想到了是不是调用的时机不对,部署到admin这个应用之前的应用中的resource都被删除了,之后部署的没有被删除(从日志中发现的),那是不是放到我commit change之前去调用,就可以了呢??试了发现,看似日志里每个应用的resource都删除了,但是所有的app都被redeploy了。
回顾一下,发现,其实之前在servlet的init方法中调用后,只有这个app是没有redeploy的,继续看tomcat source吧。
添加了对org.apache.catalina下的debug等级的日志输出,发现,所有servlet的装载,包括调用init方法都是在Context的start方法中执行的,然后再会调用HostConfig中的start方法,HostConfig的实例其实是作为一个lifecycleListener在StandardHost的init方法中被添加到其lifecycle属性中,当收到Lifecycle.START_EVENT时候,就会去调用这些listener的start方法,这时候就会去把context的warchedResource添加到HostConfig中的deployed里面去,
deployed是个map,值是DeployedApplication实例,这个类是HostConfig的一个内部类,
resource就是被添加到这个类实例的reloadResources属性中去。
那是不是可以获取到这些DeployedApplication实例,在从他们的属性reloadResources中去删除对应的resource呢?
看似不行,HostConfig的deployed属性是protected的,别且之前也说了DeployedApplication是protected内部类。
继续苦闷中。既然是protected,那就是可以自己来继承一下HostConfig嘛?看了下StandardHost里面,获取HostConfig时候貌似还是通过反射来的,类路径是可以set的,于是找到了server.xml中的Host标签,找到name是localhost的,添加了一个属性hostConfigClass="com.elitecrm.tomcat.EliteHostConfig",并且自己实现的EliteHostConfig继承了HostConfig:
public class EliteHostConfig extends HostConfig {
private static final Log log = LogFactory.getLog(EliteHostConfig.class);
public HashMap<String,DeployedApplication> getDeployed(){
return super.deployed;
}
public void removeReloadResources(){
HashMap<String,DeployedApplication> deployed=getDeployed();
for(String contextPath:deployed.keySet()){
DeployedApplication app = deployed.get(contextPath);
String[] resources = (String[]) app.reloadResources.keySet().toArray(new String[0]);
for(int i=0;i<resources.length;i++){
log.debug("Remove resources " + resources[i]+ " for context " + contextPath);
if(resources[i].indexOf("conf"+File.separator+"context.xml")>=0){
app.reloadResources.remove(resources[i]);
}
}
}
}
}
打包成jar,放到了tomcat的lib目录下
嘿嘿,还真的可以:
LifecycleListener[] lls= host.findLifecycleListeners();
for(int i=0;i<lls.length;i++){
LifecycleListener ll=lls[i];
if(ll instanceof EliteHostConfig){
EliteHostConfig hostConfig=(EliteHostConfig)ll;
hostConfig.removeReloadResources();
}
}
现在,删除resource的代码变成了这样。
在登录我的tomcat admin登录界面时候,调用这个方法,然后,commit change都没问题了,所有app都老老实实,一动不动,爽了。
那这样commit change后的datasource,能不能直接使用了呢?
测试发现不行,继续看tomcat source,查找如何创建对应的resource的。
找到了StandardContext里面有类似:context.getNamingResources().addResourceLink(resourceLink);
这样的代码,但是传递的resourceLink是个ContextResourceLink对象,然后又找到了这个对象的创建:
ContextResourceLink resourceLink = new ContextResourceLink();
resourceLink.setGlobal(resourceLinkName);
resourceLink.setName(resourceLinkName);
resourceLink.setType("javax.sql.DataSource");
同时发现context.getNamingContextListener().addResourceLink(resourceLink);这样的代码,于是又添加上了:
NamingContextListener ncl = context.getNamingContextListener();
ContextAccessController.setWritable(ncl.getName(), context);
ncl.addResourceLink(resourceLink);
这里还不能直接调用addResourceLink或者removeResourceLink,其实这些方法里面是jndi的bind和unbind方法,但是tomcat设置了Context的resource是read only的,所以要先自己去执行的ContextAccessController.setWritable方法,才能去bind或者unbind。
尝试下来,发现只需要对context的NamingResources做addResourceLink,不需要对NamingContextListener去手动bind。
这样,终于算是基本完成了,可以再我的tomcat admin上去添加修改删除数据源,并且可以commit change保存数据到配置文件,并且不会引发app的redeploy,生产环境中也可以直接使用,并且添加后的数据源直接可以被调用到。
啦啦啦~
写了大托大托的废话,没啥深入的,完全是自己的一点体会,能力有限,写的比较土,仅作自己笔记使用,欢迎吐槽。
分享到:
相关推荐
随着技术的发展,新版本的Tomcat增加了更多特性,如更好的性能、更完善的管理工具以及对Java EE标准的更全面支持。然而,对于初学者来说,从这个基础版本开始学习,可以更清晰地理解Web服务器的工作原理。
这种方式不仅简化了数据库连接的管理,还提高了应用的灵活性和可扩展性。对于需要频繁访问数据库的应用程序来说,合理配置JNDI数据源是非常重要的。此外,还可以结合Spring框架进一步优化数据源管理,实现更加灵活的...
spring.datasource.password=admin spring.datasource.driver-class-name=com.mysql.jdbc.Driver ``` Druid是一个功能丰富的JDBC组件,它的特点包括: 1. **监控性能**:Druid内置了StatFilter插件,能够详细...
**双数据源**:在SpringBoot中,通过`Spring Boot Data JPA`和`DataSource`配置,我们可以设置多个数据源。这通常需要使用`@Primary`注解来标记主数据源,同时定义另一个数据源。在本项目中,Oracle作为源数据库,...
- 启动Tomcat后访问`localhost:8080/solr/`,可以看到欢迎页面,通过点击`Admin db`进入管理界面进行查询等操作。 #### 四、数据导入与索引建立 为了实现从Oracle数据库导入数据至Solr进行全文搜索,需要完成以下...
- 数据库设计:包括新闻分类、新闻详情、用户管理等多个表,可能需要涉及的关系有分类与新闻的一对多关系,用户与新闻的多对多关系等。 - RESTful API设计:为前后端分离,需要设计一套清晰、符合REST原则的API...
本文将深入探讨如何将这三个技术整合到一个项目中,以便构建高效、可扩展的微服务架构。 首先,Spring Boot是Spring框架的一个扩展,它提供了快速开发新Spring应用的能力。通过内嵌Tomcat或Jetty服务器,自动配置...
在软件开发中,数据库连接池是一种重要的资源管理工具,它能显著提高数据库操作的效率和性能。连接池通过预先创建并维护一定数量的数据库连接,使得应用程序在需要时可以直接获取,而不是每次都进行数据库连接的建立...
- **外部化配置**: 支持将配置从应用代码中分离出来,便于管理且不影响代码。 - **嵌入式服务器**: 提供了内置的 Tomcat、Jetty 或 Undertow 服务器,无需额外部署。 #### 三、Spring Security 4 **Spring ...
2. **Spring Boot Admin**:一个独立的应用,可以用来可视化管理和监控 Spring Boot 应用。 **九、持续集成与部署** 1. **Maven 插件**:Spring Boot 提供了 Maven 插件,可以打包成可执行的 JAR 或 WAR 文件。 2. ...
Druid作为一款高性能的数据库连接池,被广泛应用于各种项目中,它提供了丰富的监控和扩展能力。本篇将详细介绍如何在Spring Boot中集成和配置Druid。 首先,我们需要在项目的`pom.xml`文件中引入Druid的依赖包。...
【SpringBoot + Mybatis + MySQL实现文件系统】 在IT领域,构建一个文件系统通常涉及到后端服务、数据库管理和...在实际开发中,还需要考虑性能优化、错误处理、日志记录等多个方面,以确保系统的稳定性和可扩展性。
MyBatisPlus是对MyBatis的轻量级扩展,提供了诸如插入、更新、删除、查询等基本操作,还支持条件构造、分页、自定义SQL、关联查询等功能,极大地减少了开发者编写SQL的负担。它简化了MyBatis的使用,使得数据库操作...
- **作用**:定义Web应用的名称,通常用于在开发工具或管理控制台中展示。 - **示例**: ```xml <display-name>TomcatExample ``` 2. **`<description>`** - **作用**:提供关于Web应用的描述信息,便于管理...
#### 二、环境搭建与依赖 ##### 2.1 环境需求 | 编号 | 环境 | 版本 | |------|--------|---------| | 1 | JDK | 1.6+ | | 2 | Tomcat | 7 | | 3 | Maven | 3.3.* | ##### 2.2 依赖库 以下是项目所需的主要依赖...
3. Spring Boot Admin - 第三方扩展,可以可视化地管理和监控 Spring Boot 应用。 以上就是关于启动 Spring Boot 及其相关知识点的详细介绍,包括它的核心特性、项目构建、HTML 整合、数据访问以及测试和监控等方面...
以下是对`Web.xml`中常见元素的详细解析: #### - **作用**:根元素,所有其他配置元素都位于其内部,用于封装整个Web应用的配置。 #### - **作用**:定义Web应用的名称,通常用于GUI工具中显示应用的名称。 -...