Использование EJBContext getContextData - это безопасно? - PullRequest
12 голосов
/ 22 декабря 2011

Я планирую использовать EJBContext для передачи некоторых свойств из уровня приложения (в частности, объекта, управляемого сообщениями) в обратный вызов жизненного цикла постоянства, который не может быть напрямую введен или передан параметрами (слушатель сеанса в EclipseLink, жизненный цикл объекта обратный вызов и т. д.), и этот обратный вызов получает EJBContext через JNDI.

Кажется, это работает, но есть ли какие-то скрытые ошибки, такие как безопасность потоков или срок службы объектов, которые я пропускаю? (Предположим, что передаваемое значение свойства является неизменным, как String или Long.)

Пример кода боба

@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 -подобной структуры?

Ответы [ 2 ]

10 голосов
/ 29 декабря 2011

Я думаю, что в целом контракт метода заключается в том, чтобы обеспечить связь между перехватчиками + контекстами веб-сервиса и bean-компонентами. Таким образом, контекст должен быть доступен для всего кода, , если не создан новый контекст вызова . Как таковой, он должен быть абсолютно потокобезопасным.

Раздел 12.6 спецификации EJB 3.1 гласит следующее:

Объект InvocationContext предоставляет метаданные, которые позволяют методы-перехватчики для управления поведением цепочки вызовов. Контекстные данные не разделяются между отдельными бизнес-методами вызовы или события обратного вызова жизненного цикла. Если перехватчики вызываются в результате вызова конечной точки веб-службы карта getContextData возвращает JAX-WS MessageContext

Кроме того, метод getContextData описан в 4.3.3:

Метод getContextData позволяет бизнес-методу, методу обратного вызова жизненного цикла или методу тайм-аута извлечь любой контекст перехватчика / веб-сервисов, связанный с его вызовом.

С точки зрения фактической реализации, 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 интересно то, что соединение с веб-сервисом легче обнаружить в источнике.

8 голосов
/ 28 декабря 2011

Я не могу помочь вам напрямую с вашими вопросами относительно EJBContext, так как метод getContextData был добавлен в JEE6, по-прежнему мало документации по нему.

Однако существует другой способ передачи контекстных данных между EJB, перехватчиками и обратными вызовами жизненного цикла с использованием TransactionSynchronizationRegistry . Концепцию и пример кода можно найти в этом блоге Адама Бьена .

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");
}
...