The Jakarta Project
      The Apache Jakarta Tomcat Servlet/JSP Container

链接

用户指南

参考

开发 Tomcat

The Apache Jakarta Tomcat 5 Servlet/JSP Container

JNDI Resources HOW-TO

Printer Friendly Version
print-friendly
版本
简介

Tomcat 5以一个可与Java2企业版(J2EE)应用服务器相容的方式,提供给每个在它里面运行的网络程序一个JNDI InitialContext执行实例。J2EE标准在/WEB-INF/web.xml文件里提供了一系列标准元素,可用来查询资源;被这些元素索引的资源还需要根据不同的应用服务器来配置。

对Tomcat 5来说,每个网络应用的InitialContext是通过<服务器>元素中的<Context><DefaultContext>元素来配置的<Context>最好被指定在该网络应用的上下文XML文件中,或者放在$CATALINA_HOME/conf/server.xml文件中。<DefaultContext>必须被指定在$CATALINA_HOME/conf/server.xml 。

Tomcat 5还为整个服务器所使用的全球性资源提供一个单独的命名空间(namespace)。这些资源配置在$CATALINA_HOME/conf/server.xml文件的 <GlobalNameingResources>元素中。你可以通过<ResourceLink>;元素把这些资源提供给各个网络程序。

在这些元素内定义的资源可以被你的程序部署描述符( /WEB-INF/web.xml )里的下列这些元素引用:

  • <env-entry> - 环境变量输入,这个单个值参数(single-value parameter)可以用来配置网络程序如何运行。
  • <resource-ref> - 资源引用名,它通常指向该资源的对象(object)工厂,如JDBC 数据源, a JavaMail Session,或者作为自己编写(custom)的对象工厂配置在Tomcat 5里边。
  • <resource-env-ref> - 资源环境索引,这是Servlet 2.4版本里新加入的资源引用(resource-ref)变量,对那些不要求用户认证的资源来说,这个变量比较容易配置。

InitialContext是在网络程序最初被部署到服务器上时配置的,并且所有的网络程序可以使用它(仅供只读访问)。所有配置的条目和资源都被放置在JNDI的java:comp/env 命名空间中。以下是以JDBC DataSource(数据源)为例来说明如何访问一个资源——它看起来象这样:

// Obtain our environment naming context 
Context initCtx = new InitialContext(); 
Context envCtx = (Context) initCtx.lookup("java:comp/env"); 

// Look up our data source 
DataSource ds = (DataSource) 
envCtx.lookup("jdbc/EmployeeDB"); 

// Allocate and use a connection from the pool 
Connection conn = ds.getConnection(); 
... use this connection to access the database ... 
conn.close();

参看下面两个规范,了解更多关于编码JNDI APIs的信息,以及Java2 企业版(J2EE)服务器所支持的相关性能:

配置JNDI资源

每一个JNDI资源都是由<Context><DefaultContext>内的元素来配置的,这些元素包括:

  • <Environment> - 配置环境变量的名字和值,网络程序可通过JNDI InitialContext 来查询(相当于在网络程序部署描述符中包括一个<env-entry>元素)。
  • <Resource> - 配置程序可以使用的资源名字和数据类型(相当于 在网络程序部署描述符中包括一个<resource-ref>元素)。
  • <ResourceParams> - 配置资源工厂的Java类,以及用来配置这个资源工厂的JavaBeans属性。
  • <ResourceLink>; - 为定义在全球性JNDI context内的资源加上一个链连。让网络程序可以使用这个链连访问定义在<GlobalNamingResources>里的资源,这个<GlobalNamingResources>是<Server>的子元素。  <Server>

这些元素中的任何元素组合都可以嵌套在<Context>内(仅仅与这一特定的网络程序相关)或<DefaultContext>元素内(用来设置默认的配置特征,用以自动部署程序)。

