在embed tomcat中使用jndi命名服务

在嵌入式的tomcat中,默认是不开启命名服务支持的。如果要使用命名服务,按照官方的意思,只需要增加以下代码即可:

Tomcat tomcat = new Tomcat();
tomcat.enableNaming();

这样即可,但实际上,这个tomcat.enableNaming里面的东西太多。实际上,只需要增加以下代码即可: 

        System.setProperty("catalina.useNaming", "true");//开启命名服务支持
        String value = "org.apache.naming";
        System.setProperty(javax.naming.Context.URL_PKG_PREFIXES, value);//注册命名服务URL前缀

在如上的代码中,并不需要增加关于INITIAL_CONTEXT_FACTORY的设置,因为在访问资源时,如访问java:comp/env/jdbc/xx.tomcat只需要从URL_PKG_PREFIXES所在包下,寻找以URL_PKG_PREFIXES.java.javaURLContextFactory的类即可,而tomcat中有此类。并且查找的context是URLContext,而并不是InitialContext,因此其他的代码都是不需要的。

在具体的解析中,类StandardContext中进行启动时,只需要判断System.getProperty("catalina.useNaming")即可判断出是否启动命名上下文。如果启用命名服务,则会自动将NamingContextListener注册到监听器中,余下的工作就是监听器去完成了。详细的代码如下所示:(以下代码在类StandardContext中的startInternal方法内)

        String useNamingProperty = System.getProperty("catalina.useNaming");
 ......

        if (ok && isUseNaming()) {
            if (getNamingContextListener() == null) {
                NamingContextListener ncl = new NamingContextListener();
                ncl.setName(getNamingContextName());
                addLifecycleListener(ncl);
                setNamingContextListener(ncl);
            }
        }
//启动lifecycle,即刚注入的监听器,以及其他信息
fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null);

tomcat如何解析META-INF目录下的context.xml

前阵子,准备重新测试一下tomcat对context.xml中数据源的处理情况,当时我的context.xml是这样写的,并且放在web目录下的META-INF目录下

<?xml version='1.0' encoding='UTF-8'?>
<Context>
	<resource name="jdbc/xx" auth="Container" type="javax.sql.DataSource" password="123456"
			  driverClassName="com.mysql.jdbc.Driver"
			  username="root" url="jdbc:mysql://127.0.0.1/xx"
			  />
</Context>

并且相应的mysql.jar包已经放到tomcat的lib目录下,然后在进行运行之后,死活也找不到所配置的数据源信息。最后找了半天,错误原因也很简单的,并不是context.xml位置不正确。在上面的xml配置中,resource节点中的resource应该写首字母大写,即应该为Resource而不是resource。修改之后,一切正确了。

那么是什么原因导致tomcat找不到这里所写的小写的resource呢,这就涉及到tomcat内部对context.xml解析了。在tomcat内部,是通过ContextConfig类来用于处理context.xml信息,并通过Digester类来完成对context.xml的解析,在具体的解析过程中,正是因为对相应节点的匹配不成功,导致没有解析。但是不会发生错误。

在tomcat内部,使用ContextConfig来解析web.xml和context.xml,当仔细查看代码时,你会发现,解析web.xml和context.xml使用的是同一个类,即org.apache.tomcat.util.digester.Digester,通过使用不同的Rule对节点进行匹配,让匹配之后的Rule进行不同的操作,而Digester本身只做一个分发的作用,并不参与实际逻辑。整个流程可以由如下图所示:

继续阅读“tomcat如何解析META-INF目录下的context.xml”

深入理解jvm装载约束

    网上进行google或者baidu时,以及在使用tomcat或者其它框架时,经常碰到以下的问题:

ava.lang.LinkageError: loader constraint violation: when resolving field  XXXXXX 
have different Class objects for that type

    这种问题在使用jbmp,或者websphere时经常会出现,一般的解决方法就是说有的包重复了,删除一些jar包就可以了。
    问题是解决了,但问题产生的根源在哪,我们需要知道这个问题。在阅读了《深入JVM虚拟机》之后,中间有提到一段话,这段话揭示了问题产生的本质在于在一个方法体中同一个类被多个不同的classLoader所加载了,即在声明时的类与引用的类(同一个类)在加载时,却是由不同的classLoader所加载的:

如果引用的类型和被引用的类型并非由同一个初始装载器装载,虚拟机必须确保在字段或者方法描述符中所提及的类型
在不同的命名空间中保持一致。

    这句话理解起来非常的困难,简单一点理解可以由下面的说明来理解:

  1. 类A中有一个字段a,它的类型为X
  2. 类B中有一个字段b,它的类型也为X
  3. 类A由classLoaderA所加载,类B由classLoaderB所加载
  4. 执行赋值语句A.a = B.b,由于这两个类型均为X,可以执行,但是有一个要求,这个要求就是在A中所装载类X的装载器必须和在B中装载类X的装载器相同,否则赋值语句失败

    为什么会产生上面的输出,我们可以来看一个以下的代码

