引言
现在很多的企业都在使用开源框架开发自己的企业级应用,如 Struts、Spring 和 Hibernate 等。起初由于受到资金和规模等的限制,大部分应用都部署在 Tomcat 或 Jboss 等开源应用服务器上。但随着业务不断发展,对应用部署的安全和性能要求也越来越高,企业希望将现有的开源应用从开源服务器迁移到商业应用服务器之上,比如:WebSphere Application Server ( 以下简称为 WAS),通过 WAS 增强应用整体性能,并实现更加可靠的管理。本文将通过实例向大家介绍如何将开源应用从 Tomcat 迁移到 WAS,并帮助大家解决一些可能遇到的普遍问题。
基于 Eclipse 开发的 Struts、Spring 和 Hibernate 开源应用和开发环境的特点
随着 Java 技术的逐渐成熟与完善,作为建立企业级应用的标准平台,J2EE 平台得到了长足的发展。借助于 J2EE 规范中包含的多项技术:Enterprise JavaBean (EJB)、Java Servlets (Servlet)、Java Server Pages (JSP)、Java Message Service (JMS) 等,大量的应用系统被开发出来。但是,在传统 J2EE 应用的开发过程中也出现了一些问题,比如在存储和读取过程中使用大量 SQL 和 JDBC 操作,会降低编程的效率以及系统的可维护性;过去传统的 J2EE 应用多采用基于 EJB 的重量级框架 ( 比如:EJB 2.1),这样做的问题在于使用 EJB 容器进行开发和调试需要耗费大量时间并且耦合度非常高,不利于扩展。
在摸索过程中,各种开源框架孕育而生。开源框架以其免费、开源和简单等特点逐渐成为开发人员的最爱,现在仍然有很多的企业使用开源框架开发自己的应用程序。在开源框架中使用最多的就是 Struts、Spring 和 Hibernate 整合框架 ( 以下简称 SSH 框架)。
典型的 J2EE 三层结构,分为表现层、中间层(业务逻辑层)和数据服务层。三层体系将业务规则、数据访问及合法性校验等工作放在中间层处理。客户端不直接与数据库交互,而是通过组件与中间层建立连接,再由中间层与数据库交互。下面就介绍以下 SSH 框架在 J2EE 三层结构中的作用:
- Struts 是一个在 JSP Model2 基础上实现的 MVC 框架,主要分为模型 (Model) 、视图 (Viewer) 和控制器 (Controller) 三部分,其主要的设计理念是通过控制器将表现逻辑和业务逻辑解耦,以提高系统的可维护性、可扩展性和可重用性。
- Spring 是一个解决了许多 J2EE 开发中常见问题并能够替代 EJB 技术的强大的轻量级框架。这里所说的轻量级指的是 Spring 框架本身,而不是指 Spring 只能用于轻量级的应用开发。Spring 的轻盈体现在其框架本身的基础结构以及对其他应用工具的支持和装配能力。与传统 EJB ( 比如 EJB 2.1) 相比,Spring 可使程序研发人员把各个技术层次之间的风险降低。当然,随着 Java EE 5 及 Java EE 6 中新 EJB 规范的出现,如:EJB 3.0, EJB 3.1,EJB 的开发变得越来越简单。用户可以根据自己的需求和能力,选择合适的框架。想了解更多关于 Java EE 5 和 Java EE 6 中的内容,请参考参考资源 [4] 和 [5]。
- Hibernate 是一个数据持久层框架,是一种实现对象和关系之间映射 (O/R Mapping) 的工具,它对 JDBC 进行了轻量级的对象封装,使程序员可以使用对象编程思想来操作数据库。它不仅提供了从 Java 类到数据表的映射,也提供了数据查询和恢复机制。相对于使用 JDBC 和 SQL 来操作数据库,使用 Hibernate 能大大的提高开发效率。
SSH 框架虽然非常强大,但也有一些缺点,比如 : 相比 Servlet+JDBC 开发方式,复杂度增加了不少 ; 而且开源框架开发和部署的灵活性,使得其使用方式不是很符合现有的 J2EE 规范,从而导致从 Tomcat 或其他开源服务器上迁移到 WAS 会出现很多问题和异常。并且,因为默认的 Eclipse 或 MyEclipse 工具缺少 WAS 的运行时插件,使得开发的开源应用程序无法直接从 IDE 里部署到 WAS。接下来,我们会分步介绍从 Tomcat 迁移到 WAS 可能出现的问题,虽然不能涵盖迁移过程中的所有问题,但希望能够抛砖引玉,尽量解决一些普遍存在的问题。
以下使用的实例是利用 Struts2+Spring2+Hibernate3 开发的模拟医院管理应用。其中功能模块包括前台的显示模块、登录模块、后台的文章和药品管理模块、用户管理模块等基本模块;数据库包括药品、文章、学生、教师和看病等数据表。我们利用 Struts 实现 MVC 模型处理前台的各种请求;利用 Hibernate 将数据持久化并简化对数据的查询;利用 Spring 进行依赖注入控制整个业务逻辑层。图 1 为应用的部分包和配置文件结构
图 1. 应用部分包结构和相关配置文件
配置好 MYSQL,将应用部署到 Tomcat 正常显示页面如下:
图 2. 应用主页
这里需要注意的是,由于开源框架的开发和目录结构不规范,导致在 WAS 中部署 WAR 文件失败。您可能会看到诸如“EAR 文件可能已损坏或不完整。确保对于 WebSphere Application Server,该应用程序处于兼容的 Java 2 Platform, Enterprise Edition (J2EE) 级别。”这样的错误。
图 3. WAS 中应用部署错误
遇到上述错误的原因,可能是因为 WAR 文件中包含 EXE 文件或者 WAR 文件结构不规范,去掉这些文件或调整文件结构即可解决该错误。
迁移之前的准备工作
迁移之前的准备工作非常关键。我们首先要确保应用可以在 Tomcat 成功运行,当然我们还需要确认以下几个方面:
- Tomcat 启动正常
- Struts、Spring 和 Hibernate 所需要的 lib 包都包含在应用的 WAR 或者 EAR 包中
- 应用在 Tomcat 上部署并且启动成功
- 应用的 Struts 功能启动成功,并能正常处理请求
- 应用的数据库连接正常,Hibernate 映射成功并能正常实现数据持久化
- 应用的 Spring 功能成功,并能将所需要的资源注入到应用中
- 测试应用的其他功能确保应用整体运行正常
同时查看 Tomcat、Eclipse 的日志,确保应用没有编译异常或错误。当然我们利用 Eclipse 或者 MyEclipse 开发应用时可能会用到 WAS 的插件,我们在部署之前一定要确保系统中只有一个 WAS 实例在运行。如果其他 WAS 实例运行,可能会出现端口冲突等错误,这时 WAS 会提示一些错误:
清单 1. WAS 端口冲突错误
org.omg.CORBA.INTERNAL: CREATE_LISTENER_FAILED_4 vmcid: 0x49421000 minor code:
Caused by: org.omg.CORBA.INTERNAL: CREATE_LISTENER_FAILED_4 vmcid: 0x49421000 minor
code: 56 completed: No
at com.ibm.ws.orbimpl.transport.WSTransport.createListener(WSTransport.java:719)
at com.ibm.ws.orbimpl.transport.WSTransport.initTransports(WSTransport.java:591)
at com.ibm.rmi.iiop.TransportManager.initTransports(TransportManager.java:155)
at com.ibm.rmi.corba.ORB.set_parameters(ORB.java:1212)
at com.ibm.CORBA.iiop.ORB.set_parameters(ORB.java:1662)
at org.omg.CORBA.ORB.init(ORB.java:364)
at com.ibm.ws.orb.GlobalORBFactory.init(GlobalORBFactory.java:86)
at com.ibm.ejs.oa.EJSORBImpl.initializeORB(EJSORBImpl.java:179)
at com.ibm.ejs.oa.EJSServerORBImpl.<init>(EJSServerORBImpl.java:102)
at com.ibm.ejs.oa.EJSORB.init(EJSORB.java:55)
at com.ibm.ws.runtime.component.ORBImpl.start(ORBImpl.java:379)
... 26 more
|
WAS 在启动的时候抛出以上异常,这主要是因为端口冲突,我们可以查看系统中是否有已经启动的 WAS 或者别的程序正在占用此端口。也可以通过修改概要文件 config\cells\cellname\nodes\nodename 目录下的 serverindex.xml 文件中的端口解决。
应用和环境都没问题了,我们就可以着手迁移应用了。
部署中 Struts 框架可能遇到的问题所遇到的问题
Struts 框架最早是作为 Apache Jakarta 项目的组成部分问世运作,它继承了 MVC 的各项特性,并根据 J2EE 的特点,做了相应的变化与扩展。Struts 框架很好的结合了 Jsp,Java Servlet,Java Bean,Taglib 等技术。
不论是 Struts1 还是 Struts2,很多部署问题都跟 lib 包冲突有关,所以在部署应用的时候要尽量查看一下应用 WAR 文件本身是否存在相互冲突的 jar 文件,WAR 文件和 WAS 所带的包是否存在相互冲突。我们总结了一些 Struts 包冲突的问题供大家参考:
启动应用的时候报 Unable to load bean typecom.opensymphony.xwork2.ObjectFactory classorg.apache.struts2.impl.StrutsObjectFactory 错误。这种错误多是由于 WAS 中存在相同 sturts2-core jar 文件与应用 WAR 或者 EAR 文件中 struts 包冲突。建议删除 WAR 包中的 jar 文件,即可解决此问题。
还有一种情况是在 Tomcat 下项目运行没有任何问题,但把 WAR 包安装在 WAS 中只能访问 HTML 页面了,其余的 Struts2 的请求和 JSP 页面都不能访问,提示您无权查看此页面,查看 WAS 日志文件中发现,启动时有类似错误:
清单 2. WAS 包冲突错误
[11-8-18 15:17:41:079 CST] 00000010 webapp E com.ibm.ws.webcontainer.webapp.WebApp
initializeExtensionProcessors SRVE0280E:
扩展处理器无法在工厂
[com.ibm.ws.jsp.webcontainerext.ws.WASJSPExtensionFactory@2bec2bec]
中进行初始化:java.lang.ClassCastException:
com.sun.faces.application.WebappLifecycleListener
incompatible with java.util.EventListener
……
[10-8-18 15:17:41:562 CST] 00000010 config I Initializing
Sun's JavaServer Faces implementation (1.2_07-b03-FCS) for context '/cc'
[10-8-18 15:17:44:579 CST] 00000010 webapp W com.ibm.ws.webcontainer.webapp
.WebApp initializeTargetMappings SRVE0269W: 找不到用于处理 JSP 的扩展处理器。
|
解决方法有两种:
- 在应用程序服务器 -> [ 选择所使用的服务器 ] -> Web 容器设置 -> Web 容器 -> 定制属性,增加名称为"com.ibm.ws.webcontainer.invokefilterscompatibility"的定制属性,值设为 true。
- 或者检查 WAR 文件的 lib 库中是否存 jsf-api.jar,jsf-impl.jar,jstl-1.2.jar 三个 jar 文件。这是因为在使用 MyEclipse 开发时,MyEclipse 会自动将这三个 jar 文件加入到 lib 库中,但 jsf-impl.jar 包中的 com.sun.faces.application.WebappLifecycleListener 与 java.util.EventListener 不兼容导致应用无法访问,打开 WAR 包的 lib 目录,删除这三个 lib 包即可解决问题。
部署中 Spring 框架可能遇到的问题所遇到的问题
Spring 框架支持轻量级的企业应用开发。作为开源项目,Spring 框架得到了广泛的支持。目前大多数 SSH 架构的开源应用以 Tomcat 作为开发以及测试的 Web 容器。 WAS 也同样支持基于 Spring 框架的项目开发和部署,除了支持 Spring 框架本身的资源管理以及支持事务的特性外,WAS 也可以依靠自身的容器管理、事务支持等带来更加可靠的运行时环境。本文该部分将初步介绍基于 Spring 框架的开源应用从 Tomcat 上移植到 WAS 上需要注意的方面。
使用 Spring 开发的应用可以同时使用 Struts 和 Hibernate。整合了 Struts、Spring 和 Hibernate 进行开发的应用只要保证需要的 JAR 包全部打包在应用中,便可以正确的部署到 WAS 上。其中,WAS 不提供 Tomcat 缺省提供的一些开源 JAR 包,需要将这些包包含在应用中。此外,WAS 提供了很多 J2EE 相关特性,如果 Spring 要使用这些特性,则需要对 Spring 做相关配置。
数据访问
Spring 整合 Hibernate 时,数据源配置信息应该定义在 applicationContext.xml 文件中。清单 3 给出了一个典型的配置数据源信息的示例。您可以将这段代码不做任何修改,加入到 applicationContext.xml 文件中,并放到打包之后的应用中,WAS 会自动识别并利用 Spring 框架完成数据源的配置。
清单 3. applicationContext.xml 文件中的数据源配置
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.ibm.db2.jcc.DB2Driver"></property>
<property name="url" value="jdbc:db2://localhost:50000/MYTEST"></property>
<property name="username" value="db2admin"></property>
<property name="password" value="password"></property>
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="dataSource" />
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.DB2Dialect</prop>
</props>
</property>
<property name="mappingResources">
<list><value>com/ibm/user/Person.hbm.xml</value></list>
</property>
</bean>
|
您也可以使用 WAS 中已经配置好的数据源,在 Spring 的配置文件 applicationContext.xml 文件中声明数据源的代理 Bean,将 WAS 的数据源等资源通过该 Bean 委托给 Spring 框架进行调用。清单 4 给出了一个使用该方式进行配置的 applicationContext.xml 文件的片段。应用在运行时会使用该 Bean 找到相应的数据源完成与数据库的交互。
清单 4. applicationContext.xml 文件中配置 WAS 数据源
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName"><value> java:comp/env/jdbc/SSHTestDB</value></property>
</bean>
|
事务管理
Spring 框架同 WAS 一样支持两种事务管理的方式,分别为编程式和声明式。大多数的用户会选择声明式的事务管理方式,这种方式也是推荐使用的。
通常情况下 Spring 事务管理的一个核心是 PlatformTransactionManager 接口,使用声明方式的事务管理的类均实现该接口,如对数据源进行事务管理的 DataSourceTransactionManager,对 Hibernate 进行事务管理的 HibernateTransactionManager 等。用户可以选择继续使用这些事务管理方法,在 applicationContext.xml 文件中做相应的配置,如清单 5 所示,然后打包部署到 WAS 上完成应用的安装和配置。
清单 5. Spring 框架的事务管理配置
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="txManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
|
其中 ref=”dataSource” 对应于清单 3 中的 Bean id=”dataSource”;ref=”sessionFactory”对应于清单 3 中的 bean id="sessionFactory"。
WAS 支持 JTA,而 Spring 也提供了 JtaTransactionManager。因此也可以将 Spring 中的事务管理交给 WAS 来做。Spring 2.5 之后提供的特定于 WAS 的事务管理的实现类为 WebSphereUowTransactionManager,您可以在 applicationContext.xml 文件中进行相应的配置,将事务的管理交由 WAS 来做。如清单 6 所示。其中配置的 bean id="transactionManager" 并不需要知道自己为哪些资源负责,因为它使用了 WAS 容器的全局事务管理体系。
清单 6. Spring 配置 WAS 的事务管理
<bean id="txManager"
class="org.springframework.transaction.jta.WebSphereUowTransactionManager">
</bean>
<bean id="transactionManager"
class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="transactionManager">
<ref local="txManager"/>
</property>
</bean>
|
部署中 Hibernate 框架可能遇到的问题所遇到的问题
Hibernate 做为数据持久层框架非常灵活,易于上手,并且便于与其它开源框架整合,从而使它成为开源解决方案中数据持久层框架的不二选择。Hibernate 从 3.2 开始,就开始兼容 JPA。Hibernate3.2 获得了 Sun TCK 的 JPA (Java Persistence API) 兼容认证。这使它的应用范围更加广泛。这里介绍将使用 Hibernate 做为持久层的应用移植到 WAS 上时需要注意的方面。
在项目初期,由于定位或需求的原因,很多 Hibernate 应用都使用 Tomcat 或 Jboss 做为应用服务器。随着项目规模越来越大,对应用的可靠性和安全要求越来越高,就会考虑向商业应用服务器的迁移。总的来说,对一个可以正常运行在 Tomcat 或 Jboss 上的应用来说,移植到 WAS 上非常简单,并不需要做太多改动,只需要将应用所依赖的 Hibernate 相关 jar 包都打包在应用中,再更具情况对配置文件做轻微调整即可,不用修改任何 Java 源代码。
保持原有连接方式
很多 Hibernate 应用采用 JDBC 的链接方式,即在配置文件 hibernate.cfg.xml 中配置 connection.url 属性,指定数据库链接信息。例如:<property name="connection.url">jdbc:db2://db2url:port/dbname</property>
这种应用程序往往还要使用第三方提供的数据库连接池,例如 C3P0 等。如果在移植到 WAS 之后仍然想保持现有连接形式和数据库连接池不变,则不需要对配置文件做任何修改,只需要将第三方提供的数据库连接池所依赖的 jar 包文件一同打包到 WAS 应用中即可。例如将 C3P0 数据库连接池 jar 文件 c3p0-0.9.1.jar 打包到应用程序 lib 目录下。
使用 WebSphere Application Server 数据源
WAS 数据源有着众多企业级优势,很多用户移植后都希望能使用到 WAS 做为企业级应用服务器数据层的强大功能,对 Hibernate 的移植也不例外。其实将 Hibernate 应用的数据源移植到 WAS 非常简单,只需要在 hibernate.cfg.xml 配置文件中加入数据源属性 connection.datasource 和 JNDI 提供商信息即可,无需修改任何源代码。这里需要注意,一旦配置好 WAS 的数据源,WAS 将接管与数据库通信的工作,如果在您以前的应用中使用了第三方数据库连接池,将会产生冲突。解决方法也很简单,只要将 hibernate.cfg.xml 配置文件中的关于第三方数据库连接池的信息注释或删除即可。
总结起来可分为三步:
- 将数据库连接信息
<property name="connection.url">jdbc:db2://db2url:port/dbname</property>
替换为 WAS 数据源信息
<property name="connection.datasource">jdbc/hibernate</property>
- 加入 jndi.class 属性
<property name="jndi.class">com.ibm.websphere.naming.WsnInitialContextFactory</property>
- 注释或删除原有数据库连接池相关属性
总结
现在的开源框架越来越庞大,同一框架不同版本的区别也很大。这篇文章虽不能覆盖开源框架迁移到 WAS 的所有问题,但总结的都是一些比较普遍的问题,力求让用户快速地发现和解决部署和迁移过程中的问题。在迁移过程中,应用的代码基本不需要修改,只要配置和部署得当,从 Tomcat 将开源应用迁移到 WAS 并不是一件难事。