最近,公司的GlassFish移植项目基本告以段落,由于之前的代码严重依赖于Weblogic,给移植工作带来了很大的难度,很多实现方式在GlassFish中根本就没有对应的替代品。在经历了几个月的移植之后,竟让我对Weblogic开始产生好感了,作为一款商用的Application Server,Weblogic确实非常成熟,非常强大,提供了很多特性,以帮助提高程序的运行效率,但是太笨重了,访问Admin Console极慢;GlassFish作为一款开源的Application Server,非常适合开发者使用,速度很快,并且严格遵照J2EE的标准,以达到平台独立的特性,但是确实简陋了点,只提供了最标准的实现,并且还存在一些明显的BUG,社区不够活跃,文档、资源都很少,可能是现在SUN处于动乱期,连商业Support都很难联系到。下面是我在做移植工作时,随笔记下来的一些小经验,让其他的同学们少受一些折磨,少踩一些坑。
1、EJB
Client与weblogic.jar冲突
若使用EJB Client访问GlassFish中的EJB,需将appserv-rt.jar、appserv-ext.jar、appserv-deployment-client.jar和javaee.jar加入到classpath中。若classpath中存在weblogic.jar,则可能会遇到错误:
java.lang.NoSuchMethodError:
org.omg.CosTransactions.OTSPolicy.value()S
将weblogic.jar从classpath中移除即可。
2、Transaction使用
使用Spring的JtaTransactionManager需要配置两个属性:JtaTransactionManager 和userTransactionName。对于GlassFish,JtaTransactionManager 为 java:appserver/TransactionManager , userTransactionName 为 java:comp/UserTransaction。只有Bean管理的SessionBean和MDB允许使用UserTransaction,Entity Bean只允许使用Container管理的transaction。如果Container管理的SessionBean或MDB使用了UserTransaction,则会出现错误:Lookup of java:comp/UserTransaction not allowed
for Container managed Transaction beans。
3、HTTP
Thread count
使用asadmin修改HTTP
的thread count后,从Admin Console上访问,Admin
Server的配置可生效,但Cluster不生效,检查domain.xml已改变,通过asadmin查询也已生效,应该是GlassFish页面展示的BUG。
4、EJB Timer的使用
在GlassFish中使用EJB
Timer,需要有一个独立的XADataSource,和数据表EJB__TIMER__TBL,建表语句可在<server_path>/lib/install/databases中找到。对于developer模式,GlassFish默认使用内置的__TimerPool,不需要你手工创建datasource和表;对于cluster模式,Admin Server会默认使用__TimerPool,Cluster则需要单独配置。如果让Admin Server和Cluster同时使用手工创建的datasource,则可能导致Cluster配置中的timer datasource在server重启后丢失,Timer
Service会出现异常,这应该是GlassFish的BUG,目前的解决方案就是Admin Server用默认的Timer配置,Cluster用另外的配置。
5、ClassLoader优先加载
在weblogic中,可以通过配置prefer-application-packages来优先加载application中的类,在GlassFish中则没有对应的方式来控制加载顺序,一个典型的场景就是:项目中采用CXF作为webservice的实现,但GlassFish中默认使用了Metro的实现,由于Metro的jar包比application加载的早,就会导致CXF依赖的类库没有正常加载,而是使用了Metro的JAX-WS的实现。
6、CMP配置中的数据库表名区分大小写
CMP在GlassFish中需要配置sun-cmp-mappings.xml,该XML中的table-name是区分大小写的,Oracle中的表名默认是大写的,如果这里的table-name写成小写,就会报找不到表的错误,可以通过添加一个*.dbschema文件,对表名进行适配,以减少切换数据库时的修改操作。
7、GlassFish的部署结果不可靠
在使用asadmin部署EAR时,如果没有遇到极其严重的错误,部署一般都会返回成功,但这个结果并不可靠,你需要关注server.log,如果这里出现了错误,应用程序则可能没有真正部署成功,在运行时就会出现错误,所以要确保你的程序部署时,server.log中没有错误信息。
8、TLD路径
根据JSP2.1规范,tld文件不能存放在/WEB-INF/classes或者/WEB-INF/lib目录中,特别不能放在/WEB-INF/tags目录或子目录中,否则会出现错误:
exception:
org.apache.jasper.JasperException: PWC6180: Unable to initialize
TldLocationsCache
root cause:
org.apache.jasper.JasperException: PWC6336: Illegal TLD path
/WEB-INF/tags/fn.tld, must not start with “/WEB-INF/tags”
在Tomcat和Weblogic中不会出现该问题,GlassFish则严格遵照规范,可将tld文件放置在/WEB-INF/tld目录。
9、注册servlet listener
在web.xml中注册servlet的listener时,在<listener>中添加多个<listener-class>不会报错,但是只有最后一个<listener-class>生效,因此,要注册多个listener,需要添加多个<listener>。
10、
Pass-by-reference
Weblogic中的call-by-reference能够极大的提高本地接口调用的效率,在GlassFish中也有相应的替代,就是pass-by-reference,可以在sun-ejb-jar.xml中对某个EJB进行配置,也可以在sun-application.xml中配置,这样就可以对整个application中的EJB生效。
11、HTTP错误消息体
当HTTP的ErrorCode大于400,并且相应的消息体是空时,GlassFish会自动在返回的Response中添加错误信息,对于使用HttpClient操作时,就可能和我们期望的Response不同,该问题的解决办法:在往Response中写入内容后,调用response.getOutputStream().flush()
或 response.flushBuffer();或者在web.xml中设置ErrorcCde对应的ErrorPage,ErrorPage可以是一个空内容的页面。
12、ServletRequest中inputStream的使用
InputStream有一个markSupported属性,如果该属性为true,则支持mark和reset,可以多次读取该流,反之则只能读取一次该输入流。一种情形就是:如果在Filter中读取了该InputStream,则不能在Servlet中再次读取。ServletRequest中的InputStream在不同的Server中有不同的实现,在Weblogic中markSupported就设为了true,在GlassFish中则为false。