Использование EJBContext getContextData - это безопасно?
Я планирую использовать EJBContext
, чтобы передать некоторые свойства вокруг уровня приложения (в частности, управляемый сообщениями bean) в обратный вызов жизненного цикла persistence, который нельзя напрямую вводить или передавать параметры (прослушиватель сеанса в EclipseLink, обратный вызов жизненного цикла объекта и т.д.), и этот обратный вызов получает EJBContext
через JNDI.
Это похоже на работу, но есть ли какие-либо скрытые gotchas, такие как безопасность потоков или срок службы объектов, которые мне не хватает? (Предположим, что переданное значение свойства является неизменным, как String или Long.)
Пример bean code
@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
одновременно, и оба ищут EJBContext
из JNDI, они фактически получают разные содержимое карты contextData
?
И как это работает на самом деле - это EJBContext
, возвращенный из поиска JNDI, действительно, объект оболочки вокруг структуры ThreadLocal
в конце?
Ответы
Ответ 1
Я думаю, что в целом контракт метода заключается в том, чтобы обеспечить связь между перехватчиками + контекстами webservice и beans. Таким образом, контекст должен быть доступен для всего кода, если не будет создан новый контекст вызова. Как таковой, он должен быть абсолютно потокобезопасным.
В разделе 12.6 спецификации EJB 3.1 указано следующее:
Объект InvocationContext предоставляет метаданные, которые позволяют перехватчиков для управления поведением цепочки вызовов. Контекстные данные не распространяются на отдельные бизнес-методы вызовы или события обратного вызова жизненного цикла. Если вызываются перехватчики в результате вызова в конечной точке веб-службы карта, возвращаемый getContextData, будет JCX-WS MessageContext
Кроме того, метод getContextData описан в 4.3.3:
Метод getContextData позволяет бизнес-методу, методу обратного вызова жизненного цикла или методу тайм-аута получать любой контекст перехватчика /webservices, связанный с его вызовом.
В терминах реальной реализации JBoss AS выполняет следующие действия:
public Map<String, Object> getContextData() {
return CurrentInvocationContext.get().getContextData();
}
Где CurrentInvocationContext
использует стек, основанный на потоковом локальном списке, чтобы выскочить и нажать текущий контекст вызова.
См. org.jboss.ejb3.context.CurrentInvocationContext. Контекст вызова лениво создает простой HashMap
, как это сделано в org.jboss.ejb3.interceptor.InvocationContextImpl
Glassfish делает что-то подобное. Он также получает вызов, и делает это из диспетчера вызовов, который также использует стек, основанный на списке локальных массивов потоков, чтобы снова щелкнуть эти контексты вызовов.
JavaDoc для реализации GlassFish особенно интересен здесь:
Эта переменная TLS хранит ArrayList. ArrayList содержит Объекты ComponentInvocation, которые представляют стек вызовов по этой теме. Доступ к ArrayList не нужно синхронизировать потому что каждый поток имеет свой собственный ArrayList.
Как и в JBoss AS, GlassFish лениво создает простой HashMap
, в данном случае в com.sun.ejb.EjbInvocation. Интересным в случае GlassFish является то, что соединение webservice легче обнаружить в источнике.
Ответ 2
Я не могу помочь вам с вашими вопросами относительно EJBContext
, так как метод getContextData
был добавлен в JEE6, документация о нем еще мало.
Существует также другой способ передачи контекстных данных между EJB, перехватчиками и обратными вызовами жизненного цикла, используя TransactionSynchronizationRegistry. Концепцию и образец кода можно найти в этом блоге от Адама Биена.
javax.transaction.TransactionSynchronizationRegistry
содержит Map-подобную структуру и может использоваться для передачи состояния внутри транзакции. Он отлично работает со старого J2EE 1,4 дня и не зависит от потока.
Поскольку Interceptor выполняется в той же транзакции, что и ServiceFacade, состояние может быть даже установлено в методе @AroundInvoke
. TransactionSynchronizationRegistry
(TSR) может быть непосредственно введен в Interceptor.
В примере используется @Resource
injection для получения TransactionSynchronizationRegistry
, но его также можно найти с помощью InitialContext
следующим образом:
public static TransactionSynchronizationRegistry lookupTransactionSynchronizationRegistry() throws NamingException {
InitialContext ic = new InitialContext();
return (TransactionSynchronizationRegistry)ic.lookup("java:comp/TransactionSynchronizationRegistry");
}