使用EJBContext getContextData - 这样安全吗?

时间:2011-12-22 18:39:12

标签: java java-ee java-ee-6 ejb-3.1

我打算使用EJBContext将一些属性从应用程序层(特别是消息驱动的bean)传递给不能直接注入或传递参数的持久性生命周期回调(EclipseLink中的会话侦听器,实体生命周期回调等),并且该回调通过JNDI获取EJBContext

这似乎有用,但是有没有任何隐藏的陷阱,比如我缺少的线程安全或对象生命周期? (假设传递的属性值是不可变的,如String或Long。)

示例bean代码

@MessageDriven
public class MDB implements MessageListener {
   private @Resource MessageDrivenContext context;

   public void onMessage(Message m) { 
      context.getContextData().put("property", "value");
   }
}

然后是使用EJBContext的回调

public void callback() { 
   InitialContext ic = new InitialContext();
   EJBContext context = (EJBContext) ic.lookup("java:comp/EJBContext");
   String value = (String) context.getContextData().get("property");
} 

我想知道的是,我能确定contextData地图内容只对当前的调用/线程可见吗?换句话说,如果两个线程同时运行callback方法,并且都从JNDI查找EJBContext,那么它们实际上会获得不同的contextData映射内容?

而且,这实际上是如何工作的 - 从JNDI查找返回的EJBContext真的是一个围绕ThreadLocal类似结构的包装器对象吗?

2 个答案:

答案 0 :(得分:10)

我认为通常方法的合同是启用拦截器+ webservice上下文和bean之间的通信。因此,只要没有创建新的调用上下文,所有代码都可以使用上下文。因此,它应该绝对是线程安全的。

EJB 3.1规范的第12.6节说明如下:

  

InvocationContext对象提供启用的元数据   拦截器方法来控制调用链的行为。   上下文数据不能在单独的业务方法中共享   调用或生命周期回调事件。如果调用拦截器   作为在Web服务端点上调用映射的结果   getContextData返回的将是JAX-WS MessageContext

此外,getContextData方法在4.3.3中描述:

  

getContextData方法使业务方法,生命周期回调方法或超时方法能够检索与其调用关联的任何拦截器/ webservices上下文。

在实际实现方面,JBoss AS执行以下操作:

public Map<String, Object> getContextData() {
    return CurrentInvocationContext.get().getContextData();
}

CurrentInvocationContext使用基于thread-local linked list的堆栈来弹出并推送当前的调用上下文。

org.jboss.ejb3.context.CurrentInvocationContext。调用上下文只是懒惰地创建了一个简单的HashMap,就像org.jboss.ejb3.interceptor.InvocationContextImpl中所做的那样

Glassfish做了类似的事情。它也是gets an invocation,并执行此from an invocation manager,它还使用基于thread-local array list的堆栈来再次弹出和推送这些调用上下文。

GlassFish实现的JavaDoc在这里特别有趣:

  

此TLS变量存储ArrayList。 ArrayList包含   ComponentInvocation对象,表示调用堆栈   在这个线程上。对ArrayList的访问不需要同步   因为每个线程都有自己的ArrayList。

就像在JBoss AS中一样,GlassFish也懒得创建一个简单的HashMap,在本例中是com.sun.ejb.EjbInvocation。对GlassFish案例感兴趣的是,Web服务连接更容易在源中发现。

答案 1 :(得分:8)

我无法直接帮助您解决有关EJBContext的问题,因为在JEE6中添加了getContextData方法,但仍然没有太多关于它的文档。

然而,有另一种方法可以使用TransactionSynchronizationRegistry在EJB,拦截器和生命周期回调之间传递上下文数据。可以在此blog post by Adam Bien中找到概念和示例代码。

  

javax.transaction.TransactionSynchronizationRegistry包含类似Map的结构,可用于在事务内传递状态。它从旧的J2EE 1.4天开始就完美运行,并且与线程无关。

     

因为Interceptor在与ServiceFacade相同的事务中执行,所以甚至可以在@AroundInvoke方法中设置状态。 TransactionSynchronizationRegistry(TSR)可以直接注入拦截器。

此示例使用@Resource注入来获取TransactionSynchronizationRegistry,但也可以从InitialContext中查找,如下所示:

public static TransactionSynchronizationRegistry lookupTransactionSynchronizationRegistry() throws NamingException {
    InitialContext ic = new InitialContext();
    return (TransactionSynchronizationRegistry)ic.lookup("java:comp/TransactionSynchronizationRegistry");
}