2011-12-22 11 views
10

Estoy planeando usar algunas propiedades desde el nivel de aplicación (específicamente, un bean controlado por mensajes) a una devolución de llamada de ciclo de vida de persistencia que no puede ser inyectada directamente o pasar parámetros (escucha de sesión en EclipseLink, devolución de llamada de ciclo de vida de entidad, etc. .), y esa devolución de llamada está obteniendo el EJBContext a través de JNDI.Usando EJBContext getContextData - ¿esto es seguro?

Esto parece funcionar, pero ¿hay algún truco oculto, como seguridad de hilo o vida útil del objeto que me falta? (Suponga que el valor de la propiedad que se pasa es inmutable como cuerdas o Long.)

código de bean de ejemplo

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

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

A continuación, la devolución de llamada que consume el EJBContext

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

Lo que me pregunto es, ¿Puedo estar seguro de que los contenidos del mapa contextData solo son visibles para la invocación/cadena actual? En otras palabras, si dos subprocesos ejecutan el método callback de forma simultánea, y ambos buscan un EJBContext de JNDI, en realidad están obteniendo contenidos de mapa contextData diferentes.

Y, ¿cómo funciona eso realmente - es el EJBContext devuelto de la búsqueda JNDI realmente un objeto envoltorio alrededor de una estructura similar a ThreadLocal en última instancia?

Respuesta

8

Creo que en general el contrato del método es permitir que el comunicación entre interceptores + contextos del servicio web y beans. Por lo tanto, el contexto debe estar disponible para todo el código, , siempre que no se cree un nuevo contexto de invocación. Como tal, debería ser absolutamente seguro para subprocesos.

Sección 12.6 de la especificación EJB 3.1 dice lo siguiente:

El objeto InvocationContext proporciona metadatos que permite métodos interceptores para controlar el comportamiento de la cadena de invocación. Los datos contextuales no se pueden compartir entre invocaciones de métodos de negocio separados o eventos de devolución de llamada de ciclo de vida. Si interceptores se invocan como resultado de la invocación en un extremo de servicio web, el mapa devuelto por getContextData será el JAX-WS MessageContext

Además, el método getContextData se describe en 4.3.3:

El método getContextData habilita un método comercial, un método de devolución de llamada del ciclo de vida o un método de tiempo de espera para recuperar cualquier contexto interceptor/webservices asociado a su invocación.

En términos de implementación real, JBoss AS realiza lo siguiente:

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

Cuando el CurrentInvocationContext utiliza una pila basada en una thread-local linked list al pop y empujar el contexto actual de invocación.

Ver org.jboss.ejb3.context.CurrentInvocationContext. El contexto de invocación simplemente crea un simple HashMap, como se hace en org.jboss.ejb3.interceptor.InvocationContextImpl

Glassfish hace algo similar. También es gets an invocation, y lo hace from an invocation manager, que también usa una pila basada en thread-local array list para abrir y volver a presionar estos contextos de invocación.

el Javadoc para la aplicación GlassFish es especialmente interesante aquí:

Esta variable almacena TLS un ArrayList. ArrayList contiene objetos ComponentInvocation que representan la pila de invocaciones en este hilo. Los accesos a ArrayList no necesitan ser sincronizados porque cada hilo tiene su propia ArrayList.

Al igual que en JBoss AS, GlassFish crea también un simple pereza HashMap, en este caso en com.sun.ejb.EjbInvocation. Interesante en el caso GlassFish es que la conexión al servicio web es más fácil de detectar en la fuente.

9

No puedo ayudarle directamente con sus preguntas con respecto a EJBContext, ya que el método getContextData se agregó en JEE6 aún no hay mucha documentación al respecto.

Sin embargo, hay otra manera de pasar datos contextuales entre EJB, interceptores y devoluciones de llamadas de ciclo de vida utilizando el TransactionSynchronizationRegistry. El concepto y el código de muestra se pueden encontrar en este blog post by Adam Bien.

javax.transaction.TransactionSynchronizationRegistry tiene una estructura tipo mapa y se puede usar para pasar el estado dentro de una transacción. Funciona perfectamente desde el antiguo J2EE 1.4 días y es independiente del hilo.

Como se ejecuta un Interceptor en la misma transacción que ServiceFacade, el estado se puede establecer incluso en un método @AroundInvoke. El TransactionSynchronizationRegistry (TSR) se puede inyectar directamente en un Interceptor.

El ejemplo no utiliza @Resource inyección para obtener el TransactionSynchronizationRegistry, pero también se puede consultar desde el InitialContext así:

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

Buena sugerencia. El patrón que estoy usando para 'EJBContext' es casi idéntico, inyectando' @ Resource' en un lugar, colocando objetos en el mapa y luego buscando en JNDI en otro lugar. Así que la pregunta es si el objeto 'EJBContext' también es independiente del hilo como' TransactionSynchronizationRegistry' – wrschneider

+1

TransactionSynchronizationRegistry tiene un límite: siempre necesita una transacción, pero en algunos casos es necesario para propagar información sin una transacción – obe6