`
hiphunter921
  • 浏览: 68320 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

对tomcat admin中的datasource管理的扩展(二)

阅读更多

但是,能不能不重启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,生产环境中也可以直接使用,并且添加后的数据源直接可以被调用到。

 

 

啦啦啦~

 

 

写了大托大托的废话,没啥深入的,完全是自己的一点体会,能力有限,写的比较土,仅作自己笔记使用,欢迎吐槽。

分享到:
评论

相关推荐

    apache-tomcat-6.0.18.rar

    随着技术的发展,新版本的Tomcat增加了更多特性,如更好的性能、更完善的管理工具以及对Java EE标准的更全面支持。然而,对于初学者来说,从这个基础版本开始学习,可以更清晰地理解Web服务器的工作原理。

    Tomcat JDNI数据源配置详解

    这种方式不仅简化了数据库连接的管理,还提高了应用的灵活性和可扩展性。对于需要频繁访问数据库的应用程序来说,合理配置JNDI数据源是非常重要的。此外,还可以结合Spring框架进一步优化数据源管理,实现更加灵活的...

    详解SpringBoot配置连接池

    spring.datasource.password=admin spring.datasource.driver-class-name=com.mysql.jdbc.Driver ``` Druid是一个功能丰富的JDBC组件,它的特点包括: 1. **监控性能**:Druid内置了StatFilter插件,能够详细...

    springboot+mysql+oracle+druid 双数据库进行数据同步

    **双数据源**:在SpringBoot中,通过`Spring Boot Data JPA`和`DataSource`配置,我们可以设置多个数据源。这通常需要使用`@Primary`注解来标记主数据源,同时定义另一个数据源。在本项目中,Oracle作为源数据库,...

    使用lucenesolr搭建Oracle数据库全文搜索服务.docx

    - 启动Tomcat后访问`localhost:8080/solr/`,可以看到欢迎页面,通过点击`Admin db`进入管理界面进行查询等操作。 #### 四、数据导入与索引建立 为了实现从Oracle数据库导入数据至Solr进行全文搜索,需要完成以下...

    AlwaysInMySelf-SpringBoot-master.zip

    - 数据库设计:包括新闻分类、新闻详情、用户管理等多个表,可能需要涉及的关系有分类与新闻的一对多关系,用户与新闻的多对多关系等。 - RESTful API设计:为前后端分离,需要设计一套清晰、符合REST原则的API...

    springboot整合mybatis+activemq(activemq可以去官网下载 )

    本文将深入探讨如何将这三个技术整合到一个项目中,以便构建高效、可扩展的微服务架构。 首先,Spring Boot是Spring框架的一个扩展,它提供了快速开发新Spring应用的能力。通过内嵌Tomcat或Jetty服务器,自动配置...

    hibernate配置数据库连接池的三种方法

    在软件开发中,数据库连接池是一种重要的资源管理工具,它能显著提高数据库操作的效率和性能。连接池通过预先创建并维护一定数量的数据库连接,使得应用程序在需要时可以直接获取,而不是每次都进行数据库连接的建立...

    Spring-Boot1.52 SpringSecurity4 Spring Data Jpa 整合例子

    - **外部化配置**: 支持将配置从应用代码中分离出来,便于管理且不影响代码。 - **嵌入式服务器**: 提供了内置的 Tomcat、Jetty 或 Undertow 服务器,无需额外部署。 #### 三、Spring Security 4 **Spring ...

    中文 Spring Boot 参考指南

    2. **Spring Boot Admin**:一个独立的应用,可以用来可视化管理和监控 Spring Boot 应用。 **九、持续集成与部署** 1. **Maven 插件**:Spring Boot 提供了 Maven 插件,可以打包成可执行的 JAR 或 WAR 文件。 2. ...

    Spring Boot使用和配置Druid

    Druid作为一款高性能的数据库连接池,被广泛应用于各种项目中,它提供了丰富的监控和扩展能力。本篇将详细介绍如何在Spring Boot中集成和配置Druid。 首先,我们需要在项目的`pom.xml`文件中引入Druid的依赖包。...

    基于SpringBoot + Mybatis + MySQL实现文件系统

    【SpringBoot + Mybatis + MySQL实现文件系统】 在IT领域,构建一个文件系统通常涉及到后端服务、数据库管理和...在实际开发中,还需要考虑性能优化、错误处理、日志记录等多个方面,以确保系统的稳定性和可扩展性。

    springbootmybatisplus.zip

    MyBatisPlus是对MyBatis的轻量级扩展,提供了诸如插入、更新、删除、查询等基本操作,还支持条件构造、分页、自定义SQL、关联查询等功能,极大地减少了开发者编写SQL的负担。它简化了MyBatis的使用,使得数据库操作...

    web.xml详细说明

    - **作用**:定义Web应用的名称,通常用于在开发工具或管理控制台中展示。 - **示例**: ```xml &lt;display-name&gt;TomcatExample ``` 2. **`&lt;description&gt;`** - **作用**:提供关于Web应用的描述信息,便于管理...

    ssh(三大框架)集成

    #### 二、环境搭建与依赖 ##### 2.1 环境需求 | 编号 | 环境 | 版本 | |------|--------|---------| | 1 | JDK | 1.6+ | | 2 | Tomcat | 7 | | 3 | Maven | 3.3.* | ##### 2.2 依赖库 以下是项目所需的主要依赖...

    boot-spring-boot:“启动Spring Boot:스프링기”

    3. Spring Boot Admin - 第三方扩展,可以可视化地管理和监控 Spring Boot 应用。 以上就是关于启动 Spring Boot 及其相关知识点的详细介绍,包括它的核心特性、项目构建、HTML 整合、数据访问以及测试和监控等方面...

    Web.xml常用元素

    以下是对`Web.xml`中常见元素的详细解析: #### - **作用**:根元素,所有其他配置元素都位于其内部,用于封装整个Web应用的配置。 #### - **作用**:定义Web应用的名称,通常用于GUI工具中显示应用的名称。 -...

Global site tag (gtag.js) - Google Analytics