Ответ 1
На самом деле, я бы сказал, что прослушивание JMS - лучшая причина для сервера приложений. Автономный брокер сообщений не устраняет проблему, так как вам по-прежнему нужен компонент, который прослушивает сообщения. Лучший способ сделать это - использовать MDB. В теории вы можете использовать Springs MessageListenerContainer. Однако это имеет несколько недостатков, таких как JMS, только поддерживает блокирование чтения, а Spring поэтому необходимо развернуть свои собственные потоки, которые полностью не поддерживаются (даже в Tomcat), и могут прерывать транзакции, безопасность, именование (JNDI) и загрузку классов (что в свою очередь может сломать удаленный доступ). Адаптер ресурсов JCA может делать все, что захочет, включая разворот потоков через WorkManager. Вероятно, база данных используется помимо JMS (или другого адресата), в этот момент вам нужны XA-транзакции и JTA, другими словами сервер приложений. Да, вы можете исправить это в контейнере сервлета, но этот момент он становится неотличимым от сервера приложений.
ИМХО - самая большая причина для серверов приложений - это долгие годы после публикации спецификации (которая также требует много лет), пока severs не реализуют спецификацию и не устранят худшие ошибки. Только сейчас, прямо перед публикацией EE 7, мы начинаем появляться серверы EE 6, которые не полностью пронизаны ошибками. Это становится смешно, когда некоторые производители больше не исправляют ошибки в своей линии EE 6, потому что они уже заняты предстоящей линией EE 7.
Edit
Длительное объяснение последнего абзаца:
Java EE во многих местах зависит от контекстной информации. Информация, которая явно не передана в качестве аргумента от сервера/контейнера к приложению, но неявно "там". Например, текущий пользователь для проверки безопасности. Текущая транзакция или соединение. Текущее приложение для поиска классов для ленивой загрузки кода или десериализации объектов. Или текущий компонент (сервлет, EJB,...) для просмотра JNDI. Вся эта информация находится в локаторах потоков, которые сервер/контейнер устанавливает перед вызовом компонента (сервлет, EJB,...). Если вы создаете свои собственные потоки, сервер/контейнер не знает о них, и все функции, основанные на этой информации, больше не работают. Вы можете уйти от этого, просто не используя какие-либо из этих функций в потоках, которые вы создаете.
Некоторые ссылки
http://www.oracle.com/technetwork/java/restrictions-142267.html#threads http://www.ibm.com/developerworks/websphere/techjournal/0609_alcott/0609_alcott.html#spring-4
Если мы проверим спецификацию Servlet 3.0, мы найдем:
2.3.3.3 Асинхронная обработка
Возможности Java Enterprise Edition, такие как раздел 15.2.2, "среда веб-приложений" на стр. 15-174 и раздел 15.3.1 "Распространение идентификатора безопасности в вызовах EJBTM" на стр. 15-176 доступны только для потоков, выполняющих первоначальный запрос или когда запрос отправляется в контейнер через метод AsyncContext.dispatch. Функции Java Enterprise Edition могут быть доступны для других потоков, работающих непосредственно с объектом ответа через метод AsyncContext.start(Runnable).
Это касается асинхронной обработки, но для пользовательских потоков применяются те же ограничения.
public void start (Runnable r). Этот метод заставляет контейнер отправлять поток, возможно из пула управляемых потоков, для запуска указанного Runnable. Контейнер может распространять соответствующую контекстуальную информацию на Runnable.
Опять же, асинхронная обработка, но те же ограничения применяются для пользовательских потоков.
15.2.2 Окружающая среда веб-приложений
Этот тип контейнера сервлета должен поддерживать это поведение при выполнении в потоках, созданных разработчиком, но в настоящее время не требуется для этого. Такое требование будет добавлено в следующей версии этой спецификации. Разработчики предупреждаются, что в зависимости от этой возможности для создаваемых пользователем потоков не рекомендуется, так как она не переносима.
Non-portable означает, что он может находиться на одном сервере, но не в другом.
Если вы хотите получать сообщения с JMS за пределами MDB, вы можете использовать четыре метода на javax.jms.MessageConsumer
:
-
#receiveNoWait()
вы можете это сделать в потоке контейнера, он не блокируется, но это похоже на подглядывание. Если сообщение отсутствует, оно просто возвращаетnull
. Это не очень хорошо подходит для прослушивания сообщений. -
#receive(long)
вы можете это сделать в потоке контейнера, он блокирует. Обычно вам не нужно блокировать ожидания в потоке контейнера. Снова не очень хорошо подходит для прослушивания сообщений. -
#receive()
, это возможно, возможно, бесконечно. Снова не очень хорошо подходит для прослушивания сообщений. -
#setMessageListener()
это то, что вы хотите, получите обратный вызов, когда поступит сообщение. Однако, если библиотека не может подключиться к серверу приложений, это не будет потоком контейнера. Крючки на сервере приложений доступны только через JCA для адаптеров ресурсов.
Итак, да, это может сработать, но это не гарантируется, и есть много вещей, которые могут сломаться.