2011-12-22 8 views
10

Sto pianificando di utilizzare EJBContext per passare alcune proprietà dal livello applicazione (in particolare, un bean basato sui messaggi) a un callback del ciclo di vita della persistenza che non può essere immesso direttamente o passato parametri (listener di sessione in EclipseLink, callback del ciclo di vita dell'entità, ecc. .), e quella richiamata sta ottenendo il EJBContext tramite JNDI.Utilizzo di EJBContext getContextData: è sicuro?

Questo sembra funzionare ma ci sono dei trucchi nascosti, come la sicurezza dei thread o la durata degli oggetti che mi manca? (Si assuma il valore della proprietà di essere passato è immutabile come String o Long.)

Esempio codice del bean

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

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

Poi il callback che consuma l'EJBContext

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

Quello che mi chiedo è, posso essere sicuro che il contenuto della mappa contextData sia visibile solo al richiamo/thread corrente? In altre parole, se due thread eseguono il metodo callback contemporaneamente ed entrambi cercano uno EJBContext da JNDI, in realtà ottengono diversi contenuti della mappa contextData?

E, come funziona davvero - lo EJBContext restituito dalla ricerca JNDI è davvero un oggetto wrapper attorno a una struttura di tipo ThreadLocal alla fine?

risposta

8

penso che, in generale, il contratto del metodo è quello di consentire la comunicazione tra interceptor + contesti e bean webservice. Pertanto, il contesto dovrebbe essere disponibile per tutto il codice, a condizione che non venga creato alcun nuovo contesto di chiamata. Come tale dovrebbe essere assolutamente sicuro per i thread.

sezione 12.6 del bean 3.1 specifiche dice il seguente:

Scopo InvocationContext prevede metadati che consente metodi intercettatori per controllare il comportamento della catena invocazione. I dati contestuali non sono condivisibili tra invocazioni del metodo di attività o eventi di callback del ciclo di vita separati. Se intercettori sono invocati a seguito dell'invocazione su un endpoint del servizio web, la mappa restituito da getContextData sarà il JAX-WS MessageContext

Inoltre, il metodo getContextData è descritto in 4.3.3:

Il metodo getContextData consente un metodo business, un metodo di callback del ciclo di vita o un metodo di timeout per recuperare qualsiasi contesto di intercettatore/webservice associato al suo richiamo.

A livello di esecuzione effettiva, JBoss AS esegue le seguenti operazioni:

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

Quando la CurrentInvocationContext utilizza uno stack basata su un thread-local linked list pop e spingere contesto chiamata corrente.

Vedere org.jboss.ejb3.context.CurrentInvocationContext. Il contesto di invocazione crea pigramente un semplice HashMap, come è fatto in org.jboss.ejb3.interceptor.InvocationContextImpl

Glassfish fa qualcosa di simile. È anche gets an invocation e fa questo from an invocation manager, che utilizza anche uno stack basato su un thread-local array list per eseguire il pop e spingere nuovamente questi contesti di chiamata.

Javadoc per l'attuazione GlassFish è particolarmente interessante:

Questo TLS negozi variabile un ArrayList. ArrayList contiene oggetti ComponentInvocation che rappresentano lo stack di invocazioni su questo thread. Gli accessi a ArrayList non devono essere sincronizzati poiché ogni thread ha il proprio ArrayList.

Proprio come in JBoss AS, GlassFish crea troppo pigramente un semplice HashMap, in questo caso nel com.sun.ejb.EjbInvocation. Interessante nel caso GlassFish è che la connessione al webservice è più facile da individuare nella fonte.

9

Non posso aiutarti direttamente con le tue domande riguardanti EJBContext, poiché il metodo getContextData è stato aggiunto in JEE6, non c'è ancora molta documentazione a riguardo.

Esiste tuttavia un altro modo per passare i dati contestuali tra bean, interceptor e callback del ciclo di vita utilizzando TransactionSynchronizationRegistry. Il concetto e il codice di esempio possono essere trovati in questo blog post by Adam Bien.

javax.transaction.TransactionSynchronizationRegistry detiene una struttura simile alla mappa e può essere utilizzato per passare lo stato all'interno di una transazione. Funziona perfettamente dal vecchio J2EE 1.4 giorni ed è indipendente dal thread.

Poiché un Interceptor viene eseguito nella stessa transazione di ServiceFacade, lo stato può essere impostato anche in un metodo @AroundInvoke. TransactionSynchronizationRegistry (TSR) può essere iniettato direttamente in un intercettore.

L'esempio ci usa @Resource iniezione per ottenere il TransactionSynchronizationRegistry, ma può anche essere guardato su dal InitialContext in questo modo:

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

Buon consiglio. Lo schema che sto usando per 'EJBContext' è quasi identico - iniettando con' @ Resource' in un posto, mettendo gli oggetti nella mappa, e poi cercando in JNDI da qualche altra parte. Quindi la domanda è se l'oggetto 'EJBContext' è anche indipendente dal thread come il 'TransactionSynchronizationRegistry' – wrschneider

+1

TransactionSynchronizationRegistry ha un limite: ha sempre bisogno di una transazione, ma in alcuni casi è necessario propagare le informazioni senza una transazione – obe6