解惑

解己之惑,解人之惑

标签:EJB

突然发现误解了EJB很多年

本来感觉是很理所当然的事情,那就是Bean要实现Remote或者Local借口,但是EJB规范里面并没有这个要求,只要Bean里面实现了Remote和Local里面定义的方法,方法签名完全一致,我所经历的所有的项目都是这样的。现在为另外一个项目做东西,在检查一些代码调用的时候发现跟踪不到,结果发现就是因为Bean没有实现接口,开始还笑话他们,后来google了下,发现原来是自己错了。不过实现接口也有好处,可以进行编译期检查约束。

控制Message Driven Bean的实例数

EJB2和EJB3的不一样,而且好像和容器提供商也有关,这里给出的是适用于EJB3和JBoss4的:
@MessageDriven(activationConfig = {
        @ActivationConfigProperty(
                propertyName = "destinationType",
                propertyValue = "javax.jms.Queue"),
        @ActivationConfigProperty(
                propertyName = "destination",
                propertyValue = "queue/email"),
        @ActivationConfigProperty(
                propertyName = "maxSession",
                propertyValue = "1")})

关键的就是那个maxSession,它控制实例的最大个数。

太复杂了!!!

今天总算是把工程在Eclipse下配置好了,而且没有任何错误,从Oneal那里拷贝来的工程文件一堆错误,我这个人又是那种容不得满眼全是错误的人,花了一个小时搞定这个问题,开始熟悉项目,结果是:真是太复杂了!
系统的大部分都是通过Web Service调用的,包括和其他的系统集成以及和工作流引擎集成,而Web Service又是通过Session Bean调用DAO,DAO再调用hibernate,而Session Bean用到了版本2和版本3(也就是同时使用了EJB2和EJB3),因此工程有N个EJB的模块,源代码也分布在几十个大大小小的目录中,同时Web工程也是N个模块,另外还使用到了LDAP完成用户登陆,Eclipse工程目前只是用于远程调试和编译,最主要是写代码,完全失去了IDE最重要的功能。(这个是目前的理解,不排除遗漏的东西以及误解)

如果发现问题或者要修改Bug,抱歉,不能Hot Deploy,必须修改代码后使用Ant编译并发布好,然后重启Jboss,Ant一般情况下至少需要5分钟,即使是修改一个properties也要2分钟才能跑完,然后重启Jboss需要8分钟(我的是新安装的机器,比较快,其他的人的需要10分钟左右),天啊,开发效率怎么可能高啊!
不过这也正好给我一个机会,上一家公司的情况也是类似,大家修改什么也是先Ant再重启Jboss,一般也至少要10分钟,后来我就在Eclipse下配置了可以Hot Deploy的工程,如果只是修改代码内容,不新增EJB类或者方法就不用Ant编译了,修改了代码保存下就更新了,开发速度提高很多,我这个月的任务就是在这个公司也把这个工程搞出来,这样弄完以后对项目的熟悉程度也会上一个台阶。

全面自动测试

今天参加了QA的一个演示,演示的是他们基于Watir的测试框架,这样加上我做的单元测试框架,我们就可以全面的进行自动测试了,使用我的单元测试框架,可以测试EJB和Action,进行的是白盒测试,而使用QA的测试框架,可以测试页面的功能,进行的是黑盒测试。

总算是初步完成了EJB单元测试框架

经过一段时间的努力,我总算是初步完成了这个EJB单元测试框架,现在运行那些测试用例(183个,难易不等)只需要60秒左右,我挑选出来的一个做为样例的Session Bean的代码行覆盖率达到94%,而Branch覆盖率为100%。
明天计划说服其它的人使用这个框架辅助他们的开发,虽然这个是单元测试,但是因为不用启动JBoss也可以调试Session Bean和Action,我想使用它辅助开发也是个不错的主意(我们的JBoss启动需要至少2分钟,一般都是在3分钟以上)。另外一个进行辅助开发的原因是我们的时间很紧,原本计划在这个版本中要求新的功能必须有单元测试的要求可能也要取消了,那我辛辛苦苦完成的框架可能要拖到至少明年3月以后才可能被使用了,夜长梦多啊。
另外一个考虑就是他们写的测试用例越多,可能发现的问题也越多,也好帮助我更加完善这个框架。另外就是可能会要求我增加更多的功能辅助他们的验证工作。

基于MockEJB的CMP实现

MockEJB提供了一个简单的CMP容器实现,但是功能实在是过于简单,只实现了一个findByPrimaryKey方法。它的CMP其它功能的实现需要依赖于Aspect,我的这个单元测试框架就是靠实现了一堆的Aspect来完成CMP的一些功能的。

  1. BaseCreateAspect:调用entity bean的create方法会进入这个Aspect,如果是系统初始化从数据库和文件读取初始数据的话,会直接根据那些数据填充entity bean,如果是程序代码创建的entity bean,就会调用entity bean的create和postCreate并且准备把创建的entity bean的内容同步到数据库。
  2. BasePostCreateAspect:调用entity bean的postCreate会调用这个,目前功能不多。(没有必要,删除)
  3. BaseFindAspect:调用entity bean的find方法会调用这个,前面的finder解析器解析的结果就是由它来使用的。
  4. BaseRemoveAspect:调用entity bean的remove方法会调用这个,会把记录从数据库删除。
  5. BaseSetterAspect:调用entity bean的setter方法会调用这个,记录entity bean的内容有更新,可能会需要同步到数据库。