继续阅读“深入理解jvm装载约束”

使用监控技术实现tomcat的按需要进行重启

    我们经常需要碰到这种情况,当用户提出一个问题,并且我们已经修改之后,已经由自动更新系统更新到客户方,但这时需要重新启动tomcat才能使新修改的代码工作。这时候,就要想办法来重新启动这个tomcat了。
    有的同学提到一个使用tomcat控制台重新部署项目的想法,但是通过这种方法,有时候并不能有效地进行部署,因为可能存在非守护进程不能完全退出,以及有些资源未能正确回收的问题。相比来说,直接重启tomcat来得更方便一些。

    在前面已经说到,实现项目的自动更新,是利用embeded tomcat来进行重新启动来完成的,这中间实现tomcat在指定时间更新并重启的功能是使用类似监听技术来实现的。即在客户机器上,同时部署了两个组件,一个为运行我们项目的tomcat,另一个则为监控项目代码的java监听器。此监听器用于在指定时间监听项目代码是否有更新,如果有更新,则使用svn进行更新,并重新启动tomcat。相应的代码如下所示:

	private void updateAndRestart() throws Exception {
		logger.log(Level.INFO, "准备执行更新线程......");
		if(hasUpdate(svn)) {
			logger.log(Level.INFO, "发现更新,准备重新启动tomcat......");
			shutdownTomcat();
			logger.log(Level.INFO, "成功关闭tomcat,正在重起");
			startTomcat();
			logger.log(Level.INFO, "重起tomcat成功......");
		} else
			logger.log(Level.INFO, "无有效更新......");
	}

    这样的代码,即实现了tomcat的更新和重启操作,惟一不足的是这段代码并不能接收来自于网页上的命令实现按需要重启,它只能在规定的时间(如每天24点)进行操作。
    有的同学,想到如果将这个监听器做成一个类似项目的服务并启动, 即可以实现效果了。但实际的问题时,对于项目来说,通过路由器打开太多的端口是有害而无利了。我们已经打开了80端口用于项目访问,最好不要再打开其它端口了。

    本文描述了一个在项目代码中增加用于实现重启的功能代码,并通过socket请求访问监听器并通过监听器来重启项目tomcat的实现。

    实现原理如下:

  1.     监听器监听指定端口,进行监听操作
  2.     由项目向外提供重启功能界面,通过功能界面进行操作
  3.     项目的重启功能向监听器指定端口发送重启指令
  4.     监听器接收到相应socket请求,即执行重启操作

继续阅读“使用监控技术实现tomcat的按需要进行重启”

zip版tomcat(6,7)不能注册成windows服务或者注册后不能启动的问题解决

     在日常的开发中,经常要使用到tomcat,一般情况下,我们只需要下载zip版的tomcat,并在ide中进行配置即可。而不需要将tomcat注册成服务,但在部署中,如果需要将tomcat部署到客户机器上时,就会出现一定问题了。
    一般情况下,如果要部署成服务的形式,就需要下载专门的windows安装版(一个exe的安装文件),在windows下进行安装。而这种安装版,有一个问题就是,它默认的服务名就是tomcat6或tomcat7,而不能手动的进行修改。如果需要安装多个tomcat,就不能使用安装版了。在这种情况下,我们一般是下载zip版的tomcat,通过service.bat的方式来将tomcat注册成服务。
     一般情况下,注册成服务的tomcat并不能启动,问题有很多种。我就在实际过程中发现的问题,一一列举出来,以方便碰到此问题的同学一起解决这个问题。

A service specific error occurred: 0.

More help is available by typing NET HELPMSG 3547.

继续阅读“zip版tomcat(6,7)不能注册成windows服务或者注册后不能启动的问题解决”

(转)Servlet 工作原理解析(以tomcat7为基础)

     Web 技术成为当今主流的互联网 Web 应用技术之一,而 Servlet 是 Java Web 技术的核心基础。因而掌握 Servlet 的工作原理是成为一名合格的 Java Web 技术开发人员的基本要求。本文将带你认识 Java Web 技术是如何基于 Servlet 工作,你将知道:以 Tomcat 为例了解 Servlet 容器是如何工作的?一个 Web 工程在 Servlet 容器中是如何启动的? Servlet 容器如何解析你在 web.xml 中定义的 Servlet ?用户的请求是如何被分配给指定的 Servlet 的? Servlet 容器如何管理 Servlet 生命周期?你还将了解到最新的 Servlet 的 API 的类层次结构,以及 Servlet 中一些难点问题的分析。
    本文转自 许令波(来自淘宝),原文为 Servlet 工作原理解析 

继续阅读“(转)Servlet 工作原理解析(以tomcat7为基础)”