内容


3-D 视图

使用 WebSphere Application Server 4.0 高级单服务器版设计、开发和部署 EJB

Comments

介绍

如果您对 EJB 组件感到好奇,或者想知道 Java 企业应用程序是如何工作的,那么这篇文章就很适合您。本文假设您懂得 Java 编程语言并对 J2EE API 和技术有基本的理解。EJB 1.1 指定的 Bean 提供者、应用程序装配者和部署者以及从 WebSphere Application Server 3.5 迁移到 WebSphere Application Server 4.0 的开发者可能会发现这篇文章很有用。

这个样本工程由一个实体 bean、一个会话 bean、一个样本 servlet 和一个样本客户机组成。实体 bean 代表数据库中的一张表 EMPBEAN。会话 bean 非常简单并且使用数据库连接池直接访问数据库。它还使用实体 bean 访问数据库。样本 servlet 是一个 EJB 客户机,它将结果输出到浏览器。在这个工程中有一些数据包装类和从属类用来在真实情景中演示典型的类路径问题(尽管在这个工程中使用它们是多余的)。您可以下载一个 带所有源代码的 zip 文件。

J2EE 技术

这部分将对使用 J2EE 技术进行 Java™ 企业应用程序编程作一次深入介绍,并讨论如何为企业应用程序选择不同类型的 bean。

EJB 组件是基于分布式对象技术的 Java 服务器端组件模型。这个模型非常健壮、灵活并且是独立于厂商的。它有助于快速开发业务对象(中间层),只需对代码作极小的更改,或者不作任何更改,这些业务对象就可以运行在任意 EJB 服务器(应用程序服务器/容器提供者)上。EJB 服务器提供一些基本服务,比如对象分发、命名、安全性、持久性、事务管理和并发性。因此,应用程序开发者只需集中精力对业务逻辑进行编码而不必对底层服务编码,这些底层服务由容器自动处理。

EJB 的两种基本类型是 实体 bean(表示持久数据)和 会话 bean(表示工作流或者业务流程)。

由于实体 bean 只表示数据,建议不要把工作流逻辑编码在实体 bean 中。在大多数必需实体 bean 的情况下,容器管理的持久性(CMP)实体 bean 都是最佳选择,因为容器会生成数据库访问代码,这样 EJB 开发者不必重写 bean 就能够灵活地更改 DBMS 或者表(Table)的布局。但是,这个选择会受到部署工具局限性的限制 — 部署工具将 bean 实例的状态映射到数据库,这种情况下可能会使用 bean 管理的持久性(BMP)实体 bean。

典型情况下,会话 bean 用于实现业务逻辑和封装实体 bean。它们公开一个接口,通过这个接口客户机可以访问各种 服务或者方法。无状态会话 bean 不维护任何两次方法调用(它提供的服务)之间的状态,但有状态会话 bean 维护与客户机有关的状态。会话 bean 可以从数据库直接访问数据,也可以通过调用实体 bean 的方法来访问数据。要在直接访问和间接访问之间做出一个正确的选择通常都很难。

设计注意事项

只有在需要客户机频繁查询/更新/删除数据库表中的行时,才应该把表中的行建模为实体 bean 实例。在这种情形下,维护实体 bean 所用的开销就是正当的,因为它保证数据操作逻辑(这种逻辑可以在几个工作流中重用)的安全和一致性。只有在下列情况下,这种实体 bean 才封装在会话 bean 中:

  • 有许多这种客户机使用对应用程序很有意义的特定方法执行更新、删除和查询。在这种情况下,实体 bean 顶层的会话 bean 将确保整个企业应用程序间对数据的操作统一、一致。例如,对一列的更新总是会导致更新同一张表或者其它一些表中的标志(另一列)。会话 bean 中的这个逻辑将强制客户机一直做正确的事情,确保应用程序中的一致性并防止客户端的代码重复。
  • 在工作流中有不止一个实体 bean 需要被客户机更新、删除或者查询。

