From
 
 

构建生产质量 EAR 文件的最佳实践

作者:Hussein Badakhchani
02/13/2008

摘要

在本文中,我将介绍封装和部署 Java EE 应用程序的可行方案。首先,我将回顾与应用程序组装及部署有关的 Java EE 5 规范。 然后,我将论述生产 EAR 文件的封装规范并回顾一些实现和执行技术。
本文的目标读者为那些负责创建或执行标准的人员,以及那些经常需要调试应用程序部署的人员。 因此,主要读者会是 BEA WebLogic 管理员;但是,任何希望确保应用程序部署运行顺畅的人都将从本文中获益。

简介

封装应用程序以便在生产服务器上部署是 Java EE 项目的一个组成部分。随着越来越多的项目在国外开发,企业对清晰且合理的封装规范的需求已经变得更加重要。没有正确封装的应用程序会导致细微的错误,迫使专业人员花费很长时间分析和解决。这些问题通常是间歇性的 — 例如,不同的部署机制(有一些适用于 WebLogic Server)可能掩盖部署错误,也可能触发部署错误。
由于人们普遍忽视因封装不好和缺少清晰的错误消息所引起的问题的严重性,这些问题也演变得更加复杂。迫于完成应用程序部署的压力,部署失败的原因时常被人们忽视。好的 WebLogic 管理员会找到一种变通方法来完成部署,即使这意味着需要重新部署应用程序。然而,如果没有确定失败的根源并加以解决,那么在后面的测试阶段还会出现相同的问题,这样解决起来可能更费时、代价更高,同时还会增加无法按时交付项目的风险。在最坏的情况下,人们甚至认为方案部署失败不是缺陷,但只接受失败是应用程序行为的一部分。

Java EE 应用程序组装和部署

在开始有关部署的 Java EE 5 规范的细节之前,需要理解规范的目的。该规范的第 8 章“Application Assembly and Deployment”清楚地说明了其目的:“本章指定了组装、封装和部署 Java EE 应用程序的 Java 平台企业版 (Java EE) 要求。这些要求的主要目的是将可伸缩的模块化应用程序组装和可移植的 Java EE 应用程序部署提供到任何 Java EE 产品中。”
一般而言,规范涉及平衡各涉众之间的需求,可以通过平台角色识别它们,例如,部署人员、系统管理员和工具提供者。 这种平衡作用,或者说得不好听一点就是妥协,向最终用户提供了一些灵活性,但其代价是增加了成本。因为封装细节由用户决定,所以不同应用程序的封装可能会有极大的差异,特别是那些由不顾一切的项目团队创建的应用程序。我们需要熟悉规范以确保必要时严格遵守规范并证明我们施加于它的各种限制是正确的。

Java EE 应用程序

Java EE 应用程序由最多四种 Java EE 模块和一个可选的应用程序部署描述符组成。这些模块包括:
  1. 企业 JavaBean (EJB) 模块
  2. Web 应用程序模块
  3. 应用程序客户端模块
  4. 资源适配器模块
与它们的父应用程序组件一样,这些模块所拥有的目录结构和部署描述符符合所有 Java EE 容器的预期。还有一个选项可包含特定于容器的部署描述符。例如,Web 应用程序模块可以包含一个 weblogic.xml 部署描述符。以下图给出了三个最常见的应用程序组件的标准结构和部署描述符:EAR、WAR 和 EJB。
EAR文件打包最佳实践_打包
EAR文件打包最佳实践_文件_02
EAR文件打包最佳实践_实践_03
图 1. EAR 文件结构 图 2. WAR 文件结构 图 3. EJB 文件结构

为生产环境封装最佳实践

将应用程序部署到生产环境时,安全、性能和操作的敏捷性是最重要的考量。 WebLogic 管理员需要能够快速地部署应用程序而且尽可能少的人工干预。 无需修改代码即可满足这些要求的方法是,使用部署描述符和应用服务器配置的组合并且在所有项目上准确、一致地封装应用程序。
下面我们将回顾一个相当优秀的封装最佳实践,它可确保将应用程序正确部署到 WebLogic 域。 当然,应用程序部署后实际上是否工作就是开发人员的事情了。
显然,并非所有规范都适用于您的组织,而且您对它们也许并不都赞成;重要的是在第一个实例中有一些封装规范并能够证明它们。

PS001:以开放格式部署应用程序

以开放格式部署应用程序提供了以下好处:
  1. WebLogic 能够更快地部署应用程序并且使用更少的资源(相对于存档格式)。这提高了操作的敏捷性。
  2. 它允许应用程序支持团队更加方便快速地对配置文件进行更改(相对于存档格式)。
