Что такое mdb java
Перейти к содержимому

Что такое mdb java

  • автор:

JMS сообщения в Java приложении

JMS (Java Message Service) является стандартом обмена сообщениями между приложениями. Java приложения, выполненные по технологии Java SE (standalone) или Java EE (WEB) могут создавать, отправлять и получать JMS сообщения. Программное обеспечение, используемое для передачи сообщений между приложениями по стандарту JMS, формирует очереди сообщений queue.

Отправка сообщений java-приложениями выполняется в асинхронном режиме, т.е. процедура не ждет ответа от получателя. В качестве получателей сообщений, организующих очереди (queue), может быть либо программное обеспечение типа WebSphere MQ, связывающее приложения через канал обмена сообщениями, либо WEB-контейнер типа JBoss, GlassFish, обеспечивающих обмен сообщениями между приложениями контейнера, либо по каналам Интернета с использованием JNDI.

Получение сообщений java-приложением осуществляется либо по «подписке» (технология «издатель-подписчик»), либо из очереди («точка-точка»). Для получения сообщений по подписке необходимо должным образом подключиться к соответствующей очереди. При появлении в очереди сообщений они отправляются всем подписчикам. В модели «точка-точка» для получения сообщений необходимо периодически подключаться к соответствующей очереди и читать в ней сообщения.

В статье рассматриваются примеры отправки и получения сообщений с использованием как WEB-приложения (Java EE), так standalone приложения (Java SE). Описание примеров разделено на две части. На этой странице рассматривается взаимодействие WEB-приложения с провайдером HornetQ. Взаимодействие standalone Java приложения с Websphere MQ рассматривается здесь.

Пример обмена сообщения с сервером JBoss

В первом простейшем WEB-приложении «jms-jboss» для отправки и получения JMS сообщений в качестве контейнера будет использован сервер приложений Wildfly версии 8.2 (ранее JBoss Application Server или JBoss AS).

Настройка сервера приложений Wildfly

Чтобы использовать Wildfly для обмена сообщениями JMS необходимо настроить его файл конфигурации /standalone/configuration/standalone.xml. По умолчанию настройки JMS не включены в конфигурационный файл и их нужно определить вручную. Но можно использовать файл конфигурации standalone-full.xml, в котором сервер включает настройки JMS провайдера HornetQ, обеспечивающего создание соответствующих очередей и обмен сообщениями.

В файл конфигурации standalone-full.xml в раздел секции добавим очередь. Две очереди (ExpiryQueue и DLQ) уже имеются в подразделе . Добавим свою очередь testQueue с JNDI ‘jms/queue/test’ :

Для примера достаточно было добавить один элемент », который работает внутри контейнера. Второй элемент «java:jboss/exported/jms/queue/test» может работать за пределами контейнера, т.е. из другой JVM. Для него обязательным условием является определение в начале наименования «java:jboss/exported/». Можно было бы конечно использовать и существующие очереди (ExpiryQueue и DLQ).

Примечание :
DLQ (Dead Letter Queue) – это локальная очередь, называемая инача как очередь недоставленных сообщений. Такую очередь создают для каждого менеджера очередей, чтобы отлавливать неотправленные сообщения, связанные с проблемами в сети или у получателя.

Если не создать DLQ, то ошибки в приложениях могут отключать каналы. В этом случае не только прекратится получение сообщений в очереди, но может нарушиться работа и провайдера MQ, к примеру, не будут получены и выполнены команды администратора. Рекомендуется проектировать приложения таким образом, чтобы избегать подобных сценариев. Кроме того, следует проводить мониторинг DLQ, поскольку поступающие в эту очередь сообщения чаще всего содержат ошибки.

Чтобы стартовать Wildfly с файлом конфигурации standalone-full.xml из IDE Eclipse необходимо открыть окно ‘JBoss Runtime’ и определить значение ‘Configuration file’. Для этого откройте вкладку Servers (Perspective ‘Java EE’) и дважды щелкните мышью на сервере Wildfly. В открывшемся окне Overview нажмите на ссылку ‘Runtime Enviroment’, в результате чего будет открыто окно ‘JBoss Runtime’ :

Для запуска сервера приложений Wildfly не из IDE Eclipse c конфигурационным файлом standalone-full.xml можно использовать командный файл, в котором определить файл в качестве параметра : ./standalone.sh -c standalone-full.xml

В конце страницы приведен Log сервера Wildfly, в котором показано создание соответствующих очередей и подключение MDB-объекта приложения к адаптеру HornetQ для получения JMS сообщений по подписке.

Описание примера

На следующем скриншоте приведена структура WEB-приложения jms-jboss, включающего :

  • ServiceServlet — сервлет, используемый для отправки и получения JMS сообщений;
  • Sender — отправитель JMS сообщений;
  • Receiver — MDB получатель JMS сообщений;
  • jquery-3.2.1.min.js — библиотека jQuery для асинхронных ajax-вызовов сервлета;
  • index.jsp — интерфейсная страница приложения.