每个远程方法调用都包括将数据从客户机 EJB 存根传送到服务器,然后再传送回产生网络流量的客户机 EJB 存根。网络流量和延迟会影响性能,特别是在上千个客户机与 EJB 服务器交互的大型企业应用程序中更是如此。通过减少远程方法调用可以提高应用程序的性能。一个会话 bean 访问多个实体 bean 比允许客户机进行多次远程方法调用效率要高得多,维护起来也要容易得多。

尽管使用实体 bean 有许多好处,但它们并不总是最佳解决方案。它们占用更多的内存,使用更多的 CPU 周期,并且通常情况下与无状态会话 bean 相比效率都比较低。当只需要对数据库进行一次查询或简单的更新时,让会话 bean 直接访问数据库是正确的选择。当会话 bean 直接访问数据库时,会提高系统的整体性能。事务需求也可能要求会话 bean 有直接数据库访问。这样,有时候会话 bean 应该直接访问数据库,而有时应该通过实体 bean 访问数据库。

EJB 组件

这部分讨论编写使用两个方法操作数据的会话 bean: 间接使用 CMP 实体 bean 访问数据库, 直接使用容器提供的池态数据库连接访问数据库。这部分还包括:

  • 使用 EJB 体系结构中的 EJB 引用和资源引用
  • CMP 实体 bean 开发
  • Web 应用程序开发
  • 直接访问 EJB 组件的纯 Java 客户机

CMP 实体 bean 的描述如下:

  • 远程接口是 com.entangle.entityejbs.EmpInterface
  • 主键是 com.entangle.entityejbs.Empkey
  • 本地接口 com.entangle.entityejbs.EmpHome
  • Bean 实现是 com.entangle.entityejbs.EmpBean

Emp bean 实例表示数据库表中的一行;EmpBean 是该表的名称,由部署工具生成。Emp bean 具有下列属性:

/**All of these are container managed fields
   They represent columns in the database table */	
public String firstName;
public int id;
public String job;
public String lastName;
public double salary;

在这个 bean 类中,大多数方法(EJB 回调方法除外)都是上述属性的 accessor 和 setter。但是,有一个业务方法的返回类型为 EmployeeRec,如下所示。

/**This method returns a wrapper object that has all the      		    
properties of the bean class. This is more efficient than
using accessors for each property */
public EmployeeRec getEmployeeRec() {
return new EmployeeRec(id, firstName, lastName, job, salary);
}

这个实现中没有一个方法抛出 RemoteException 异常。EJB 1.1 规范引入了 EJBException(一个 RuntimeException),必须抛出它指出已经发生了系统异常或者非应用程序异常,当前事务如果有的话,就需要回滚。定义了方法 ejbCreate() 来返回一个主键而不是 void 值。

Emp bean 的远程接口只定义了已实现的业务方法,而本地接口按 EJB 1.1 规范的描述定义了本地方法。

会话 bean 的第一个方法

样本会话 bean 是一个无状态会话 bean,它有两个 bean 方法。第一个方法 findEmployeeRec,使用一个参数并简单地调用实体 bean 的一个方法来获取 EmployeeRec 包装器类型。这个方法通过上下文对象查找 JNDIREFNAMES.EMP。 JNDIREFNAMES 是一个实用程序类,它拥有引用的各种类型的值。

将 JNDI 名赋给 bean 是部署者的责任,而不是 bean 开发者的责任。除非 bean 提供者同时还担当装配者和部署者的角色,否则作为一个 bean 提供者,它无法知道外部资源(另一个 EJB、数据源、环境值等等)的真实 JNDI 名。

在 EJB 1.1 规范中,“JNDI 环境命名上下文”(JNDI Environment Naming Context,JNDI ENC)这个概念被作为容器 bean 合同的一部分引入。这个容器需要为每个 bean 提供 java:comp/env 命名上下文,并且,对于每种 bean 类型命名上下文必须是唯一的。有可能两个 bean 可以在它们的上下文中查找相同的字符串 java:comp/env/ejb/myejb ,并得到绑定在全局 JNDI 命名上下文中的不同类型的对象。