如果您的应用程序的部署时间是服务宕机的一个因素,那么当容器解压缩每个应用程序组件而消耗了大部分部署时间的时候,通过开发格式进行部署可以降低 SLA 违规的风险。如果您定期向服务器部署大量独立的应用程序或大型应用程序包,这种好处就会放大。另一方面,如果应用程序部署没有导致任何宕机,或只是偶尔有之,或者您拥有 JAR 文件的签署策略,则不必考虑使用这个实践。

PS002:EAR 中没有重复的 JAR 文件

WebLogic 遵循一种委托类加载模型。加载类的顺序如下所示:
  1. 系统类路径加载器
  2. WebLogic 服务器类路径加载器
  3. 应用程序类路径加载器
  4. EJB 类路径加载器
  5. Webapp 类路径
这意味着位于 APP-INF/lib 中的 JAR 文件对于 Web 应用程序将是可见的。APP-INF/libWEB-INF/lib 中都不应该存在 JAR 文件。这增加了可交付程序的大小、增加了部署时间,并且可能会造成类加载问题和部署失败。
理论上,所有 JAR 文件都应该只在 APP-INF/lib 中定义,除非迫切需要为 EAR 文件中的不同模块部署不同版本的 JAR 文件。
有关更多信息,请参见 WebLogic Server 的类加载机制。

PS003:EAR 中不得包含 JSP、未处理的/未编译的批注和源文件

在生产环境中使用编译器配置 WebLogic 服务器是一个安全隐患,同时在系统或 WebLogic 类路径中包括 tools.jar 也是不安全的。使用预编译的构件消除了配置编译器的需要,也减少了应用程序初始化时间,改善了用户初次访问时感觉到的应用程序响应时间。确保所有构件在构建时进行编译也就意味着在编译时(而不是应用程序已经部署后)识别和修复错误。
由于这些原因,EAR 中的所有构件必须在完全编译之后才能进行部署。必须使用与您的项目所使用的 WebLogic 的版本兼容的最新版本的 BEA 的 Appc 工具编译 JSP。必须使用项目所使用的 JDK 版本提供的该版本的 apt 处理和编译批注。
有趣的是,这个实践的一个反对意见来自于一位坚持不编译批注的开发人员。我让他访问 Sun 的 apt 工具 Web 站点,那里写明“apt 首先运行批注处理器,该处理器可以产生新的源代码和其他文件。接下来,apt 将导致对原始文件和生成的文件进行编译,这就简化了开发过程。”有些开发人员书生气十足,令人好笑。澄清一下,编译或处理的语义学会令人分心。问题是要确保只有版本控制的二进制文件被部署到运行的服务器。
我已经在做几个项目,其中 tools.jar 被留在类路径上,WebLogic Server 控制台应用程序本身如果没有正确处理的话,则和它有一个依赖关系。WebLogic Server 9 用户应该知道这一点。

PS004:由系统或 WebLogic 类路径提供的 JAR 文件不应包括在 EAR 中

如果这些类加载器上的 JAR 文件被基础架构团队升级的话,包括在应用程序中的 WebLogic 类路径或系统上的 JAR 文件可能导致会类加载问题。它还会增加可交付程序的大小和部署时间(参见 PS001 和 PS002)。如果您曾经不得不将 JAR 文件部署到系统类路径上,那么您应该确保这样做有正当理由。在系统类路径上管理 JAR 文件是一种开销,因此最好避免如此。

PS005:确保所有 EAR 文件都有一个 weblogic-application.xml 描述符。

使用 WebLogic 扩展的应用程序必须包括一个 weblogic-application.xml 描述符。如果 EAR 中包括 Web 应用程序模块,则必须显式定义 webapp.encoding.defaultwebapp.encoding.usevmdefault 以确保应用程序的可移植性,并消除应用程序使用的字符编码的任何模糊性。尽管在 Java EE 规范中不提供特定于容器的部署描述符是完全可以接受的,但是在实践中省略这些描述符可能会在容器的部署代码中暴露错误。
当我看到没有包含特定于容器的部署描述符的应用程序时,这就表明了应用程序没有完成或者缺少对于如何以及在哪里部署应用程序的关注。

PS006:确保所有 WAR 文件都包括一个 weblogic.xml 描述符

weblogic.xml 部署描述符至少应该在以下元素中包含有效、非空的变量:
  • description
  • weblogic-version
  • jsp-descriptor
  • page-check-seconds = -1
  • precompile = false
  • encoding = the applications encoding
  • package-prefix = jsp_servlet
