Java EE 7 была недавно принята Исполнительным комитетом . Это означает, что скоро у нас должны появиться серверы приложений Java EE 7 на рынке. Одной из спецификаций, составляющих Java EE 7, является JMS 2.0 . Некоторые интересные улучшения были введены с версии 1.1.
В JMS есть много странных вещей, таких как: метод Connection # createSession (boolean transacted, intcknowledgeMode) .
Первый аргумент метода ( transacted
) определяет, должна ли быть сделана сессия. Приложения Java EE имеют JTA, который заботится о транзакциях. Мы можем выбрать, поддерживаются ли транзакции (по умолчанию) или нет. Так зачем нам этот аргумент тогда?
Второй аргумент метода ( acknowledgeMode
) является целочисленной константой, взятой из объекта Session
. Серьезно, целочисленные константы делают API, кажется, ооочень устаревшим. Наконец, что означают эти параметры в среде Java EE? JavaDoc говорит, что acceptledgeMode игнорируется, если сеанс выполняется.
Что все это значит? Не что иное, как эти аргументы не имеют никакого смысла для производителя JMS, используемого в контексте Java EE. Вот почему они рекомендуют вам использовать (true, 0)
значения параметров, чтобы избежать ненужной путаницы. Это пахнет наследием.
Хорошо, но давайте вернемся к основной теме. Я хотел посмотреть, что нового в мире JMS и как это позволяет мне кодировать проще и удобнее в обслуживании. Я подготовил простое веб-приложение, которое использует JMS 2.0, JAX-RS и EJB (SLSB и MDB), и поместил его в мой репозиторий github.
Предпосылки и инфраструктура
Чтобы запустить этот код, вы должны создать пример очереди. Я настроил его под jms/queue/myqueue
JNDI jms/queue/myqueue
.
Я использовал Glassfish v4 build 87 . Чтобы использовать API Java EE 7, мне нужно было добавить следующую зависимость Maven:
1
2
3
4
5
6
|
<dependency> <groupId>javax</groupId> <artifactId>javaee-api</artifactId> <version> 7.0 -b87</version> <scope>provided</scope> </dependency> |
который находится в этом хранилище:
1
2
3
4
|
<repository> <id>Java EE 7 </id> <url>https: //maven.java.net/content/groups/promoted/</url> </repository> |
И это все для конфигурации.
Конфигурация полезной нагрузки и REST
BusinessObject
— это простой объект, который будет действовать как полезная нагрузка для наших сообщений JMS. Он будет отправлен производителем и получен потребителем. Ничего особенного, так что давайте двигаться дальше. RESTConfiguration
еще проще — он просто определяет префикс конечной точки JAX-RS для нашего приложения. Этот префикс: «/ rest». Вы можете вызывать EJB-компоненты производителя, открывая URL-адрес вашего приложения / rest / seller / jms11 или / rest / seller / jms20 (например, http://localhost:8080/jms2_0_spike/rest/producer/jms20
).
JMS продюсер
Теперь здесь все становится интересным. Ниже вы можете найти код JMS 1.1 для производителя сообщений SLSB JMS:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
@Stateless public class JMS11Producer { @Resource (lookup = "jms/__defaultConnectionFactory" ) private ConnectionFactory connectionFactory; @Resource (lookup = "jms/queue/myqueue" ) private Queue queue; @Path ( "/jms11" ) @GET public String produce() { String status = "OK" ; Connection connection = null ; try { connection = connectionFactory.createConnection(); Session session = connection.createSession( true , 0 ); MessageProducer producer = session.createProducer(queue); BusinessObject payload = new BusinessObject(UUID.randomUUID().toString()); ObjectMessage message = session.createObjectMessage(); message.setObject(payload); producer.send(message); } catch (JMSException e) { status = e.getMessage(); } finally { if (connection != null ) { try { connection.close(); } catch (JMSException e) { status = e.getMessage(); } } } return status; } } |
Несмотря на путаницу состояния результатов JAX-RS, это многообещающий код, который размывает основную ответственность метода. Он должен просто отправить сообщение в очередь, но он делает очень много вещей. Он создает соединение, сеанс (включая неприятные, игнорируемые параметры), сообщение типа объекта, инициализирует его, а затем, наконец, отправляет сообщение в очередь… о да — конечно, не забывайте о проверенных исключениях и вложенных блоках try / catch. Мы можем попытаться оптимизировать его — например, переместив создание соединения в какой- @PostConstruct
метод @PostConstruct
и закрыв его на @PreDestroy
— но все равно остается много ненужного шума.
Теперь давайте посмотрим на функционально тот же код, выраженный в JMS 2.0:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
@Stateless public class JMS20Producer { @Resource (lookup = "jms/queue/myqueue" ) private Queue queue; @Inject private JMSContext jmsContext; @Path ( "/jms20" ) @GET public String produce() { BusinessObject payload = new BusinessObject(UUID.randomUUID().toString()); jmsContext.createProducer().send(queue, payload); return "OK" ; } } |
Довольно аккуратно, а? Намного легче понять, что делает этот метод: он создает полезную нагрузку и отправляет ее в очередь. Вот и все — вот и весь этот метод. Обработка исключений, создание соединения и сеанса, тип сообщения — все сделано для нас. Если эти обязанности можно перенести в контейнер для облегчения жизни разработчика, то почему бы не сделать это?
Давайте рассмотрим несколько функций JMS 2.0, использованных в этом примере:
- нет необходимости в
ConnectionFactory
, - нет необходимости в
Connection
илиSession
, -
JMSContext
— это новый объект, который объединяет возможностиConnection
иSession
; это может быть введено контейнером, - нет проверенных исключений — только
JMSRuntimeException
, - Цепной вызов для создания сообщений облегчает чтение.
JMS Consumer
Consumer
сообщений не сильно изменился со времени JMS 1.1. Это все еще MDB, но теперь легче получить ожидаемую полезную нагрузку из-за приведения сообщений с помощью message.getBody(Clazz)
:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
@MessageDriven (mappedName = "jms/queue/myqueue" ) public class Consumer implements MessageListener { @Override public void onMessage(Message message) { try { // In JMS 1.1: // ObjectMessage objectMessage = (ObjectMessage)message; // BusinessObject payload = (BusinessObject)objectMessage.getObject(); BusinessObject payload = message.getBody(BusinessObject. class ); System.out.println( "Message received: " + payload); } catch (JMSException e) { System.err.println( "Error while fetching message payload: " + e.getMessage()); } } } |
Вывод
Это был просто очень быстрый взгляд на JMS 2.0. Однако мне было интересно посмотреть, сколько чистого кода можно создать по сравнению с JMS 1.1. Более подробную информацию о JMS 2.0 смотрите в его официальной спецификации .