在完成这些Aspect的时候需要定义拦截点,可以使用表达式,可惜使用的是Apache的ORO的语法,和Java自己带的表达式语法不同。(修改为自己的Pointcut实现

代码如下:
阅读全文

艰难的过程

最近在做公司的单元测试框架,本来感觉已经初具规模,可以开始正常的使用了,因为我写了40多个各种类型的测试用例都可以正常工作,另外一个组的同事开始写了一个,他说还是挑的一个最简单的,结果报了一堆的异常,呵呵,其实他是测试Message Driven Bean,代码里面要调用很多其它的东西,出了几个问题,一个是他要使用的一个Entity Bean的finder是我写的表达式不支持的,我只能把那个finder实现掉,然后就是代码中使用了一个Topic,而且不是我们的Server端代码使用的,没有对应的Message Driven Bean处理(框架只能自动发布有对应的Message Driven Bean的Queue或者Topic),这个问题忽略不计了,因为那个Message本来就不需要处理,然后就是另外一个finder方法,我的表达式是支持的,但是死活找不到,结果发现是那个Entity Bean的xdoclet的配置比较特殊,一般的Home接口都是Remote接口的名字加Home,结果那个Bean是加BeanHome,而Aspect的匹配表达式是默认的格式,没有办法,修改我的表达式了,谁让那个Home接口的名字是可配置的呢。
另外两个问题还没有搞定,一个是Stateless Session Bean的remove方法,不知道要做什么,我现在处理了Entity Bean的remove,调用那个的时候会从数据库中删除对应的记录,但是Stateless Session Bean的remove应该做什么呢?
一个是获得Topic,我们的那个Locator接口里面定义了获得各种Bean以及Queue的接口方法,但是没有定义获得Topic的方法,所以代码中都是使用:
Context jndiContext = new InitialContext();
topic = (Topic) jndiContext.lookup(serverDataSendTopicName);
这个lookup的过程框架拦截不到。

MockEJB测试框架之自动发布EJB后续

本来今天已经完成了框架的大部分功能,写的一些测试用例都可以成功执行,但是自动发布是放在BaseTestCase里面的,感觉不好,就重构了一下,扩展了InitialContext类,覆盖了lookup方法(参数为字符串的那个,我们的代码中都是使用的这个),这样代码看起来更好一些的。另外我们的系统里面使用到了一些类似的机制,我为了产品可以进行单元测试还对一些Factory类进行了简单的修改,让他们返回我为了单元测试而写的实现类。如果使用这个扩展的InitialContext类,应该不需要做那些修改了,试了下,发现不行,因为产品的代码好像和Jboss的某些特性进行绑定了,先做简单的重构,以后再研究产品到底使用了JBoss的哪些特性,能否搞定。
代码如下:
阅读全文

MockEJB测试框架之自动发布EJB

前段时间大致介绍了下这个测试框架,目前还没有完全完成,这里先慢慢介绍下框架得构成,这个是第一篇。

其实MockEJB提供的功能还是比较简单的,做单元测试需要我们自己写代码发布Bean,例如发布一个Session Bean:
            SessionBeanDescriptor sampleServiceDescriptor =
                new SessionBeanDescriptor( SampleService.JNDI_NAME,
                SampleServiceHome.class, SampleService.class, new SampleServiceBean() );
             mockContainer.deploy( sampleServiceDescriptor );

其实每个EJB工程都应该有现成的EJB配置文件(ejb-jar.xml、jboss.xml、jbosscmp-jdbc.xml),因此这个发布工作应该可以自动完成,思路就是自己读取那些配置文件获得EJB的配置信息。
然后就是要修改lookup的过程,由于我们的工程提供一个共通的接口来进行lookup,所以问题就演变为实现一个那个接口。对于其它的项目,应该是类似的,如果是直接使用Context的lookup,那么对于MockEJB而言就是扩展MockContext,覆盖lookup方法,先查找,找到目标就返回,没有的话尝试根据读取的EJB配置信息自动发布需要的EJB。这样,我们就在单元测试中可以直接获取EJB,而不用再进行发布了。

EJB单元测试框架

公司的产品要引入单元测试以及代码覆盖,让我做一个框架方便我们写单元测试代码,上网找了一下,找到一个不错的基础:MockEJB,大家可以先了解下相关的内容:
MockEJB项目简介信息
使用MockEJB简化EJB测试

我在这个基础上进行了扩充,框架具有如下的特性:

  • 读取EJB配制文件完成EJB的自动发布,框架会读取我们工程的ejb-jar.xml,jboss.xml,jbosscmp- jdbc.xml文件的配制信息并组合这些信息完成自动发布所需要的内容。
  • 共享的dummy数据,我们自己提供一个xml文件,里面存放一些dummy的数据,这些数据会在对应的Entity Bean被使用的时候自动插入数据库,完成数据的初始化工作(这样Session Bean中的SQL也可以取到一致的内容),在一个test方法结束的时候回滚,删除那些记录,消除Test Case之间的影响。
  • 自定义事务管理,每个test方法都处于同一个事务中,方法结束的时候事务回滚,消除Test Case之间的影响。
  • 支持部分find方法的实现,由于MockEJB对于Entity Bean只提供了对findByPrimaryKey的支持,甚至连findAll都不支持,框架在MockEJB提供的Aspect机制的基础上实现了 findAll等常用必备的find方法,并且对于自定义的find方法也提供部分支持,通过解析EJB配制文件中的SQL完成,但是可能性能不好,因为会遍历所有的记录并比较字段的值完成这个查找的过程。其它比较复杂的find方法(框架无法解析的SQL),需要自己实现对应的Aspect,框架会自动查找对应的Aspect。
  • Struts TestCase的基础上支持对Action的单元测试。

测试代码范例:
阅读全文

© 2019 解惑

本主题由Anders Noren提供向上 ↑