对于在 WAR 文件中包含一个 weblogic.xml 部署描述符的抵制总是令我吃惊。在向开发人员和他们的管理者解释为何包含此文件是一个不错的做法的时候,他们完全可以从 WebLogic 的例子中复制一个文件到源控制并部署应用程序。最终,未包含 weblogic.xml 部署描述符会导致一些我所见过的、接触过的所有版本的 WebLogic Server 的、最难以解决且代价高昂的部署失败。
在我最近遇到的一个案例中,某个应用程序并未附带 weblogic.xml 部署描述符。然而,当应用程序需要在开发人员自己 PC 以外的机器上运行的时候,就需要正确定义 security-role-assignment 元素。显然,该缺陷与应用程序有关,但它却是可能破坏部署这个问题的一个好例子。在许多组织中,没有理由假设负责应用程序部署的 WebLogic 管理员知道该元素需要的值应该是什么。然而,如果错误消息中包含了部署描述符,也许会为管理员提供一个诊断此问题的机会。

PS007:确保所有 EJB 文件都包括一个 weblogic-ejb-jar.xml 描述符

weblogic-ejb-jar.xml 部署描述符至少应该在以下元素中包含有效、非空的变量:
  • description
  • ejb-name
另外,原因与 PS005 和 PS006 相同,即便您的应用程序无需任何特定于容器的特性,包含特定于容器的部署描述符也是一个不错的做法。

PS008:EAR 文件中不能包含构建构件

build.xmlpom.xml 文件等构建构件必须从所有可部署包中滤除。此规范确实是一个良好的管理措施;不用部署任何超出您需要的东西。在一个程序包中查找 build.xml 文件和 pom.xml 文件是一个散乱的构建过程。如果您的可交付程序将要发送给第三方,则其中还存在一个实际的安全问题。

PS009:EAR 中不能包含测试构件

所有测试构件都应该从可部署程序包中删除,例如,JUnit 系列的 JAR 文件以及使用它们的测试用例。还有,不要部署任何多余的东西,满足自己的需求即可。包括这些文件会不必要地增加应用程序包的大小,使结构变得散乱。

PS010:EAR 中不能包含静态内容

在使用代理 Web 服务器提供静态内容的环境中,应删除 EAR 文件中的静态内容。这样可以确保快速检测出基础架构堆栈的错误配置和错误代码并加以解决,以允许 WebLogic 提供静态内容。如果您的应用程序使用大量静态内容,由于它会对部署时间产生影响(参见 PS001),这一点就尤为重要。

PS011:配置文件的命名和位置必须一致

理想情况下,存储在平面文件中的与应用程序环境相关的应用程序配置应该存储在应用程序部署描述符中。如果应用程序确实要求使用特定属性文件来存储配置信息,这些文件的命名和位置必须保持一致。稍后,我将在实现指导中详细阐述这一点。与此有关的一点是,我将尽量减少使用 Java 命令行中指定的环境参数来配置应用程序。

PS012:EAR 中不能包含容器外部的 JAR 文件

部署在生产基础架构上的任何 EAR 都不能使用应用程序容器外部实现中的 JAR 文件。发现在容器内运行代码困难的开发人员会使用容器外测试。这种做法的一个令人遗憾的结果就是,容器外实现使用的 JAR(提供 Java EE 标准的接口和实现类 — 例如,JTA 和 servlet 规范 JAR)包含在应用程序包中。部署应用程序之后,这会导致各种各样的类加载问题 — 特别是如果应用程序试图管理自己的类加载。

PKS013:禁止使用 MANIFEST.MF 文件中的 Class-Path 头加载库

在 JAR 的 Manifest 文件中使用 Class-Path 头时,管理类路径和类加载变得过于复杂。相关的 JAR 文件应该位于应用程序的 APP-INF/lib 文件夹中或者位于 EAR 文件部署描述符的 library-directory 元素定义所指定的目录中。

PS014:EAR 名称和版本控制必须符合公司的命名法

确保应用程序 EAR 命名一致,并且符合公司的命名法、命名标准和惯例。这将简化应用程序部署和其他与应用程序 EAR 有关的流程的自动化。

PS015:EAR 中的 Web 应用程序模块必须与它们的上下文根目录同名并以 .war 为扩展名

遵循此做法,应用程序支持团队和构建工具只需通过名称便可快速确定应用程序的上下文根目录。还可以轻松地标识应用程序的静态内容。

实现指导

任何一套规范都有助于提供参考实现。 在本节中,我们将介绍如何实现已定义规范的一个子集。我们将讨论 PS003、PS005、PS006、PS007、PS010 和 PS011。

PS003:删除未编译的文件

EAR 中不能包含 JSP、未编译的批注和其他任何源文件。为了使 WebLogic 将 JSP 请求委托给经过编译的类文件,必须将以下代码片段添加到 Web 应用程序模块的 web.xml 文件中。
<servlet>
    <servlet-name>JSPClassServlet</servlet-name>    
    <servlet-class>weblogic.servlet.JSPClassServlet</servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>JSPClassServlet</servlet-name>
    <url-pattern>*.jsp</url-pattern>
</servlet-mapping>

PS005:包含 weblogic-application.xml 部署描述符。

这是一个 weblogic-application.xml 描述符示例。
<?xml version="1.0" encoding="UTF-8"?>
<weblogic-application xmlns="http://www.bea.com/ns/weblogic/90"
  xmlns:j2ee="http://java.sun.com/xml/ns/j2ee"   
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   
  xsi:schemaLocation="http://www.bea.com/ns/weblogic/90 http://www.bea.com/ns/weblogic/90/weblogic-application.xsd">
  <application-param>
    <param-name>webapp.encoding.default</param-name>
    <param-value>UTF-8</param-value>
  </application-param>
</weblogic-application>

PS006:包含 web.xml 部署描述符。

这是一个 web.xml 描述符示例。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.4"
  xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
  <description>
    The Monitor application is used by WhatsIt infrastructure for system validation testing.
  </description>
  <servlet>
    <servlet-name>JSPClassServlet</servlet-name>
      <servlet-class>weblogic.servlet.JSPClassServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>JSPClassServlet</servlet-name>
    <url-pattern>*.jsp</url-pattern>
  </servlet-mapping>
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
  <error-page>
    <error-code>500</error-code>
    <location>/error.jsp</location>
  </error-page>
  <error-page>
    <error-code>404</error-code>
    <location>/error.jsp</location>
  </error-page>
</web-app>

PS007:包含 web.xml 部署描述符。

weblogic-ejb-jar.xml 描述符示例:
<?xml version="1.0" encoding="UTF-8"?>
<weblogic-ejb-jar xmlns="http://www.bea.com/ns/weblogic/90" xmlns:j2ee="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.bea.com/ns/weblogic/90 http://www.bea.com/ns/weblogic/90/weblogic-ejb-jar.xsd"> <weblogic-enterprise-bean> <description>Example EJB</description> <ejb-name>BeanManagedAccountEJB</ejb-name> .... <weblogic-ejb-jar>

PS010:EAR 中不能包含静态内容

静态内容应该通过 JAR 文件交付,其结构如下:

<Webapp Content>
     |--css
     |--p_w_picpaths
     |--index.html
index.html 文件应该将用户重定向到 Web 应用程序模块的 web.xml 文件的 <welcome-file list> 元素的值。例如,如果 webapp 模块使用 JSF,则 index.html 应如下所示:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
  <head>
    <title>Redirect to JSF controller</title>
    <meta http-equiv="REFRESH" content="0;url=http://mydomain.com/webapp/index.faces"/>
  </head>
  <body>
  </body>
</html>
这将确保分解 JAR 文件之后,所有 Web 应用程序都将有自己静态内容,位于一个一致且可识别的目录结构中。

PS011:配置文件的名称和位置

所有 EAR 的应用程序配置都应该保存在一个名为 env.properties 的文件中。这些文件都必须位于 APP-INF/lib/classes 下。

实现封装最佳实践

具备了一组封装最佳实践和实现指导之后,应该如何来实现它们呢? 显然,您不想在每一个新构建的应用程序上花费数小时的时间。
一个检查应用程序包的封装规范符合度的好时机正是在构建应用程序之后,因此,编写一个自定义 Ant 任务或扩展 Maven 的 Verifier 插件可能是最佳解决方案。而且,请记住,临时或无限期同意让步的理由总是很充分,这将允许应用程序违犯您的规范。任何违规都应该与许可的截止日期一起记录在应用程序的版本说明中。

总结

此处详述的封装最佳实践并非说明目的 — 您应该以它们为指导实现自己的规范。 毫无疑问,这与您的方法会有一定程度的冲突,因此请确保能够验证它们。最后,如果您负责向服务器成功部署应用程序,封装规范是实现目标必不可少的工具。

资源