Проект в среде IDE Eclipse с использованием maven представлен на следующем скриншоте.

Листинг сервлета ServiceServlet

Сервлет ServiceServlet вызывается со страницы index.jsp из браузера с использованием jQuery. В качестве параметров сервлет получает ‘prefix’ сообщения и команду ‘mode’. Для отправки JMS сообщения сервлет использует метод sendMessage класса Sender. Отправленные сообщения сервер Wildfly сразу же возвращает подписчику Receiver. Полученные сообщения сервлет читает из статической коллекции Receiver.messages, после чего очищает её.

Отправленные и полученные сообщения сервлет оформляет в виде HTML-кода и возвращает в браузер.

package org.jms.example; import java.io.IOException; import java.io.PrintWriter; import javax.inject.Inject; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class ServiceServlet extends HttpServlet < private static final long serialVersionUID = 1L; @Inject Sender sender; private final String BR = "
"; @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException < String mode = request.getParameter("mode" ); String prefix = request.getParameter("prefix"); if(prefix == null || "".equals(prefix)) prefix = "prefix"; response.setContentType("text/html;charset=UTF-8"); PrintWriter out = response.getWriter(); if (mode.equalsIgnoreCase("send")) < out.print("send messages :" + BR); for (int i = 0; i < 3; i++) < String msg = prefix + " #" + i; out.print("• " + msg + BR); sender.sendMessage(msg); >out.close(); > else < out.print("receive messages :" + BR); if (Receiver.messages.size() >0) < for (int i=0; iReceiver.messages.clear(); > out.close(); > > >

Листинг дескриптора приложения, web.xml

В дескрипторе приложения web.xml описывается сервлет ServiceServlet и его URL (url-pattern) для вызова, а также открываемая по умолчанию страница приложения.

  JMS Application JMSServlet org.jms.example.ServiceServlet  JMSServlet /service  index.jsp   

Листинг класса отправителя сообщений Sender

Класс отправителя создается и «живет» вместе с WEB-приложением согласно аннотации @ApplicationScoped. При инсталляции сразу же определяются context и очередь queue. Сервлет отправляет текстовые сообщения, вызывая метод sendMessage.

package org.jms.example; import javax.annotation.Resource; import javax.inject.Inject; import javax.jms.Queue; import javax.jms.JMSContext; import javax.enterprise.context.ApplicationScoped; @ApplicationScoped public class Sender < @Resource(mappedName = "jms/queue/test") private Queue queue; @Inject private JMSContext context; public void sendMessage(String txt) < context.createProducer().send(queue, txt); >>

Листинг получателя сообщений Receiver

Здесь следует несколько слов сказать о MDB (Message Driven Beans). Объект MDB используется для поддержки асинхронных коммуникаций в приложении, как правило, в сочетании с очередями. Клиент посылает сообщение в очередь, а MDB объект получает эти сообщения из очереди по подписке. Клиент не может вызывать MDB напрямую, связь обеспечивается с помощью сообщений JMS. MDB никогда не отвечает клиенту.

Класс получателя сообщений оформлен как MDB объект с использованием аннотации, в которой определен ряд параметров, включающий JNDI очереди ‘jms/queue/test’. Получатель реализует интерфейс javax.jms.MessageListener, согласно которому переопределяет метод onMessage.

При появлении в очереди ‘jms/queue/test’ сообщения сервер приложений Wildfly сразу же вызовет метод onMessage данного MDB-объекта и передаст ему в качестве параметра сообщение javax.jms.Message, текст которого будет сохранен в коллекции messages. Сервлет имеет прямой доступ к набору полученных сообщений messages с модификаторами public и static.

package org.jms.example; import java.util.List; import java.util.ArrayList; import java.util.logging.Level; import java.util.logging.Logger; import javax.jms.Message; import javax.jms.JMSException; import javax.jms.MessageListener; import javax.ejb.MessageDriven; import javax.ejb.ActivationConfigProperty; @MessageDriven(name = "Receiver", activationConfig = < @ActivationConfigProperty(propertyName = "destinationLookup", propertyValue = "jms/queue/test"), @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"), @ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge") >) public class Receiver implements MessageListener < private Logger LOG = Logger.getLogger(Receiver.class.getName()); public static Listmessages = new ArrayList(); @Override public void onMessage(Message rcvMessage) < try < messages.add(rcvMessage.getBody(String.class)); >catch (JMSException ex) < LOG.log(Level.SEVERE, ex.getMessage(), ex); >> >

Листинг страницы index.jsp

На странице index.jsp определена библиотека jquery-3.2.1.min.js и JavaScript методы, которые используются для ajax-вызова сервлета. Интерфейс страницы включает текстовое поле ‘prefix’ и кнопки ‘Отправить’ и ‘Получить’.

     JMS пример    

Пример JMS с использованием JBoss

Отправьте сообщения JBoss с префиксом

Получите сообщения из очереди JBoss

Интерфейс страницы в браузере представлен на следующем скриншоте.