另外,包含在程序部署描述符(/WEB-INF/web.xml)里的所有<env-entry>元素的名字和值也都会加入到initial context中,如果<Environment环境>元素允许(通过把override(改写,覆盖)设置为"true"),<env-entry>元素中的值将覆盖conf/server.xml 中相应的值。

全球性资源可以定义在全服务器的JNDI context,把上面描述的资源元素加到<Server>的子元素<GlobalNamingResources>中,并且用<ResourceLink>;把它包括在每个网络程序的(per-web-application)上下文中。

Tomcat标准资源工厂

Tomcat 5包含了一系列标准资源工厂来为你的网络程序服务,但又给你配置适度的弹性(在 $CATALINA_HOME/conf/server.xml 里)而不需要修改网络程序或部署描述符。下列章节详细描述了如何配置和使用这些标准资源工厂。

关于怎样产生,安装,配置和使用你自己编写的资源工厂类,参看Adding Custom Resource Factories 。  Adding Custom Resource Factories

注意——在标准资源工厂当中,只有"JDBC Data Source" 和 "User Transaction" 是必须让所有执行Java2企业版(J2EE)规范的平台使用。其他的标准资源工厂,以及你自己写的资源工厂只对Tomcat有效,而不能假设可让其他容器使用。

Generic JavaBean Resources

简介

这个资源工厂可以用来产生任何遵守标准JavaBeans命名习惯的Java类的对象(例如,它具有一个没有变量的构造式,具有遵守setFoo()命名模式的属性设置方法)。每次lookup()输入,这个资源工厂就会产生一个新的相称的bean class实例。  lookup()

使用这一设施的必需步骤如下:

产生你的JavaBean类

产生JavaBean类,让它每一次要查找资源工厂时就被实例化(instantiated)。在这个例子中,假设你产生一个叫做com.mycompany.MyBean的类,它看起来象这样:

package com.mycompany; 

public class MyBean { 

private String foo = "Default Foo"; 

public String getFoo() { 
return (this.foo); 
} 

public void setFoo(String foo) { 
this.foo = foo; 
} 

private int bar = 0; 

public int getBar() { 
return (this.bar); 
} 

public void setBar(int bar) { 
this.bar = bar; 
} 


}

声明你的资源要求

下一步,修改你的网络程序部署描述符( /WEB-INF/web.xml ) 来声明JNDI名字,在这个名字下你将要求产生这个bean的新的实例。最简单的方法是象这样来使用<resource-env-ref>元素:??????

<resource-env-ref> 
<description> 
Object factory for MyBean instances. 
</description> 
<resource-env-ref-name> 
bean/MyBeanFactory 
</resource-env-ref-name> 
<resource-env-ref-type> 
com.mycompany.MyBean 
</resource-env-ref-type> 
</resource-env-ref>

警告——一定要尊重元素的排列顺序,这是网络程序部署描述符DTD所要求的。详细信息请参看Servlet 规范

编写使用这个资源的程序

典型的使用这个资源环境索引的编码看起来象这样:

Context initCtx = new InitialContext(); 
Context envCtx = (Context) initCtx.lookup("java:comp/env"); 
MyBean bean = (MyBean) envCtx.lookup("bean/MyBeanFactory"); 

writer.println("foo = " + bean.getFoo() + ", bar = " + 
bean.getBar());

配置Tomcat的资源工厂

要配置Tomcat的资源工厂,把这样一个元素加入到$CATALINA_HOME/conf/server.xml 文件里边,再把它嵌套在这个网络程序的Context元素里(或者嵌套在一个DefaultContext元素for the surrounding <Host> or <Engine> element.?????)  DefaultContext  <Host>

<Context ...> 
... 
<Resource name="bean/MyBeanFactory" auth="Container" 
type="com.mycompany.MyBean"/> 
<ResourceParams name="bean/MyBeanFactory"> 
<parameter> 
<name>factory</name> 
<value>org.apache.naming.factory.BeanFactory</value> 
</parameter> 
<parameter> 
<name>bar</name> 
<value>23</value> 
</parameter> 
</ResourceParams> 
... 
</Context>

