Использование 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");
}