我们有一种情况,我们想在请求或事务结束时执行一些任务。更具体地说,我们需要在该请求期间收集一些数据,最后我们使用这些数据进行一些自动数据库更新。
此过程应尽可能透明,即需要此过程的 EJB 用户不必担心这一点。
此外,我们无法控制确切的调用堆栈,因为进程有多个入口点。
为了实现我们的目标,我们目前正在考虑以下概念:
- 某些低级操作(总是被调用)触发 CDI 事件
- 一个无状态的 EJB 监听这些事件,并在接收到一个事件后收集数据并将其存储到一个作用域 CDI bean 中(请求作用域或会话作用域都可以)
- 在请求结束时,另一个事件被触发,导致作用域 CDI bean 中的数据被处理
到目前为止,我们设法启动并运行了第 1 步和第 2 步。
但是,问题在于第 3 步:
正如我已经说过的,该流程有多个入口点(源自 Web 请求、预定作业或远程调用),因此我们想到了以下方法:
3a。CDI 扩展扫描所有 bean 并向每个 EJB 添加注释。
3b。为添加的注解注册了一个拦截器,因此在每次调用 EJB 方法时都会调用该拦截器。
3c。该拦截器的第一次调用将在被调用的方法返回后触发一个事件。
这就是问题所在(再次在第三步:)):
拦截器如何知道它是否是第一次调用?
我们想到了以下方法,但到目前为止都没有奏效:
- 获取请求/对话范围的 bean
- 失败,因为没有上下文处于活动状态
- 获取请求/对话上下文并激活它(然后应该标记第一次调用,因为在后续调用中上下文应该是活动的)
- 系统创建了另一个请求上下文,因此 WELD 以至少两个活动请求上下文结束并对此抱怨
- 转换上下文保持活动状态或过早停用(我们还不知道原因)
- 开始长时间的对话并在调用后结束
- 失败,因为没有活动的请求上下文 :(
我们尚未尝试但似乎不鼓励的另一种选择:
- 使用 ThreadLocal 来存储一些上下文数据或至少使用调用上下文传播,如下所述:http ://blog.dblevins.com/2009/08/pattern-invocationcontext-propagation.html
但是,AFAIK 不能保证请求将完全由同一个线程处理,因此当容器决定切换到另一个线程时,甚至不会调用上下文传播中断?
所以,感谢所有与我一起忍受并阅读所有冗长描述的人。
欢迎任何有关如何解决此问题的想法。
顺便说一句,这是我们正在使用的一些软件组件/标准(我们无法切换):
- JBoss 7.1.0.Final(连同 WELD 和 CDI 1.0)
- EJB 3.1
- Hibernate 3.6.9(还不能切换到 4.0.0)
更新:
根据您迄今为止提供的建议,我们提出了以下解决方案:
- 使用请求范围的对象将数据存储在
- 第一次将对象存储在该对象中时会触发事件
- 在事务结束之前调用侦听器(使用
@Observes(during=BEFORE_COMPLETION)
- 谢谢,@bkail)
到目前为止,这有效,但仍然存在一个问题:
我们还有由 CDI 管理并自动注册到 MBean 服务器的 MBean。因此,这些 MBean 可以注入 EJB 引用。
但是,当我们尝试调用 MBean 方法时,该方法又调用 EJB 并因此导致上述过程启动,我们得到一个ContextNotActiveException
. 这表明在 JBoss 中执行 MBean 方法时请求上下文没有启动。
当使用 JNDI 查找而不是 DI 来获取服务时,这也不起作用。
对此也有任何想法吗?
更新 2:
好吧,看来我们现在可以运行它了。
基本上我们做了我在之前的更新中描述的,并通过创建我们自己的范围和上下文(它在第一次调用 EJB 方法时激活并在相应的拦截器完成时停用)解决了上下文不活动的问题。
通常我们应该能够对请求范围做同样的事情(至少如果我们没有错过规范中的任何内容)但是由于 JBoss 7.1 中有一个错误,当从 MBean 调用 EJB 或计划的作业(执行 JNDI 查找)。
在拦截器中,我们可以尝试获取一个活动的上下文,并在失败时激活 bean 管理器中存在的一个(很可能EjbRequestContext
在这种情况下),但尽管我们进行了测试,但我们宁愿不要指望它在每种情况下都能正常工作。
然而,自定义作用域应该独立于任何 JBoss 作用域,因此不应该在这里干涉。
感谢所有回答/评论的人。
所以还有最后一个问题:我应该接受谁的回答,因为你们都帮助我们进入了正确的方向?- 我会尝试自己解决这个问题并将这些点归因于 jan - 他得到的最少:)