注意资源名称(这里是bean/MyBeanFactory)必须与网络程序部署描述符里指定的值相对映。我们也还要初始化bar property的值,这会在返回新的bean之前引起调用setBar(23)。因为我们没有初始化foo property(尽管我们也可以这样做),bean会包含它的构造器设置的任何默认值。  foo

JavaMail Sessions

简介

在许多网络应用中,发送电子邮件是系统功能必须的一部分。Java Mail API 使得这一处理过程相对直截了当,但是客户的应用程序必须要了解许多配置的详细情况(包括用来发送消息的SMTP主机名字)。

Tomcat 5包括一个产生javax.mail.Session实例的标准资源工厂,它已经与被server.xml配置过的SMTP服务器连接好了。通过这种办法,程序完全和电子邮件服务器配置环境中的变化分隔开了——在任何需要的时候,只要请求和接受一个预先配置过的session就行了。

这个过程必需的步骤概括如下:

声明你的资源要求

你要做的第一件事就是修改网络程序部署描述符(/WEB-INF/web.xml)来声明JNDI名称,在这个名称下你可查看预先配置过的sessions。??????根据习惯性,所有这样的名称都必须围绕着邮件的subcontext而派生(与标准的java:comp/env naming context 相关,这个naming context是所有的资源工厂的根)。 一个典型的web.xml看起来象这样:

<resource-ref> 
<description> 
Resource reference to a factory for javax.mail.Session 
instances that may be used for sending electronic mail 
messages, preconfigured to connect to the appropriate 
SMTP server. 
</description> 
<res-ref-name> 
mail/Session 
</res-ref-name> 
<res-type> 
javax.mail.Session 
</res-type> 
<res-auth> 
Container 
</res-auth> 
</resource-ref>

警告——一定要尊重元素的排列顺序,这是网络程序部署描述符DTD所要求的。详细信息请参看Servlet 规范

编写使用这个资源的程序

一个典型的使用这个资源索引的例子看起来象这样:

Context initCtx = new InitialContext(); 
Context envCtx = (Context) initCtx.lookup("java:comp/env"); 
Session session = (Session) envCtx.lookup("mail/Session"); 

Message message = new MimeMessage(session); 
message.setFrom(new InternetAddress(request.getParameter("from")); 
InternetAddress to[] = new InternetAddress[1]; 
to[0] = new InternetAddress(request.getParameter("to")); 
message.setRecipients(Message.RecipientType.TO, to); 
message.setSubject(request.getParameter("subject")); 
message.setContent(request.getParameter("content"), "text/plain"); 
Transport.send(message);

注意,这个程序使用的是与网络程序部署描述符里声明的资源索引相同的名字。这是与在$CATALINA_HOME/conf/server.xml 里配置的资源工厂相映射的,描述如下:

配置Tomcat的资源工厂

要配置Tomcat的资源工厂,把这样一个元素加入到$CATALINA_HOME/conf/server.xml 文件里边,再把它嵌套在这个网络程序的Context元素里(或者嵌套在一个DefaultContext元素for the surrounding <Host> or <Engine> element.?????)  DefaultContext  <Host>

<Context ...> 
... 
<Resource name="mail/Session" auth="Container" 
type="javax.mail.Session"/> 
<ResourceParams name="mail/Session"> 
<parameter> 
<name>mail.smtp.host</name> 
<value>localhost</value> 
</parameter> 
</ResourceParams> 
... 
</Context>

注意,资源的名字(这里是mail/Session)必须与网络程序部署描述符里指定的值相映射。客户化(自己指定)mail.smtp.host参数值,让它指向为你的网络(network)提供SMTP服务的服务器那里。

应用程序示例(Example Application)

这个Tomcat包含的/examples 程序里有一个利用这一资源工厂的例子。你可通过"JSP Examples"链连来访问。实际上发送邮件的这个servlet的源代码是在/WEB-INF/classes/SendMailServlet.java里面。

警告——默认的配置假设在局部主机的端口25(port 25)有一个SMTP服务器的列单。如果不是这样,去编辑一下$CATALINA_HOME/conf/server.xml文件,把mail.smtp.host的参数值修改成你的网络SMTP服务器的主机名字。

JDBC Data Sources

简介

许多网络程序需要通过JDBC驱动来访问数据库,从而来支持这个程序必需的功能。J2EE Platform Specification 要求J2EE应用服务器让数据源实施(DataSource implementation) 成为可能(那就是,让数据源实施作为JDBC连接的连接池)。Tomcat 5提供完全一样的支持,这样你在Tomcat上开发的基于数据库的(database-based)程序,要使用这个服务,可以在任何J2EE服务器上运行而不需任何更改。

关于JDBC的信息,你应该去参考下列链连:

注意——Tomcat里支持的默认数据源是基于Jakarta Commons分项目的DBCP连接池的。然而,也有可能通过使用其他实施javax.sql.DataSource的连接池来编写你自己的资源工厂,描述如下

安装你的JDBC驱动

要使用JDBC Data Sources JNDI 资源工厂,你必须让适当的JDBC驱动既能被Tomcat内部的类使用,又能被你的网络程序使用。最容易的办法是在$CATALINA_HOME/common/lib目录里边安装驱动的JAR文件,这样就让这个驱动既能被资源工厂使用,又能被你的程序使用。

声明你的资源要求

下一步,修改网络程序部署描述符(/WEB-INF/web.xml)来声明JNDI名称,在这个名称下你可查看预先配置过的data source。??????根据习惯性,所有这样的名称都必须围绕着jdbc subcontext 而派生(与标准的java:comp/env naming context 相关,这个naming context是所有的资源工厂的根)。 一个典型的web.xml看起来象这样:

<resource-ref> 
<description> 
Resource reference to a factory for java.sql.Connection 
instances that may be used for talking to a particular 
database that is configured in the server.xml file. 
</description> 
<res-ref-name> 
jdbc/EmployeeDB 
</res-ref-name> 
<res-type> 
javax.sql.DataSource 
</res-type> 
<res-auth> 
Container 
</res-auth> 
</resource-ref>

警告——一定要尊重元素的排列顺序,这是网络程序部署描述符DTD所要求的。详细信息请参看Servlet 规范

编写使用这个资源的程序

一个典型的使用这个资源索引的例子看起来象这样:

Context initCtx = new InitialContext(); 
Context envCtx = (Context) initCtx.lookup("java:comp/env"); 
DataSource ds = (DataSource) 
envCtx.lookup("jdbc/EmployeeDB"); 

Connection conn = ds.getConnection(); 
... use this connection to access the database ... 
conn.close();

注意,这个程序使用的是与网络程序部署描述符里声明的资源索引相同的名字。这是与在$CATALINA_HOME/conf/server.xml 里配置的资源工厂相映射的,描述如下:

配置Tomcat的资源工厂

要配置Tomcat的资源工厂,把这样一个元素加入到$CATALINA_HOME/conf/server.xml 文件里边,再把它嵌套在这个网络程序的Context元素里(或者嵌套在一个DefaultContext元素for the surrounding <Host> or <Engine> element.?????)  DefaultContext  <Host>

<Context ...> 
... 
<Resource name="jdbc/EmployeeDB" auth="Container" 
type="javax.sql.DataSource"/> 
<ResourceParams name="jdbc/EmployeeDB"> 
<parameter> 
<name>username</name> 
<value>dbusername</value> 
</parameter> 
<parameter> 
<name>password</name> 
<value>dbpassword</value> 
</parameter> 
<parameter> 
<name>driverClassName</name> 
<value>org.hsql.jdbcDriver</value> 
</parameter> 
<parameter> 
<name>url</name> 
<value>jdbc:HypersonicSQL:database</value> 
</parameter> 
<parameter> 
<name>maxActive</name> 
<value>8</value> 
</parameter> 
<parameter> 
<name>maxIdle</name> 
<value>4</value> 
</parameter> 
</ResourceParams> 
... 
</Context>

注意,这个资源的名字(这里是jdbc/EmployeeDB )必须与网络程序部署描述符里指定的值相映射。

这个例子假设你使用HypersonicSQL数据库JDBC驱动。客户化(自己指定)driverClassNamedriverName这两个参数,让它们与你实际使用的JDBC驱动和连接URL相映射。

Tomcat的标准数据源资源工厂(org.apache.naming.factory.DbcpDataSourceFactory)的配置属性如下:

  • driverClassName - 被使用的JDBC驱动的完全合格的Java类名。
  • maxActive - 可以同时从这个pool被分配的最大active实例数目。
  • maxIdle - 可以同时在这个pool闲滞的最大连接数目。
  • maxWait - 这个pool在抛出异常之前可以等待连接返回的最长milliseconds 。
  • password - 将要传递给JDBC驱动的数据库密码
  • url - 将要传递给JDBC驱动的连接URL(因为向后兼容性,属性的driverName也被认可)。
  • user - 将要传递给JDBC驱动的数据库用户名。
  • validationQuery - 在连接被返回到程序之前,可以被这个pool用来验证连接用的SQL query 。 如被指定,这个query必须是一个SQL SELECT 指令,它至少有一行的返回说明。
添加客户化资源工厂

如果任何标准资源工厂都不能满足你的要求,你可以编写你自己的资源工厂,并把它整合到Tomcat 5里面,再在conf/server.xml configuration文件里配置怎样使用它。在下面的例子里,你将要产生一个资源工厂,这个工厂仅仅知道怎样产生上面所描述的Generic JavaBean Resources例子里的com.mycompany.MyBean beans 。

编写一个资源工厂类

你必须编写一个实施JNDI服务供给者 javax.naming.spi.ObjectFactory 界面的类。每次你的程序在这个工厂绑定的context entry上调用lookup() ,这个getObjectInstance()方法就会被叫到,有以下这些参数:  lookup()

  • Object obj - 包含可用于制造一个对象(object)的地址或索引信息的对象(很可能是空值)??????对Tomcat来说,这总是一个javax.naming.Reference类型的对象,它包括这个工厂类的类名,以及用来产生返回对象的配置属性(来自conf/server.xml)。
  • Name name - 这个工厂绑定的与nameCtx有关联的名字,如果没有名字被指定,它就是空值
  • Context nameCtx - 名称参数相对于这个context被指定,如果名称是相对于默认的初始context,它就是空值
  • Hashtable environment - 用来产生这个对象的环境(很可能是空值)。这通常在Tomcat对象工厂被忽略。

要制造一个知道怎样产生MyBean实例的资源工厂,你可能会产生一个象这样的类:

package com.mycompany;

import java.util.Enumeration;
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.NamingException;
import javax.naming.RefAddr;
import javax.naming.Reference;
import javax.naming.spi.ObjectFactory;

public class MyBeanFactory implements ObjectFactory {

  public Object getObjectInstance(Object obj,
      Name name, Context nameCtx, Hashtable environment)
      throws NamingException {

      // Acquire an instance of our specified bean class
      MyBean bean = new MyBean();

      // Customize the bean properties from our attributes
      Reference ref = (Reference) obj;
      Enumeration addrs = ref.getAll();
      while (addrs.hasMoreElements()) {
          RefAddr addr = (RefAddr) addrs.nextElement();
          String name = addr.getType();
          String value = (String) addr.getContent();
          if (name.equals("foo")) {
              bean.setFoo(value);
          } else if (name.equals("bar")) {
              try {
                  bean.setBar(Integer.parseInt(value));
              } catch (NumberFormatException e) {
                  throw new NamingException("Invalid 'bar' value " + value);
              }
          }
      }

      // Return the customized instance
      return (bean);

  }

}

在这个例子里,我们无条件地产生一个新的com.mycompany.MyBean类的实例,并且在<ResourceParams>元素所包含的参数基础上populating它的属性,而这个<ResourceParams>元素是配置这个工厂用的(参见下面)。你应该注意,必须跳过所有名字叫工厂的参数——这个参数是用来指定工厂类自身的名字(在这里的情况下是com.mycompany.MyBeanFactory),而不是用来指定要被配置的bean的属性。  com.mycompany.MyBeanFactory

关于ObjectFactory的更多信息,请参看JNDI 1.2 Service Provider Interface (SPI) Specification 。  JNDI 1.2 Service Provider Interface (SPI) Specification

你需要对照一个包括所有$CATALINA_HOME/common/lib 和$CATALINA_HOME/server/lib目录里的JAR文件的class path来编译这个类。当你完成以后,把这个工厂类(以及相关的bean类)以未包装形式放在$CATALINA_HOME/common/classes下面,或者放在$CATALINA_HOME/common/lib里的一个JAR文件里。通过这种方式,所需要的类文件既可以被Catalina内部资源又可以被你的网络程序使用。

声明你的资源要求

下一步,修改你的网络程序部署描述符( /WEB-INF/web.xml ) 来声明JNDI名字,在这个名字下你将要求产生这个bean的新的实例。最简单的方法是象这样来使用<resource-env-ref>元素:??????

<resource-env-ref> 
<description> 
Object factory for MyBean instances. 
</description> 
<resource-env-ref-name> 
bean/MyBeanFactory 
</resource-env-ref-name> 
<resource-env-ref-type> 
com.mycompany.MyBean 
</resource-env-ref-type> 
</resource-env-ref>

警告——一定要尊重元素的排列顺序,这是网络程序部署描述符DTD所要求的。详细信息请参看Servlet 规范

编写使用这个资源的程序

典型的使用这个资源环境索引的编码看起来象这样:

Context initCtx = new InitialContext(); 
Context envCtx = (Context) initCtx.lookup("java:comp/env"); 
MyBean bean = (MyBean) envCtx.lookup("bean/MyBeanFactory"); 

writer.println("foo = " + bean.getFoo() + ", bar = " + 
bean.getBar());

配置Tomcat的资源工厂

要配置Tomcat的资源工厂,把这样一个元素加入到$CATALINA_HOME/conf/server.xml 文件里边,再把它嵌套在这个网络程序的Context元素里(或者嵌套在一个DefaultContext元素for the surrounding <Host> or <Engine> element.?????)  DefaultContext  <Host>

<Context ...> 
... 
<Resource name="bean/MyBeanFactory" auth="Container" 
type="com.mycompany.MyBean"/> 
<ResourceParams name="bean/MyBeanFactory"> 
<parameter> 
<name>factory</name> 
<value>com.mycompany.MyBeanFactory</value> 
</parameter> 
<parameter> 
<name>bar</name> 
<value>23</value> 
</parameter> 
</ResourceParams> 
... 
</Context>

注意资源名称(这里是bean/MyBeanFactory)必须与网络程序部署描述符里指定的值相对映。我们也还要初始化bar property的值,这会在返回新的bean之前引起调用setBar(23)。因为我们没有初始化foo property(尽管我们也可以这样做),bean会包含它的构造器设置的任何默认值。  foo

你会注意到,从程序开发员的角度来看,对资源环境索引的声明,和用于请求新实例的编程,与Generic JavaBean Resources例子里使用的方法完全一样。这里举例说明了使用JNDI资源来封装功能(functionality)的一个优点——那就是,只要你保持相兼容的APIs,你可以改变下面的实施(underlying implementation)而不需要使用资源来修改程序。


Copyright © 1999-2003, Apache Software Foundation