При нажатии на кнопку ‘Отправить’ вызывается JavaScript процедура sendMessages, определяющая значения параметров (mode, prefix) и вызывающая сервлет для отправки сообщений. Результат выполнения команды в виде набора сообщений, оформленных в HTML-коде, возвращается в функцию success, которая выкладывает его в поле sendJMS на странице. Если префикс не определен, то сервлет будет использовать по умолчанию значение ‘prefix’. Как только сообщения попадут в очередь сервер вернет их объекту ‘Receiver’ по подписке.

При нажатии на кнопку ‘Получить’ вызывается JavaScript процедура receiveMessages, определяющая значение параметры mode и вызывающая сервлет для чтения полученных по подписке сообщений. Результат выполнения команды в виде набора сообщений, оформленных в HTML-коде, возвращается в функцию success, которая выкладывает его в поле receiveJMS на странице.

Старт сервера Wildfly

Сервер приложений Wildfly «логирует» всю информацию. При старте из IDE Eclipse эта информация дополнительно выводится в консоль. Ниже представлена информация (в сокращенном виде), связанная с настройкой сервером JMS очередей и стартом приложения «jms-jboss». В предпоследней строке отображено подключение MDB (Message Driven Bean) ‘Receiver’ к провайдеру HornetQ.

[org.hornetq.core.server] Server is now live [org.hornetq.core.server] HornetQ Server version 2.4.5.FINAL [org.hornetq.core.server] trying to deploy queue jms.queue.ExpiryQueue [org.jboss.as.messaging] Bound messaging object to jndi name java:/jms/queue/ExpiryQueue [org.hornetq.ra] HornetQ resource adaptor started [org.jboss.as.messaging] Bound messaging object to jndi name java:jboss/DefaultJMSConnectionFactory [org.jboss.as.messaging] Bound messaging object to jndi name java:jboss/exported/jms/RemoteConnectionFactory [org.jboss.as.messaging] Bound messaging object to jndi name java:/ConnectionFactory [org.hornetq.core.server] trying to deploy queue jms.queue.testQueue [org.jboss.as.messaging] Bound messaging object to jndi name java:jboss/exported/jms/queue/test [org.jboss.as.messaging] Bound messaging object to jndi name jms/queue/test [org.hornetq.core.server] trying to deploy queue jms.queue.DLQ [org.jboss.as.messaging] Bound messaging object to jndi name java:/jms/queue/DLQ [org.jboss.weld.deployer] Processing weld deployment jms-jboss.war [org.jboss.as.ejb3] (MSC Started message driven bean 'Receiver' with 'hornetq-ra.rar' resource adapter [org.jboss.as.server] Deployed "jms-jboss.war" (runtime-name : "jms-jboss.war")

Скачать пример

Исходный код рассмотренного примера в виде проекта Eclipse с использованием Maven можно скачать здесь (994 Кб).

Примеры взаимодействия Java приложения c Websphere MQ по отправки и чтению JMS сообщений представлены здесь.

25. Message Driven Beans (mdb), жизненный цикл компонентов. Особенности разработки, применения и функционирования mdb, реализующие методы (примеры разработки клиента и серверной части). Настя

Бин, управляемый сообщениями (MDB), — это бин, который позволяет приложениям J2EE асинхронно обрабатывать сообщения. Он действует, как слушатель сообщений JMS, который подобен слушателю событий, за исключением того, что вместо событий, он принимает сообщения. MDB может обрабатывать JMS сообщения или другие виды сообщений.

Отличия mdb:

· MDB не сохраняют состояния клиента;

· Все MDB равноправны, т.е. EJB контейнер может присвоить сообщение любому бину, управляемому сообщениями;

· Один MDB может обрабатывать сообщения от множества клиентов;

Сообщения могут отправляться любым компонентом J2EE — клиентским приложением, другим корпоративным бином или Web-компонентом — или при помощи приложения JMS или системы, которая не использует технологию J2EE.

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

MDB – это просто MessageListener: класс реализует javax.ejb.MessageDrivenBean и javax.jms.MessageListener.

Первый интерфейс имеет два метода: setMessageDrivenContext и ejbRemove.

Второй интерфейс имеет один метод onMessage. Именно в методе onMessage() выполняется вся прикладная логика. Спецификация требует также создания метода ejbCreate без параметров.

Клиент не создает объектов MDB. Контейнер сам решит, когда и сколько ему требуется MDB для обработки сообщений из данного destination (в данном случае это: либо queue, либо topic – в зависимости от используемой модели: javax.jms.Queue или javax.jms.Topic).

Метод setMessageDrivenContext() класса ReservationProcessorBean устанавливает поле экземпляра ejbContext в значение MessageDrivenContext, которое было передано в метод. Кроме этого он получает ссылку на JNDI ENC, которую сохраняет в jndiContext. MDB может иметь поля экземпляра, похожие на поля экземпляра сеансового компонента без состояния. Значения этих полей сохраняются в экземпляре MDB в течение всей его жизни и могут многократно использоваться каждый раз, когда он обрабатывает новое сообщение. В отличие от сеансовых компонентов с состоянием, у MDB нет состояния диалога, и они не предназначаются для одного клиента JMS. Экземпляры MDB применяются для обработки сообщений от нескольких разных клиентов JMS и связаны не с клиентом, а с определенной темой или очередью, от которых они принимают сообщения.

Жизненный цикл экземпляра MDB имеет два состояния: «не существует» и «пул готовых методов». Пул готовых методов похож на пул экземпляров, используемый для сеансовых компонентов без состояния. Некоторые производители могут не использовать пул для MDB экземпляров, а вместо этого создавать и уничтожать экземпляры для каждого нового сообщения. Когда сообщение направлено экземпляру контейнером, MessageDrivenContext экземпляра MDB изменяется, чтобы отразить новый контекст транзакции. Экземпляр, закончивший обработку, сразу же становится доступным для обработки нового сообщения. Экземпляры компонентов переводятся из пула готовых методов в состояние «не существует», когда они становятся ненужными серверу. Это происходит, когда сервер принимает решение уменьшить общий размер пула готовых методов, удаляя из памяти один или несколько экземпляров. Этот процесс начинается с вызова метода ejbRemove() экземпляра.

//имя topic, на который подписан бин

public class MDBExample implements MessageListener

//метод, вызываемый при получении нового сообщения

public void onMessage(Message msg)

TextMessage message = (TextMessage)msg;

//считываем свойство из соответствующего поля, заданное вручную в consumer

System.out.println(«FROM MDB — client type IS » + message.getStringProperty(«clientType»));

//считываем само сообщение

System.out.println(«FROM MDB — payload IS» + message.getText());

> catch (JMSException ex)

26. EJB-сервер и EJB-контейнер. Роль и использование в приложениях интерфейсов EJBHome и EJBObject. Упрощения модели программирования EJB 3Х. Особенности реализации EJB версии 3Х. ПОЛИНА

Существуют также интерфейсы EJBHome и EJBObject, который вы используете как базовый интерфейс, когда определяете собственное представление компонента (если оно вам нужно) — по этой причине EJBHome и EJBObject наследуются от интерфейса Remote RMI. Оставшиеся два интерфейса, EJBLocalHome и EJBLocalObject, вы используете для предоставления локального представления вашего компонента (опять таки, предполагается, что вам нужно это локальное представление)

Home-интерфейс и Home-объект

Когда у клиента EJB возникает потребность воспользоваться услугами EJB, он с помощью home-интерфейса создаёт EJB. Клиент использует один из методов create(), которые определяет home-интерфейс. Реализация home-интерфейса осуществляется с помощью объекта, называющегося home-объектом. Экземпляр такого home-объекта создаётся на сервере и в качестве factory (построителя) предоставляется клиенту для создания бина.

Интерфейс Time Home (реализация интерфейса Home)

public interface TimeHome extends EJBHome

public static final String COMP_NAME= «java:comp/env/ejb/Time»;

public static final String JNDI_NAME= «Time»;

public Time create () throws CreateException,RemoteException;>

Home interface

У каждой EJB-компоненты есть то, что называют «родной интерфейс» (home interface), который определяет методы создания, инициализации, удаления и (в случае entity beans) поиска экземпляров EJB-компонент на стороне сервера. «Родной интерфейс», по сути, описывает возможные взаимодействия между компонентой и контейнером, а конкретно — описанные выше.

«Родной интерфейс» для EJB-компоненты наследуется от интерфейса javax.ejb.EJBHome, который представляет базовую функциональность для взаимодействия между контейнером и компонентой. Все методы этого интерфейса должны быть RMI-совместимы. Интерфейс также описывает один или более create() методов, которые все называются ключевым словом create, но тело которых различно. Все create методы возвращают объект с «внешним» (remote) для данной компоненты интерфейсом.

EJBObject

EJBObject — это видимый в сети объект с собственной структурой и наполнением, действующий как proxy бина. Имеющийся у бина remote-интерфейс расширяет интерфейс javax.ejb.EJBObject, делая таким образом класс EJBObject специфическим для данного класса бина. Для каждого бина EJB существует стандартный класс EJBObject.

Корпоративные компоненты, записанные EJB 3.0 и более поздним API, не требуют удаленного интерфейса, который расширяет интерфейс EJBObject. Удаленный деловой интерфейс может использоваться вместо этого.

Единственное требование такое: этот интерфейс наследуется от javax.ejb.EJBObject и все его методы выбрасывают RemoteException.

public interface Movie extends EJBObject

public Integer getId () throws RemoteException;

7
Message-Driven Beans

A Message-Driven Bean (MDB) is a Java Messaging Service (JMS) message listener that can reliably consume messages from a queue or a subscription of a topic. The advantage of using an MDB instead of a JMS message listener is that you can use the asynchronous nature of a JMS listener with the benefit of the EJB container performing the following:


    The consumer is created for the listener. That is, the appropriate QueueReceiver or TopicSubscriber is created by the container.

An MDB is an easy method for creating a JMS message listener.

The following sections discuss the tasks in creating an MDB in Oracle9 i AS Containers for J2EE (OC4J) and demonstrate MDB development with a basic configuration to use Oracle JMS as the resource provider.

Download the MDB example from the OC4J sample code page on the OTN web site.

MDB Overview

An MDB is a unique EJB whose function is to read or write JMS messages from a JMS Destination (topic or queue).

The OC4J MDB interacts with Oracle JMS, which must be installed and configured appropriately. Oracle JMS is installed and configured on an Oracle database. Within this database, the appropriate queue or table is created.


    The MDB opens a JMS connection to the database using a data source with a username and password. The data source represents the Oracle JMS resource provider and uses a JDBC driver to facilitate the JMS connection.

Figure 7-1 Demonstration of an MDB Interacting with an Oracle JMS Destination

Creating MDBs

MDBs interact with queues and topics furnished by the Oracle JMS resource provider. A full description of how to use this resource provider is discussed in the JMS chapter in the Oracle9iAS Containers for J2EE Services Guide .

The JMS chapter details the following steps that enable each resource provider:


    Install and configure the resource provider. For Oracle JMS, this includes the following:


    Create an RDBMS user through which the MDB connects to the database. Grant this user appropriate access privileges to perform Oracle JMS operations.

To create an MDB that uses the resource provider, perform the following steps:


    Implement the bean, which includes the following:


    The bean class must implement the javax.ejb.MessageDrivenBean and javax.jms.MessageListener interfaces, which includes the following:


    the onMessage method in the MessageListener interface


    an ejbCreate method


    Define the JMS connection factory and Destination used in the EJB deployment descriptor. Define if any durable subscriptions are used.

The following sections demonstrate a simple MDB, using Oracle JMS as the resource provider. For directions on configuring other resource providers, see the JMS chapter in the Oracle9iAS Containers for J2EE Services Guide .

Install And Configure The Resource Provider

Before you can use the MDB within the application, you must choose and configure a resource provider for the JMS Destination objects used by the MDB. The following sections discuss how to configure Oracle JMS.

Create User and Assign Privileges

The following example creates MYUSER with privileges required for Oracle JMS operations:

create user MYUSER identified by MYPASSWORD; grant connect, resource to MYUSER; grant execute on sys.dbms_aqadm to MYUSER; grant execute on sys.dbms_aq to MYUSER; grant execute on sys.dbms_aqin to MYUSER; grant execute on sys.dbms_aqjms to MYUSER; connect MYUSER/MYPASSWORD;

You may need to grant other privileges, such as two-phase commit (requires FORCE ANY TRANSACTION ) or system administration privileges, based on what the user needs.

Create JMS Destination Objects

Each resource provider requires its own method for creating the JMS Destination object. Refer to the Oracle9i Application Developer’s Guide — Advanced Queuing for more information on the DBMS_AQADM packages and Oracle JMS messages types. For our example, Oracle JMS requires the following methods:

The SQL for creating the tables for the Oracle JMS example is included in the MDB example available from OTN.

  1. Create the tables that handle the JMS Destination (queue or topic).

In Oracle JMS, both topics and queues use a queue table. The Oracle JMS example within this chapter creates two tables: QTque for a queue and QTtpc for a topic.

To create the queue table, execute the following SQL:

DBMS_AQADM.CREATE_QUEUE_TABLE( Queue_table => 'QTque', Queue_payload_type => 'SYS.AQ$_JMS_BYTES_MESSAGE', multiple_consumers => false);

The type of message is defined as one of the following:

The third parameter denotes whether there are multiple consumers or not; thus, is always false for a queue and true for a topic.

To create the topic table, execute the following SQL:

DBMS_AQADM.CREATE_QUEUE_TABLE( Queue_table => 'QTtpc', Queue_payload_type => 'SYS.AQ$_JMS_BYTES_MESSAGE', multiple_consumers => TRUE);

The Oracle JMS example within this chapter requires a single topic with two subscribers— topic1 with MDBSUB and MDBSUB2 —and a single queue— queue1 .

The following creates a topic called topic1 within the topic table QTtpc with max retries set to 2. After creation, two durable subscribers are added to the topic. Finally, the topic is started.

DBMS_AQADM.CREATE_QUEUE( 'topic1', 'QTtpc'); DBMS_AQADM.ADD_SUBSCRIBER('topic1', sys.aq$_agent('MDSUB', null, null)); DBMS_AQADM.ADD_SUBSCRIBER('topic1', sys.aq$_agent('MDSUB2', null, null)); DBMS_AQADM.START_QUEUE('topic1');

The following creates a queue called queue1 within the queue table QTque with max retries set to 2. After creation, the queue is started.

DBMS_AQADM.CREATE_QUEUE( 'queue1' , 'QTque'); DBMS_AQADM.START_QUEUE('queue1');
Configure the DataSource

Configure the Oracle JMS resource provider by configuring a data source. The topics and queues connect to the database and use database tables and queues to facilitate messaging.

The type of data source you use depends on the functionality you want.

Transactional Functionality

For no transactions or single-phase transactions, you can use either an emulated or non-emulated data sources. For two-phase commit transaction support, you can use only a non-emulated data source.

Example 7-1 Non-Emulated With Thin JDBC Driver

The following example contains a non-emulated data source that uses the thin JDBC driver. It can support a two-phase commit transaction; it cannot support session pooling.

The example is displayed in the format of an XML definition; see the Oracle9iAS Containers for J2EE User’s Guide for directions on adding a new data source to the configuration.

name="myDS" location="jdbc/MyDS" connection-driver="oracle.jdbc.driver.OracleDriver" username="myuser" password="mypasswd" url="jdbc:oracle:thin:@myhost.foo.com:1521:mydb" inactivity-timeout="30" />
Configure the Resource Provider

Identify the JNDI name of the data source that is to be used as the resource provider within the element.


    If this is to be the resource provider for all applications (global), configure the global application.xml file.

The following code sample shows how to configure the resource provider using XML syntax for Oracle JMS.


    class attribute—The Oracle JMS resource provider is implemented by the oracle.jms.OjmsContext class, which is configured in the class attribute.

The following example demonstrates that the data source identified by » jdbc/CartEmulatedDS » is to be used as the Oracle JMS resource provider. This JNDI name is identified in the ejb-location element in Example 7-1 .

  OJMS/AQ   

Bean Class Implementation

Most MDBs receive messages from a queue or a topic, then invoke an entity bean to process the request contained within the message.

The following example is a MessageBean MDB. Its functionality is to print out a message sent to it through a durable topic. The topic is identified in the deployment descriptors.

As an MDB, it is responsible for the following:


    implements the javax.ejb.MessageDrivenBean and javax.jms.MessageListener interfaces

package cart.ejb; import com.evermind.server.ThreadState; import java.io.Serializable; import java.rmi.RemoteException; import javax.ejb.MessageDrivenBean; import javax.ejb.MessageDrivenContext; import javax.ejb.CreateException; import javax.naming.*; import javax.transaction.*; import javax.jms.*; import oracle.AQ.*; import oracle.jms.*; public class MessageBean implements javax.ejb.MessageDrivenBean, javax.jms.MessageListener private transient MessageDrivenContext mdbCtx = null; /* Constructor, which is public and takes no arguments.*/ public MessageBean() < >/* setMessageDrivenContext method */ public void setMessageDrivenContext(MessageDrivenContext mdc) /* As with all EJBs, you must set the context in order to be able to use it at another time within the MDB methods. */ this.mdbCtx = mdc; > /* ejbCreate method, declared as public (but not final or * static), with a return type of void, and with no arguments. */ public void ejbCreate() throws Exception /* no implementation is necessary for this MDB */ /* An MDB does not carry state for an individual client. However, you can retrieve state for use across many calls for multiple clients - state such as an entity bean reference or a database connection. If so, retrieve these within the ejbCreate and remove them in the ejbRemove method. */ > /* ejbRemove method */ public void ejbRemove() /* no implementation is necessary for this MDB*/ > /** * onMessage method * Casts the incoming Message to a TextMessage and displays * the text. */ public void onMessage(Message msg) /* The whole point for this message MDB is to receive and print messages. It is not complicated, but it shows how MDBs are set up to receive JMS messages from queues and topics. */ BytesMessage msgBytes = null; try /* This message was created as a JMS BytesMessage. */ if (msg instanceof BytesMessage) /* Convert the BytesMessage into printable text. */ msgBytes = (BytesMessage) msg; byte[] msgdata = ((AQjmsBytesMessage) msgBytes).getBytesData(); String txt = new String(msgdata); /* Print out message */ System.out.println("Message received 1009762"> > > catch (Exception e) throw new RuntimeException("onMessage throws exception"); > > >

Configure Deployment Descriptors

The deployment descriptors define MDB configuration in the element.


    The EJB deployment descriptor ( ejb-jar.xml ) specifies whether a queue or a topic is used. This example uses a durable topic.

EJB Deployment Descriptor for the MDB

Within the EJB deployment descriptor ( ejb-jar.xml ), define the MDB name, class, JNDI reference, and JMS Destination type (queue or topic) in the element. If a topic is specified, you must also define whether it is durable.

The following example demonstrates the deployment information for the MessageBean MDB in the element, as follows:


    MDB name specified in the element.

ejb-jar.xml
   A demo cart bean package. A simple cart jar . MessageBeanTpc MessageBeanTpc cart.ejb.MessageBean Container javax.jms.Topic Durable   .   MessageBeanTpc onMessage  Required  .  
OC4J-Specific Deployment Descriptor

Once you have configured the MDB and the JMS Destination type, inform the container which JMS Destination to associate with the MDB. To identify the Destination that is to be associated with the MDB, map the Destination location and connection factory to the MDB through the element in the orion-ejb-jar.xml file.

The following is the orion-ejb-jar.xml deployment descriptor for the MessageBean example. It maps an Oracle JMS Topic to the MessageBean MDB, providing the following:


    MDB name, as defined in the in the EJB deployment descriptor, is specified in the name attribute.

For this example, the resource provider name is defined in the element in the application.xml file as cartojms1 . In this example, the topic name is topic1 .

After you specify all of these in the element, the container knows how to map the MDB to the correct JMS Destination .

 connection-factory-location= "java:comp/resource/cartojms1/TopicConnectionFactories/aqTcf" name="MessageBeanTpc" destination-location="java:comp/resource/cartojms1/Topics/topic1" subscription-name="MDBSUB" listener-threads=50 transaction-timeout=172800> . 

Deploy the Entity Bean

Archive your EJB into a JAR file. You deploy the MDB in the same way as the session bean, which «Prepare the EJB Application for Assembly» and «Deploy the Enterprise Application to OC4J» describe.

Accessing MDBs

The client sends a message to the MDB through a JMS Destination . The MDB is associated with the JMS Destination by the container.

To send a JMS message to an MDB, perform the following:


    Retrieve both the configured JMS Destination and its connection factory using a JNDI lookup.

The following code sends a message over a topic to the MessageBean MDB.

Context ic = new InitialContext(); /*1. Retrieve an Oracle JMS Topic and connection factory through JNDI */ topic = (Topic) ic.lookup("java:comp/resource/ojms/Topics/topic1"); /*Retrieve the Oracle JMS Topic connection factory */ topicConnectionFactory = (TopicConnectionFactory) ic.lookup ("java:comp/resource/ojms/TopicConnectionFactories/aqTcf"); /*2. Create a Topic connection */ topicConnection = topicConnectionFactory.createTopicConnection(); /*3. Create a Topic session over the connection */ topicSession = topicConnection.createTopicSession(true, Session.AUTO_ACKNOWLEDGE); /*4. Create a publisher to send a message to the MDB -- The createPublisher method is invoked off of the session object, but requires the retrieved topic as its input. */ topicPublisher = topicSession.createPublisher(topic); /*5. Create the message to send to the MDB */ message = topicSession.createTextMessage(); for (int i = 0; i message.setText("This is message " + (i + 1)); System.out.println("Sending message: " + message.getText()); /*6. Send the message using the topic publisher */ topicPublisher.publish(message); > /*7. After message is sent, close the connection */ topicConnection.close();

Using Logical Names in the JMS JNDI Lookup

If you have another EJB acting as the MDB client, you can retrieve the connection factory and the JMS Destination using logical references that have been configured in the client-side deployment descriptor.

The following defines logical names in the client-side deployment descriptor. If the client is a true Java client, this would be in its application-client.xml file. If the client is another EJB, these additions would be added in the ejb-jar.xml file.


    The connection factory is defined in a element.


    The logical name of the connection factory is defined in the element.


    The logical name of the topic or queue is defined in the element.

The following shows an example of how to define a queue and a topic.

 jms/Queue/senderQueueConnectionFactory javax.jms.QueueConnectionFactory Container Shareable  jms/Queue/senderQueue javax.jms.Queue  jms/Topic/senderTopicConnectionFactory javax.jms.TopicConnectionFactory Container Shareable  jms/Topic/senderTopic javax.jms.Topic  

Then, you map the logical names to actual names in the OC4J deployment descriptor. If the client is a true Java client, these additions would be within the orion-application-client.xml . If the client is another EJB, these additions are added to the orion-ejb-jar.xml .

The logical names in the client’s deployment descriptor are mapped as follows:


    The connection factory defined in the element is mapped to its JNDI name in the element.

The following example maps the logical names for the connection factories, topic and queue defined above in the client deployment descriptor to their actual JNDI names. Specifically, the topic defined logically as » jms/Topic/senderTopic » in the ejb-jar.xml file is mapped to its JNDI name of » java:comp/resource/cartojms1/Topics/topic1 .»

  name="jms/Topic/senderTopicConnectionFactory" 
location="java:comp/resource/cartojms1/TopicConnectionFactories/aqTcf">
name="jms/Topic/senderTopic"
location="java:comp/resource/cartojms1/Topics/topic1"> name="jms/Queue/senderQueueConnectionFactory"
location="java:comp/resource/cartojms1/QueueConnectionFactories/aqQcf"> name="jms/Queue/senderQueue"
location="java:comp/resource/cartojms1/Queues/queue1">

Once the mapping is complete, you can modify your JNDI lookup to use the logical name, as follows:

Context ic = new InitialContext(); /*Retrieve an Oracle JMS Topic and connection factory through JNDI */ topic = (Topic) ic.lookup("jms/Topic/senderTopic"); /*Retrieve the Oracle JMS Topic connection factory */ topicConnectionFactory = (TopicConnectionFactory) ic.lookup ("jms/Topic/senderTopicConnectionFactory");


Copyright © 2002 Oracle Corporation.
All Rights Reserved.

Работа с JMS сообщениями и MDB в JEE

Работа с сообщениями подразумевает взаимодействие между компонентами системы посредством передачи сообщений. JMS позволяет реализовать это взаимодействие в java приложении, а MDB бины позволяют асинхронно обрабатывать получаемые сообщения на сервере приложений без дополнительных усилий по асинхронной обработке.

Ниже представлен простой пример обработки JMS сообщения с помощью MDB.

Немного теории

Для работы с сообщениями используется вспомогательное программное обеспечение, обычно входящее в поставку сервера приложений.
Компоненты системы могут посылать сообщения (producer) и получать их (consumer). Сообщение
отправляет producer на пункт назначения (destination), являющимся на сервере queue или topic, после чего consumer может забрать оттуда сообщение
В зависимости от того, какой тип имеет destination, разделяют две модели работы с сообщениями.

Первая модель — Point-to-Point

В случае если на сервере destination имеет тип queue, то сообщение, которое отправил producer, получает единственный consumer. Если на эту очередь сообщений подписано несколько получателей, то сообщение получит только один из них.

Вторая модель — Publish-subscribe

В случае если на сервере destination имеет тип topic, то одно сообщение может быть прочитано неограниченным количеством consumer, подписанных на этот на этот destination.

Структура JMS сообщения

Сообщение состоит из заголовка, поля свойств и тела.
Заголовок хранит мета информацию сообщения, заполняемую автоматически.
Поле свойств схоже с заголовком, но оно заполняется программно, и позже получатель сможет прочитать эту информацию.
Тело содержит полезную нагрузку сообщения. Тип нагрузки определяется при создании сообщения. Конкретные типы унаследованы от интерфейса javax.jms.Message

Создание очереди на сервере.

Для примера создадим topic на сервере. Использовать я буду glassfish 3.1.
Для начала создадим Connection Factory. Возможны несколько типов в зависимости от того, какой тип очереди сообщений будет использоваться.

Затем создаем destination с указание типа.

Создание отправителя сообщений

В данном случае producer будет находиться на сервере приложений. В случае если вам необходимо отправлять сообщения из отдельного клиента, то необходимо будет стандартным образом получить доступ к объектам по их JNDI имени из контекста.

//получаем ресурсы сервера для отправки сообщений @Resource(name="jms/TutorialPool") private ConnectionFactory connectionFactory; @Resource(name="jms/TutorialTopic") private Destination destination; public String getEnterString() < return enterString; >public void sendString(String enterString) < try < //создаем подключение Connection connection = connectionFactory.createConnection(); Session session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE); MessageProducer producer = session.createProducer(destination); TextMessage message = session.createTextMessage(); //добавим в JMS сообщение собственное свойство в поле сообщения со свойствами message.setStringProperty("clientType", "web clien"); //добавляем payload в сообщение message.setText(enterString); //отправляем сообщение producer.send(message); System.out.println("message sent"); //закрываем соединения session.close(); connection.close(); >catch (JMSException ex) < System.err.println("Sending message error"); ex.printStackTrace(); >> 
Message-Driven Bean

Для обработки приходящих сообщений на сервере мы будем использовать MDB.

Сообщения можно было бы получать и обрабатывать и с помошью pojo, выступающего как consumer. Но использование MDB позволит параллельно обрабатывать сообщения, не заботясь о сложности асинхронной обработки и дополнительного кода для подписки на очередь сообщений.
Асинхронная обработка реализуется через пул объектов, из которых на обработку сообщения сервер выделят объекты при необходимости.

Для реализации MBD достаточно унаследовать бин от интерфейса javax.jms.MessageListener, реализуя метод onMessage(), и аннотировать соответствующим образом класс.

Сделаем пример MDB, который выводит в консоль сервера информацию о поступившем сообщении.

@MessageDriven( //имя topic, на который подписан бин mappedName="jms/TutorialTopic", name = "ExampleMDB") public class MDBExample implements MessageListener < //метод, вызываемый при получении нового сообщения @Override public void onMessage(Message msg) < try < TextMessage message = (TextMessage)msg; //считываем свойство из соответствующего поля, заданное вручную в consumer System.out.println("FROM MDB - client type IS " + message.getStringProperty("clientType")); //считываем само сообщение System.out.println("FROM MDB - payload IS" + message.getText()); >catch (JMSException ex) < ex.printStackTrace(); >> > 

В onMessage метод добавляется необходимая бизнес логика, в зависимости от типа сообщения, его содержания и тд.
При необходимости, для ручной обработки сообщений можно самостоятельно создать обработчика.
Например так:

 @Resource(name="jms/TutorialPool") private ConnectionFactory connectionFactory; @Resource(name="jms/TutorialTopic") private Destination destination; void onMessage() < try < Connection connection = connectionFactory.createConnection(); Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); MessageConsumer consumer = session.createConsumer(destination); connection.start(); while(true)< Message msg = consumer.receive(); //обработка сообщения >//закрыть connection > catch (JMSException ex) < ex.printStackTrace(); >> 

Для более подробного изучения JMS и EJB в целом, могу рекомендовать книги:
EJB 3 in Action — Debu Panda, Reza Rahman, Derek Lane
Пару книг от Adam Bien

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *