作者:Hussein Badakhchani
时间:2008年2月13日
封装应用程序以便在生产服务器上部署是Java EE项目的一个组成部分。随着越来越多的项目在国外开发,企业对清晰且可调整封装规范的需求已经变得更加重要。没有正确封装的应用程序会导致不十分细微的错误,迫使专业人员花费很长时间进行分析进行解决。这些问题常常是断断续续的——例如,不同的部署机制(有一些适用于WebLogic Server)可能掩盖部署错误,也可能触发部署错误。
由于人们普遍忽视因封装不好和缺少清晰的错误消息所引起的问题的严重性,这些问题也演变得更加复杂。迫于完成应用程序部署的压力,部署失败的原因时常受到人们的忽视。好的WebLogic管理员会通过一个工作区来完成部署,但这意味着需要重新部署应用程序。但是,如果没有确定失败的根源并加以解决,那么在后面的测试阶段,相同的问题还会出现,这样解决起来可能更费时、代价更高,同时还会增加无法按时交付项目的风险。在最坏的情况下,人们甚至认为方案部署失败不是是故障,并且接受它是应用程序行为的一部分。
在开始有关部署的Java EE 5规范的细节之前,需要理解规范的目的。在该规范的第8章“Application Assembly and Deployment”清楚地说明了其目的:“本章指定了组装、封装和部署Java EE应用程序的Java Platform、Enterprise Edition (Java EE)要求。这些要求的主要目的是将可伸缩的模块化应用程序组装和可移植的Java EE应用程序部署提供到任何Java EE产品中。”
一般而言,规范涉及平衡各涉众之间的需求,可以通过平台角色识别它们,例如部署人员、系统管理员和工具提供者。这种平衡作用,或者说得不好听一点就是妥协,向最终用户提供了一些灵活性,但其代价是增加了成本。因为封装细节由用户决定,所以不同应用程序的封装可能会有极大的差异,特别是对于那些不顾一切的项目团队。我们要熟悉规范以确保必要时严格遵守并调整我们施加于它的各种限制。
Java EE应用程序由最多四种Java EE模块和一个可选的应用程序部署描述符组成。这些模块包括:
1. Enterprise JavaBean(EJB)模块
2. Webapp模块
3. 应用程序客户端模块
4. Resource Adapter模块
与它们的父应用程序组件一样,这些模块所拥有的目录结构和的部署描述符符合所有Java EE容器的预期。还有一个选项可包含特定于容器的部署描述符。例如,Webapp模块能够包含一个weblogic.xml
部署描述符。下图给出了三个最常见的应用程序组件的标准结构和部署描述符:
EAR、WAR和EJB。
|
|
|
图1. EAR文件结构 |
图2. WAR文件结构 |
图3. EJB文件结构 |
将应用程序部署到生产环境时,安全、性能和操作的敏捷性是最重要的考量。WebLogic管理员需要能够快速地部署应用程序而且尽可能少的人工干预。满足这些要求并且不需要修改代码的方法是,使用部署描述符、应用服务器配置并将所有项目准确且一致地封装到应用程序中。
下面我们将回顾一个相当优秀的封装最佳实践,它可确保将应用程序正确部署到WebLogic域。当然,应用程序部署后实际上是否工作就是开发人员的事情了。
显然,并非所有的规范都适用于您的组织,而且您对它们也许并不都赞成;重要的是在第一个实例中有一些封装规范并能够调整它们。
以开放格式部署应用程序提供了以下的好处:
1. WebLogic能够更快地部署应用程序(相对于归档格式)。这提高了操作的敏捷性。
2. 它允许应用程序支持团队更加方便快速地对配置文件做出修改(相对于归档格式)。
如果您的应用程序的部署时间是服务宕机的一个因素,那么当容器解压缩每个应用程序组件而消耗了大部分部署时间的时候,通过开发格式进行部署就能够减少SLA违规的风险。如果您定期向服务器部署大量独立的应用程序或大型应用程序包,这种好处就会放大。另一方面,如果应用程序部署没有引起任何宕机,或只是偶尔有之,或您拥有JAR文件的签署策略,那么就不必考虑使用这个实践。
WebLogic遵循一种委托类加载模型。加载类的顺序如下所示:
1. 系统类路径加载器
2. WebLogic服务器类路径加载器
3. 应用程序类路径加载器
4. EJB类路径加载器
5. Webapp类路径
这意味着位于APP-INF/lib
中
的JAR文件对于Web应用程序将是可见的。APP-INF/lib
和WEB-INF/lib
中都不应该存在
JAR文件。这增加了可交付程序的大小、增加了部署时间并且可能会造成类加载问题和部署失败。
理论上,JAR文件都只应该定义在APP-INF/lib
中,除非迫切需要为
EAR文件中不同的模块部署不同版本的JAR文件。
有关更多信息,请参见WebLogic Server的 类加载 机制。
在生产环境使用编译器配置WebLogic服务器是一个安全隐患,同时在系统或WebLogic类路径上包含tools.jar
也是不安全的。使用预编译的工件消除了配置编译器的需要,也减少了应用程序初始化时间,改善了用户初次访问时感觉到的应用程序响应时间。确保所有工件在构建时被编译也就意味着在编译时间(而不是一旦应用程序已经部署)编译时错误被识别和修正。
由于这些原因,EAR中的所有工件必须在完全编译之后才能进行部署。必须使用最新版的与您的项目所使用的WebLogic的版本兼容的BEA的 Appc 工具编译JSP。必须使用项目所使用的JDK版本提供的该版本的 apt 处理和编译注释。
有趣的是,这个实践的一个反对意见来自坚持不编译注释的一个开发人员。我让他访问Sun的apt工具网站,那里写明“apt首先运行注释处理器,该处理器能产生新的源代码和其他文件。接着,apt可以引起原始文件和生成文件的编译,这就使开发过程更轻松了。”有些开发人员书生气十足,令人好笑。澄清一下,编译或处理的语义学会令人分心。问题是要确保只有版本化的二进制文件被部署到运行的服务器。
我已经在做几个项目,其中tools.jar
被留在类路径上,
WebLogic Server控制台应用程序自身如果没有被正确处理的话,则和它有一个依赖关系。WebLogic Server 9用户应该知道这一点。
包括在应用程序中的WebLogic类路径或系统上的JAR文件可能导致类加载问题——如果这些类加载器上的JAR文件被基础架构团队升级的话。它还会增加可交付程序的大小和部署时间(见PS001和PS002)。如果您曾经不得不将JAR文件部署到系统类路径上,那么您应该确保这样做有正当理由。在系统类路径上管理JAR文件是一种开销,因此最好避免如此。
weblogic-application.xml
描述符
使用WebLogic扩展的应用程序必须包括一个weblogic-application.xml
描述符。如果
EAR包括web应用程序模块,那么webapp.encoding.default
或webapp.encoding.usevmdefault
必须被明确定义以确保应用程序的可移植性并消除应用程序使用的特征码的任何模糊性。虽然在
Java EE规范中,不提供特定于容器的部署描述符是完全可以接受的,但是在实践中省略这些描述符可能在容器的部署代码中暴露bug。
当我看到没有包含特定于容器的部署描述符的应用程序时,这就表明了应用程序没有完成或者缺少对于如何以及在哪里部署应用程序的关注。
weblogic.xml
描述符
weblogic.xml
部署描述符至少应该在以下元素中包含有效且非空的自变量:
对于在WAR文件中包含一个weblogic.xml
部署描述符的抵制总是令我吃惊。在向开发人员和他们的管理者解释为何包含此文件是一个良好的实践的时候,他们完全可以从
WebLogic的例子中复制一个文件到源控制并部署应用程序。归根结底,未包含
weblogic.xml
部署描述符会导致一些我所见过的
、
我接触过的所有版本的
WebLogic Server的、最难以解决的和代价高昂的 部署失败。
在我最近遇到的一个案例中,某应用程序并未附带weblogic.xml
部署描述符。但当应用程序需要在开发人员自己
PC以外的机器上运行的时候,它就需要正确定义security-role-assignment
元素。显然,该缺陷与应用程序有关,但它却是可能破坏部署这个问题的一个好例子。在许多组织中,假设负责应用程序部署的
WebLogic管理员知道该元素要求的值应该是什么是没有理由的。然而,如果错误消息中包含了部署描述符,那么也许会为管理员提供一个解决此问题的机会。
weblogic-ejb-jar.xml
描述符
weblogic-ejb-jar.xml
部署描述符至少应该在以下元素中包含有效且非空的自变量:
另外,原因与PS005和PS006相同,即便您的应用程序无需任何特定于容器的特性,包含特定于容器的部署描述符也是一个良好的实践。
build.xml
文件和pom.xml
文件等构建工件必须从所有的可部署包中滤除。此规范确实是一个良好的管理措施;不要部署任何超出您需要的东西。在一个包中寻找
build.xml
文件和pom.xml
文件是一个散乱的构建过程。如果您的可交付程序将要发送给第三方,那么其中还存在一个实际的安全问题。
所有测试工件都应该从可部署包中移除,例如JUnit系列的JAR文件及使用它们的测试用例。还有,不要部署任何多余的东西,仅仅满足自己的需求即可。包含这些文件会不必要地增加应用程序包的大小,使结构变得散乱。
在使用代理Web服务器提供静态内容的环境中,静态内容应该从EAR文件中移除。这样可以确保快速检测出基础架构堆栈的错误配置和错误代码,以允许WebLogic提供静态内容。如果您的应用程序需要使用大量静态内容,由于它能对部署时间产生影响(见PS001),则这一点就尤为重要。
理论上,存储于平面文件中的与应用程序环境相关的应用程序配置应该存储在应用程序部署描述符中。如果应用程序确实要求使用特定的属性文件来存储配置信息,那么这些文件的命名和位置必须保存一致。稍后,我将在实现指导中详细阐述这一点。与此有关的一点是我将尽量减少在Java命令行指定的、配置应用程序 的环境参数的使用。
部署在生产基础架构上的任何EAR都不能使用应用程序容器外部实现中的JAR文件。发现在容器内运行代码困难的开发人员会使用容器外测试。这种做法的一个令人遗憾的结果就是容器外实现使用的JAR(提供Java EE标准的接口和实现类——例如JTA和servlet规范JAR)被包含在应用程序包中。部署应用程序之后,这便会导致各种各样的类加载问题——特别是如果应用程序试图管理自己的类加载。
当使用JAR的Manifest文件中的Class-Path头部时,管理类路径和类回转就变得过于复杂。相关的JAR文件应该位于应用程序的APP-INF/lib
文件夹中或者位于
EAR文件部署描述符的library-directory
元素定义指定的目录中。
确保应用程序EAR命名一致,并且符合公司的命名法、命名标准和规约。这将简化应用程序部署和其他与应用程序EAR有关的流程的自动化。
遵循此实践,应用程序支持团队和构建工具只需通过名称便可快速确定应用程序的上下文根。还可以轻松地标识应用程序的静态内容。
任何一套规范都有助于提供参考实现。在本节中,我们将介绍如何实现已定义规范的一个子集。我们将讨论PS003、PS005、PS006、PS007、PS010和PS011。
在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>
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>
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>
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>
静态内容应该通过JAR文件交付,其结构如下:
<Webapp Content>
|--css
|--images
|--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应用程序都将有自己静态内容,位于一个一致且可可识别的目录结构中。
所有EAR的应用程序配置都应该保存在一个名为env.properties
的文件中。这些文件都必须位于
APP-INF/lib/classes
路径下。
具备了一组封装最佳实践和实现指导之后,应该如何来实现它们呢?显然,您不想在每一个新构建的应用程序上花费数小时的时间。
一个查看应用程序包的封装规范符合度的好时机正是在应用程序被构建之后,所以编写一个自定义 Ant 任务或扩展Maven的 Verifier plugin 很可能是最佳解决方案。并且,请记住:临时或无限期同意让步的理由总是很充分,这将允许应用程序违犯您的规范。任何的违规事件都应该与许可的截止日期一起记录在应用程序的发布注释中。
此处详述的封装最佳实践并非说明目的——您应该以它们为指导实现自己的规范。毫无疑问,这与您的方法会有一定程度的冲突,因此请确保能够调整它们。归根结底,如果您负责向服务器成功部署应用程序,那么封装规范是实现目标必不可少的工具。
Hussein Badakhchani 是一名咨询顾问和中间件分析师,他在软件设计、开发、管理和支持方面拥有十年以上经验。