使用企业 bean 内的 java:comp/env 名称空间使得 bean 不必预先知道外部信息在目标操作环境中是如何命名和组织的就能够定位外部信息。使用这个命名上下文将不会把 bean 与只在部署时才知道的资源的 JNDI 名密切联系在一起。

EJB 1.1 规范推荐(但不要求)把所有的 EJB 引用都组织在 bean 环境的 EJB 子上下文中。在代码样本中,JNDIREFNAMES.EMP 的值被设置为 java:comp/env/ejb/EmpBean。

/** This method is layered on the top of the entity bean.  This is just a     
very simple method that invokes a method on the entity bean.  The value of 
EMP is java:comp/env/ejb/EmpBean.  */ 
public EmployeeRec findEmployeeRec(int id)
 { 
   Context ctx = null;
   EmployeeRec empRec = null;
   try
   {
   	ctx = new InitialContext();
   	Object obj = ctx.lookup(JNDIREFNAMES.EMP);
   	EmpHome empHome = (EmpHome) 
		PortableRemoteObject.narrow(obj, EmpHome.class);
    ........
    ........
 }

会话 bean 的第二个方法

第二个方法演示了使用池态数据库连接直接访问数据库。必须先在服务器环境中创建和配置了数据源,企业 bean 才可以访问池态数据库连接。bean 方法在上下文中查找 JNDIREFNAMES.SAMPLEJDBC。这个变量的值是 java:comp/env/jdbc/sample。这是一个逻辑名,在部署阶段可以绑定到一个 JNDI 数据源对象。在为企业 bean 编码时不需要知道实际的 JNDI 名。

/** This method in the session bean directly accesses the database
    taking advantage of the jdbc database connection pool maintained
    by the Application Server Environment.
    The value of SAMPLEJDBC is  "java:comp/env/jdbc/sample"
    The value of SELECT_FROM_EMP is 
    "select firstnme from employee where empno = ?";
*/
public String getEmployeeName(String empno)
 {
     Context ctx = null;
     DataSource ds = null;
     Connection conn = null;
     PreparedStatement ps = null;
     ResultSet result = null;
     String empName = null;
 	System.out.println("AccessDataBean: getEmployeeName for: " + empno);
 	try
 	{
 	   ctx = new InitialContext();
	   ds = (DataSource) ctx.lookup(JNDIREFNAMES.SAMPLEJDBC);
	   conn = ds.getConnection();
        ps = conn.prepareStatement(AllSqlStatements.SELECT_FROM_EMP);
             
        .......
        .......
  }

为成功地执行这个方法,数据源引用的数据库或模式名称必须包含一张名为“employee”的表,这张表列中的名字必须包含“firstname”和“empno”。缺省的样本 DB2 UDB 数据库满足这个标准。

样本 Web 应用程序

样本 Web 应用程序工程只有一个 servlet(com.entagle.sampleservlets.AccessServlet)和一个 HTML 文件。这个 servlet 使用 EJB 引用查找会话 bean。该 Web 应用程序有一个引用 AccessServlet 的 servlet 映射,“getdata”。该应用程序的上下文根路径被设置为 /data 。EnterInput.html 被添加到 WAR 文件的资源文件,同时它也是应用程序的一个 欢迎(welcome) 文件。欢迎文件是指向帮您完成 URL 的应用程序的入口点。例如,当用户请求 http://<hostname>/data/ 时,Web 服务器将其重定向到 http://<hostname>/data/EnterInput.html。

如果 servlet 要直接访问数据库,那么代码就与上面会话 bean 中的代码非常相似。这种情况下,servlet 查找 EJB 并进行远程方法调用,象下面这样操作数据。

/** This method looks up the enterprise bean (session bean) using a 
    Java:comp/env/ejb/AccessDataBean (Value of ACCESSDATAEJB)
*/
public void doGet( HttpServletRequest req, HttpServletResponse res )
		throws ServletException, IOException
 {
  ......
  ......
  try 
  { 
  	Context ctx = new InitialContext();
      Object obj = ctx.lookup(JNDIREFNAMES.ACCESSDATAEJB);
      AccessDataHome home = 
		(AccessDataHome) PortableRemoteObject.narrow(obj,
			 AccessDataHome.class);
      ......
      ......
 }

一个客户机应用程序

纯 Java 客户机(独立的 Java 应用程序)也可以访问 EJB 组件。这些客户机使用 RMI/IIOP 与应用程序服务器环境交谈。WebSphere Application Server 4.0 提供了一个 J2EE 客户机容器(您可以用它访问 EJB),还提供了一个 J2EE 环境,这样客户机就可以访问 java:comp/env 名称空间。每个 J2EE 客户机都有一个 XML 部署描述符(根据 EJB 1.1 规范),它把所有类型的引用都映射到适当的 JNDI 名。随 WebSphere Application Server 版本 4.0 一起提供的 launchclient.bat 是一个很方便的命令行实用程序,您可以用它来运行 J2EE Java 客户机。

java 命令也可以用于运行访问 EJB 的独立 Java 应用程序。但是,所有使用代码中的 Context.lookup 访问的资源必须指定真实的 JNDI 名。这些包括 usertransaction 对象、各种类型的资源工厂、EJB 等等。这种情况下不需要任何部署描述符。所有必需的 JAR 文件必须都在类路径(操作系统环境变量)中或者都在 java 命令行上被指定。样本工程讨论了这种类型的一个 J2EE 客户机。

部署和配置

这部分讨论了在 WebSphere Application Server 4.0 中成功运行企业应用程序所需的配置。配置包括设计注意事项,比如:

  • 把从属类放在什么地方
  • 在 EJB jar 文件中包含什么内容
  • 如何设置 EAR 文件的类路径、AAT、SEAppInstall 工具和管理控制台

样本工程具有下列类:

位置
有状态会话 bean在 com.entangle.sessionejbs 包中:AccessData(远程接口)、AccessDataHome(本地接口)和 AccessDataBean(实现类)。
CMP 实体 bean在 com.entangle.entityejbs 包中:EmpInterface(远程接口)、EmpHome(本地接口)、EmpKey(主键类)和 EmpBean(实现类)。
公共类在属于这个企业应用程序的多个实体 EJB 和会话 EJB 间使用,位于 com.entangle.common 包中:EmployeeRec(一个包装器类)。归档在 commonclasses.jar 文件中。
SQL 助手类在 com.entangle.sql 包中是 AllSqlStatements。归档在 sqlclasses.jar 文件中。
实用程序类有可能被多个 EAR 文件公用,在 com.entangle.util 包中。它们是 JNDIRefNames(为所有 EJB 引用、资源引用等存储的)和 FullName。归档在 utilclasses.jar 文件中。

类路径

有了类路径,您必须下一个重要决定:哪儿是放置各种包含从属类的 JAR 文件的最好地方?在我们的样本工程中,应用程序要正确运行必需 commonclasses.jar、sqlclasses.jar 和 utilclasses.jar 文件。可以用不同的方法指定从属 JAR 文件。

指定从属 JAR 文件的一种方法是把它们放在 EAR 文件的最顶层并在 EJB 模块/WAR 模块的类路径中指定它。这个决定应该根据从属类 JAR 文件是不是 EAR 文件/企业应用程序的一个集成部分。在这个样本工程中,commonclasses.jar 和 sqlclasses.jar 文件是这个企业应用程序的一个集成部分,因为它们不在多个企业应用程序间共享。因此,commonclasses.jar 和 sqlclasses.jar 文件被放在 EAR 文件中。请参阅 图 1了解更多的详细信息。

下面的 图 1显示 commonclasses.jar 和 sqlclasses.jar 被放在了 EAR 文件的最顶层。选择 Files节点,然后右键单击鼠标从菜单选择 Add可以将文件添加到这个区域。

图 1. commonclasses.jar 和 sqlclasses.jar 文件
AAT 公共类

AAT 公共类

指定从属 JAR 文件的另一种方法是把它们放在从属类路径和 Java 虚拟机(JVM) 类路径中。假定样本中的 utilclasses.jar 文件将要被多个企业应用程序/EAR 共享,所以这个 JAR 文件在部署阶段被放在从属类路径中,同时也被放在 JVM 类路径下。请参阅 图 2图 3 了解详细信息。设置了 JVM 类路径以确保在运行时可以识别 utilclasses.jar 中的类文件。

图 2显示如何在生成部署代码过程中设置从属类路径。

图 2. 设置从属类路径
AAT 从属类路径

AAT 从属类路径

图 3显示在何处设置应用程序服务器的 JVM 类路径。它必须包含 utilclasses.jar 文件,这样应用程序服务器在运行时才能找到必需的类。

图 3. 设置 JVM 类路径
JVM 类路径设置

JVM 类路径设置

在 EJB 模块或 WAR 模块的类路径中,必须用空格将 JAR 文件分开,如 图 4图 5 所示。

图 4显示类路径是通过空格分开的 EJB JAR 模块的 JAR 文件来设置的。这个类路径将会在 EJB JAR 文件的清单文件中。

图 4. 设置一个类路径
AAT EJB 模块 classpath

AAT EJB 模块 classpath

由于 commonclasses.jar 文件也被 WAR 模块共享,所以必须将它放在 WAR 模块类路径中。由于其中一个 Web 组件访问 EJB,所以除所有的从属 JAR 文件之外,EJB JAR 文件也必须放在 Web 模块类路径中。请参阅 图 5 了解详细信息。

图 5显示 Web 模块的类路径设置。EntangleApp5.jar(EJB JAR 文件)和 commonclasses.jar 文件被用空格分开了。Web 模块的 Context root 被设置为 /data

图 5. Web 模块的类路径设置
AAT Web 模块 classpath

AAT Web 模块 classpath

应用程序装配工具(AAT)的功能

AAT 使用 Java 的反射 API 探测远程接口和本地接口类。如果这个工具找不到这样一个类(这个类是属于远程或本机接口的其中一个方法的参数或返回类型),该工具就无法在那个特定的 EJB 下的“Method Extensions”节点中显示该方法(它包含一个未知类作为参数或者返回类型)。所以,就无法为这类问题方法设置隔离级别(isolation level)、访问目的(access intent)、安全性(security)和方法许可(method permission)等这些部署描述符属性。

在 AAT 的 Create new bean对话框中,如果您使用 Browse 按钮选择了本地接口,就会自动填充远程接口和 bean 类。即使那些名称与正确的远程接口和 bean 类相匹配,也必须重新浏览这些文件。这样可以确保 AAT 拷贝 EJB JAR 文件中的文件。

AAT 生成部署代码时,它在三个地方查找从属类:

  1. 启动 AAT 的批处理文件中 java 命令的类路径(-classpath 命令行参数)。
  2. EJB JAR 模块或 Web 模块的类路径( 图 4图 5)。
  3. 部署期间指定的从属类路径( 图 2)。

在样本工程中,类 FullName 在 utilclasses.jar 中,同时也是 EmpInterface EJB 中其中一个远程接口的返回类型。这个类除说明类路径问题外,不作它用。

/** The method getFullName() is in the remote interface  */
package com.entangle.entityejbs;
import com.entangle.common.*;
import java.rmi.*;
import javax.ejb.*;
import com.entangle.util.*;
public interface EmpInterface extends javax.ejb.EJBObject 
{
 ......
 ......
 /** This method returns a dependent class FullName that is
   in the package com.entangle.util */
   public FullName getFullName() throws RemoteException;
 ......
 ......
}

在样本工程中,为确保 AAT 能找到类 FullName ,在启动 AAT 的 Java 程序的类路径中指定了 utilclasses.jar 文件(上面列表中的 1 号)。如果该 Java 程序的类路径命令行参数中没指定这个类,那么启动 AAT 的命令窗口中将出现下面的错误: Could not reflect methods for com.entangle.entityejbs.EmpBean because one of the classes could not be loaded. Com/entangle/util/FullName 。注意,即使 utilclasses.jar 文件放在 EAR 文件中并且为 EJB JAR 文件设置了类路径,这种错误也会发生。要记住的关键一点是 EJB JAR 文件或 Web 模块中指定的类路径在部署阶段之前是无效的。

如果一个未知的类只在实现类(EJB 的 bean 类)中被引用,作为返回对象或者在代码中,那么 AAT 工具将不检查这个类。这些未知类就会在运行时被应用程序服务器检查,并且必须放在应用程序服务器的 JVM 设置中( 图 3)。

如果未知的类在远程接口中,并且 AAT 在 AAT 的类路径命令行或者 EAR 文件类路径中找不到该类,该工具(AAT)将在生成部署代码期间报错(RMIC Failed)。

这个问题的变通方法将是在 EJB JAR 文件下的 Files 节点中包含进 FullName 类。但这样做不适合我们的样本代码当前的设计。(请记住,utilclasses.jar 被设计为可以在多个企业应用程序内使用。此外,这样做还要撤销归档)。

使用 SEAppInstall 和 Web 管理员控制台进行安装

管理员控制台安装过程将不允许重新安装应用程序。为重新安装应用程序,必须启动服务器两次 — 一次是在卸载之后,一次是在安装之后。另一方面,SEAppInstall 命令行却允许重新安装应用程序。

使用管理员控制台安装之后,必须保存配置并重新生成插件配置。选择 Nodes> < hostname>> Application Servers> Default Server,并遵循下列指示可以重新生成插件信息。插件信息会动态生成,并且不需要重启服务器。

SEAppInstall 将保存服务器配置,但必须重新生成插件。您可以使用命令行脚本 GenPluginCfg.bat 重新生成插件信息。如果 SEAppInstall 被用于为样本工程生成已部署的代码,请确保启动 SEAppInstall 的 Java 程序的类路径包含 utilclasses.jar 文件。

另一个客户机应用程序

下面是一个访问 Emp bean 的简单 Java 客户机的代码样本。请注意,这儿使用的是真实的 JNDI 名而不是 java:comp/env 。当 Java 客户机在 J2EE 客户机容器之外运行时必须这样。

/* Make sure that PROVIDER_URL, JNDI name has been changed
   to match the real IP and the global
   JNDI name of the EMPBEAN */ 
package com.entangle.javaclient;
...........
public class EmpClient
{
  public static EmpInterface getEJB()
 {
  Properties prop = new Properties();
  Prop.put(javax.naming.Context.PROVIDER_URL, 
 					"iiop://<Real IP >:900/");
  .....
  .....
    Object obj = ctx.lookup("<JNDI NAME OF EMPBEAN>");
  ......
}

这个 Java 客户机应用程序类也可以包含在 J2EE 模块(EAR 文件)中,这种情况下可以使用逻辑名(引用)。launchClient.bat 脚本将提供 J2EE 客户机环境,并且可用于执行 EAR 文件内的 Java 客户机应用程序。

总结

WebSphere Application Server 4.0 高级版和一些工具(比如 AAT 和 SEAppInstall)一起,推动了可伸缩且有容错能力的 J2EE 应用程序的快速开发。J2EE 是一个非常健壮且流行的标准,适用于多层企业应用程序。Application Server 的进展超过了 J2EE 1.2.1 认证,因此它支持最新的 Java 服务器技术。理解 J2EE 1.2.1 体系结构的新工具和新功能将有助于您驾驭 WebSphere Application Server 的强大功能。


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=WebSphere
ArticleID=55310
ArticleTitle=3-D 视图
publish-date=11012001