tomcat如何访问jndi信息

在上一篇我们知道tomcat在启动时会解析context.xml并将相应的数据源信息解析到上下文中。那么接下来的工作就是如何去访问这个数据源信息了。我们访问数据源信息是通过以下代码进行访问的 

InitialContext initialContext = new InitialContext();
	DataSource dataSource = (DataSource) initialContext.lookup("java:comp/env/jdbc/xx");

那么我们就按照这个访问思路跟踪一下代码。我们看一下InitialContext的lookup实现,如下所示:

public Object lookup(String name) throws NamingException {
	return getURLOrDefaultInitCtx(name).lookup(name);
    }

这里会取得一个UrlContext或InitContext,究竟取得哪个取决于传递的参数信息,这里我们的方法是以java:开头,即它有一个scheme_id信息,按照官方对于传递的URL字符串规则,显示如下所示(该描述在javadoc中的InitialContext中):

解析 URL 字符串时生成此策略的一个异常,如下所述。
 在将 URL 字符串(一个 scheme_id:rest_of_name 形式的 String)作为名称参数传递给任一方法时,将定位处理该方案的一个 URL 上下文工厂,并
将它用于解析该 URL。如果没有找到这样的工厂,则使用由 "java.naming.factory.initial" 指定的初始上下文。类似地,当将第一个组件是 URL 字
符串的 CompositeName 对象作为名称参数传递给任一方法时,将定位一个 URL 上下文工厂并将它用于解析第一个名称组件

因此,它将尝试找一个URLContext,而在前文中我们提到在tomcat支持命名服务中,设置了一个Context.URL_PKG_PREFIXES参数为org.apache.naming,因此,会尝试寻找一个名叫
org.apache.naming.java.javaURLContextFactory的类,而恰好存在这个类。那么InitContext将转向这个类,再从这个类找回一个UrlContext实例,具体实现如下所示:

继续阅读“tomcat如何访问jndi信息”

在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”