 | Уровень сложности: средний Дэйл де лос Рейес, независмый писатель, вештатный сотрудник
31.07.2007 Данная статья, вторая в цикле, показывает, как взаимодействуют группы ресурсов Apache Geronimo, JNDI и сервис обмена сообщениями Java (JMS). Вдобавок вы узнаете, как создать подключение ресурсов JMS и обратиться к нему из простого Geronimo приложения, используя JNDI.
Введение
JMS - это API, который позволяет компонентам программного обеспечения создавать, посылать, принимать и читать сообщения. Эти сообщения не предназначены для людей. Скорее они являются средством для связи между неравноправными программными приложениями. Приложения, которые взаимодействуют через сообщения, обладают преимуществом нежесткого соединения, так как изменения в одной системе не влияют на вторую до тех пор, пока они соглашаются на один формат сообщений. API JMS реализуется третьей стороной, и такая реализация предоставляет преимущества асинхронной коммуникации и надежной доставки сообщений. Коммуникация является асинхронной, так как компоненты или клиенты могут посылать или получать сообщения, не ожидая подтверждения приема от адресата. Коммуникация также надежна, поскольку при использовании JMS сообщения гарантированно доставляются один и только один раз. Для получения сообщений необходимо, чтобы у приложения имелся приемник JMS, который принимает сообщения в заданном адресе назначения.
Сессионные и entity-компоненты могут посылать JMS-сообщения, но получать их могут только синхронно. Здесь можно поставить ограничения серверным ресурсам, так как компоненты должны ждать получения сообщений. С платформой Java 2, Enterprise Edition (J2EE) 1.3 был введен новый Enterprise JavaBean (EJB) - MDB. Этот EJB работает как JMS-приемник, позволяя Java-приложениям получать сообщения асинхронно.
Утилита Customer Service, включенная в эту статью, - это простое Web-приложение, которое позволяет пользователям сохранять основную информацию о клиентах в базе данных. Вы создадите ее, используя Ant 1.6.5 и Java 1.4.2_10, и установите на Geronimo 1.1 при помощи Tomcat. Вы будете использовать и базу данных Apache Derby и JMS-провайдер ActiveMQ, так как они входят в комплект вместе с Geronimo.
Данная статья является второй частью этого цикла, она продолжает приложение Customer Service, начатое в Части 1. Теперь все интересы клиентов будут анализироваться и сравниваться относительно списка известных субъектов. Эти субъекты делятся на отдельные группы интересов. Если увлечение данного клиента совпадает с одним из известных субъектов, эта группа сохраняется в базе данных вместе с электронным адресом клиента, чтобы обеспечить лучший сервис в будущем. Чтобы реализовать это с помощью JMS, после создания группы посылается сообщение. Для получения этих сообщений используется MDB, который в свою очередь вызывает entity-компонент, чтобы сохранить содержимое в базу данных. Давайте начнем и перейдем к следующему разделу, который описывает, как создавать группу ресурсов JMS, используя консоль Geronimo.
Настройка Apache Geronimo
Консоль Geronimo предоставляет удобный для пользователя способ управления Java-компонентами. После запуска Geronimo вы можете обратиться к нему из http://localhost:8080/console. Имя пользователя и пароль по умолчанию - соответственно system и manager.
Вам нужно создать новую таблицу базы данных, которая должна хранить отдельные группы увлечений, в которых может быть заинтересован пользователь:
- Выберите ссылку DB Manager в нижней части панели Console Navigation слева, чтобы открыть менеджер базы данных Derby.
- Создайте таблицу базы данных, выбрав CustomerServiceDatabase в ниспадающем меню Use DB.
- Затем вырежьте и вставьте содержимое листинга 1 в текстовое окно SQL Command/s и нажмите кнопку Run SQL.
Листинг 1. SQL код CustomerService-Part2.sql
create table interestgroup (
clientid integer primary key,
groupname varchar(20),
emailaddress varchar(30)
);
|
База данных клиентских сервисов теперь должна содержать две таблицы: customers и interestgroup.
Теперь вам надо создать группу ресурсов JMS:
- Выберите ссылку JMS Resources на панели Console Navigation слева.
- Выберите ссылку For ActiveMQ в нижней части панели, чтобы создать новую группу ресурсов JMS.
- На странице Configure Server Connection введите
CustomerServiceConnectionGroup в поле Resource Group Name. ActiveMQ будет использоваться, чтобы посылать и принимать сообщения на тот же Geronimo сервер, на котором размещен пример приложения. Отсюда, поле ServerUrl может остаться таким же, каким оно и является - указывающим на localhost.
- Оставьте остальные поля без изменений и нажмите Next. Следующая страница, Current Progress, показывает текущий прогресс. На этой стадии на ней ничего не должно отображаться.
- Нажмите кнопку Add Connection Factory.
- На странице Select Connection Factory Type выберите
javax.jms.ConnectionFactory
из ниспадающего меню JMS Factory Type, затем нажмите Next.
- На странице Configure Connection Factory введите
CustomerServiceConnectionFactory в поле Connection Factory Name.
- Оставьте остальные поля без изменений и нажмите Next.
- Следующая страница, Current Progress, теперь должна показывать таблицу, отражающую недавно созданный мастер подключений. Нажмите кнопку Add Destination.
- На странице Select Destination Type выберите
javax.jms.Topic
из ниспадающего меню JMS Destination Type, затем нажмите Next.
- На странице Configure Destination введите
CustomerServiceTopic для полей Message Destination Name и Physical Name, и нажмите Next.
- Следующая страница, Current Progress, теперь должна показывать обновленную таблицу, отражающую недавно созданный тематический раздел. Нажмите кнопку Deploy Now. Теперь должна отобразиться страница как на рисунке 1.
Рисунок 1. Недавно созданный JMS ресурс, CustomerServiceConnectionGroup
CustomerServiceConnectionGroup теперь должна быть развернута и отображена в панели JMS Resources. CustomerServiceConnectionFactory и CustomerServiceTopic также здесь отображены и готовы к использованию. Следующий раздел уточняет, как описывать эти объекты в JNDI.
Создание дескрипторов развертывания Geronimo
Конфигурирование файлов описания важно, так как это механизм, позволяющий использовать эти компоненты в Geronimo. Он связывает имена JNDI с данным Java-объектом. Компоненты, развернутые в Geronimo, обычно имеют два файла развертывания: стандартный дескриптор развертывания Java EE и специальный план развертывания Geronimo.
Как уже было упомянуто, после создания группы, совпадающей с интересом клиента, посылается сообщение в пункт назначения JMS. Это сообщение посылается из ProcessCustomerSessionBean. Листинг 2 содержит дополнительные теги, необходимые для задания JMS-ресурса в стандартном Java-дескрипторе развертывания.
Листинг 2. Частичный листинг ProcessCustomerEJB-ejb.xml
<ejb-jar>
<enterprise-beans>
<session>
<resource-ref>
<description>JMS Broker</description>
<res-ref-name>jms/CustomerServiceConnectionFactory</res-ref-name>
<res-type>javax.jms.ConnectionFactory</res-type>
<res-auth>Container</res-auth>
</resource-ref>
<resource-env-ref>
<resource-env-ref-name>jms/CustomerServiceTopic</resource-env-ref-name>
<resource-env-ref-type>javax.jms.Topic</resource-env-ref-type>
</resource-env-ref>
</session>
</enterprise-beans>
</ejb-jar>
|
Два дополнительных тега - это единственное, что нас интересует. Тег <resource-ref> задает имя JNDI для пункта связи JMS. Пункт связи отвечает за создание соединений с JMS-провайдером, которым в данном случае является ActiveMQ. Тег <resource-env-ref> задает имя JNDI для тематического раздела JMS. Тематический раздел JMS - это адрес назначения, на который посылаются и с которого получаются сообщения. Листинг 3 охватывает специальный сопроводительный Geronimo-файл для данного сессионного компонента.
Листинг 3. Частичный листинг ProcessCustomerEJB-openejb.xml
<openejb-jar xmlns="http://www.openejb.org/xml/ns/openejb-jar-2.1"
xmlns:naming="http://geronimo.apache.org/xml/ns/naming-1.1"
xmlns:security="http://geronimo.apache.org/xml/ns/security-1.1"
xmlns:dep="http://geronimo.apache.org/xml/ns/deployment-1.1">
<dep:environment>
<dep:moduleId>
<dep:groupId>default</dep:groupId>
<dep:artifactId>ProcessCustomerSessionBean</dep:artifactId>
<dep:version>1.0</dep:version>
<dep:type>jar</dep:type>
</dep:moduleId>
<dep:dependencies/>
<dep:hidden-classes/>
<dep:non-overridable-classes/>
</dep:environment>
<enterprise-beans>
<session>
<resource-ref>
<ref-name>jms/CustomerServiceConnectionFactory</ref-name>
<resource-link>CustomerServiceConnectionFactory</resource-link>
</resource-ref>
<resource-env-ref>
<ref-name>jms/CustomerServiceTopic</ref-name>
<message-destination-link>CustomerServiceTopic</message-destination-link>
</resource-env-ref>
</session>
</enterprise-beans>
</openejb-jar>
|
Снова нас интересуют два дополнительных тега. <resource-ref> связывает имя JNDI с CustomerServiceConnectionFactory, созданным в консоли Geronimo в предыдущем разделе. Аналогично, тег <resource-env-ref> связывает имя JNDI с тематическим разделом JMS, CustomerServiceTopic. Это все, что вам нужно, чтобы сделать JMS-ресурсы доступными в JNDI из сессионного компонента. Листинг 4 охватывает стандартный дескриптор для MDB, ServiceMonitorMessageBean.
Листинг 3. Частичный листинг ServiceMonitorEJB-ejb.xml
<ejb-jar xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
version="2.1">
<enterprise-beans>
<message-driven>
<ejb-name>ServiceMonitorMessageBean</ejb-name>
<ejb-class>com.service.customer.ejb.ServiceMonitorMessageBean</ejb-class>
<messaging-type>javax.jms.MessageListener</messaging-type>
<transaction-type>Container</transaction-type>
<activation-config>
<activation-config-property>
<activation-config-property-name>destination</activation-config-property-name
>
<activation-config-property-value>CustomerServiceTopic</activation-config-
property-value>
</activation-config-property>
<activation-config-property>
<activation-config-property-name>destinationType</activation-config-property-
name>
<activation-config-property-value>javax.jms.Topic</activation-config-
property-value>
</activation-config-property>
</activation-config>
<ejb-ref>
<ejb-ref-name>ejb/InterestGroupEntityBean</ejb-ref-name>
<ejb-ref-type>Entity</ejb-ref-type>
<home>com.service.customer.ejb.InterestGroupHome</home>
<remote>com.service.customer.ejb.InterestGroup</remote>
</ejb-ref>
</message-driven>
</enterprise-beans>
</ejb-jar>
|
Здесь нас интересует несколько тегов. Во-первых, тег <ejb-name> задает имя MDB. Тег <ejb-class> указывает полностью определенное имя реализуемого класса. В отличие от сессионного или entity-компонента, MDB не требуется home/localhome или remote/local интерфейс. Требуется только реализация компонента. Тег <messaging-type> задает класс приемника JMS-сообщений.
Тег <activation-config> называет адрес назначения JMS, куда будут посылаться сообщения. Он также задает тип домена сообщений, который ожидает этот адрес. Обычно, это тема или очередь для представления двух моделей сообщений: публикация/подписка или двухточечная (point-to-point), соответственно. Наконец, тег <ejb-ref> объявляет JNDI-имя EJB, используемого в MDB. Соответствующий специальный Geronimo план в листинге 5.
Листинг 5. Частичный листинг ServiceMonitorEJB-openejb.xml
<openejb-jar xmlns="http://www.openejb.org/xml/ns/openejb-jar-2.1"
xmlns:dep="http://geronimo.apache.org/xml/ns/deployment-1.1">
<dep:environment>
<dep:moduleId>
<dep:groupId>default</dep:groupId>
<dep:artifactId>ServiceMonitorEJB</dep:artifactId>
<dep:version>1.0</dep:version>
<dep:type>jar</dep:type>
</dep:moduleId>
<dep:dependencies>
<dep:dependency>
<dep:groupId>console.jms</dep:groupId>
<dep:artifactId>CustomerServiceConnectionGroup</dep:artifactId>
<dep:version>1.0</dep:version>
<dep:type>rar</dep:type>
</dep:dependency>
</dep:dependencies>
<dep:hidden-classes/>
<dep:non-overridable-classes/>
</dep:environment>
<enterprise-beans>
<message-driven>
<ejb-name>ServiceMonitorMessageBean</ejb-name>
<resource-adapter>
<resource-link>CustomerServiceConnectionGroup</resource-link>
</resource-adapter>
<ejb-ref>
<ref-name>ejb/InterestGroupEntityBean</ref-name>
<ejb-link>InterestGroupEntityBean</ejb-link>
</ejb-ref>
</message-driven>
</enterprise-beans>
</openejb-jar>
|
<dep:moduleId> задает полное имя этого компонента. Тег <dep:dependencies> показывает зависимость от JMS-ресурса, CustomerServiceConnectionGroup. Вы можете найти этот компонент в geronimo-1.1/repository. Подобно сессионному или entity-компоненту, тег <ejb-name> должен соответствовать тегу <ejb-name> в стандартном дескрипторе предыдущего листинга. Тег <resource-adapter> называет ресурс, который следует использовать, что в данном случае представляет собой JMS-ресурс. Наконец, тег <ejb-ref> привязывает имя JNDI к EJB-имени. Это все, что нужно для объявления JNDI-имен и привязки их к группам ресурсов JMS. Далее вы узнаете, как находить эти объекты в коде.
Утилита Customer Service и JMS
Усовершенствованная утилита Customer Service уже почти готова. Далее вам нужно вызывать JMS-ресурсы с помощью JNDI, чтобы иметь возможность использовать JMS. Полные JNDI-строки следуют традиции именования, которую можно видеть в листинге 6.
Листинг 6. Перечень JNDI-имен в customer.properties
# Здесь задаются имена JNDI
jndi.customer.ejb=java:/comp/env/ejb/CustomerEntityBean
jndi.process.ejb=java:/comp/env/ejb/ProcessCustomerSessionBean
jndi.group.ejb=java:/comp/env/ejb/InterestGroupEntityBean
jndi.jms.connector=java:comp/env/jms/CustomerServiceConnectionFactory
jndi.jms.topic=java:comp/env/jms/CustomerServiceTopic
# Здесь задаются специальные группы интересов
all.groups=java,sports,travel
# Здесь задаются ключевые слова для каждой специальной группы интересов
# Внимание: названия групп должны оканчиваться на .group
java.group=java,graphics,geronimo,j2ee,wireless,eclipse,tech,research,test,auto
sports.group=sport,biking,hiking,ball,tennis,golf,martial arts,tae kwon do,yoga
travel.group=travel,new zealand,europe,japan,hawaii,vacation,cruise,photo
|
Имена, ранее объявленные в теге <ref-name> дескрипторов сессионного компонента, теперь перечислены с полным контекстом JNDI-имени. Ко всем именам сделаны приставки согласно рекомендованной Java-традиции именования. Также, имейте в виду, что все специальные группы интересов вместе с относящимися к ним субъектами перечислены в этом файле. Код в листинге 7 показывает, как реализовать JNDI-поиск InterestGroupEntityBean из MDB, используя полный контекст JNDI-имени.
Листинг 7. Частичный листинг ServiceMonitorMessageBean.java
package com.service.customer.ejb;
public class ServiceMonitorMessageBean implements MessageDrivenBean, MessageListener
{
private transient MessageDrivenContext mdc = null;
private InterestGroupHome groupHome = null;
private ResourceBundle bundle = null;
private String JNDI_GROUP_EJB = null;
public void ejbCreate()
{
InitialContext initial = null;
Object objref = null;
bundle = ResourceBundle.getBundle("customer", Locale.getDefault(),
ServiceMonitorMessageBean.class.getClassLoader());
JNDI_GROUP_EJB = bundle.getString("jndi.group.ejb");
try
{
initial = new InitialContext();
objref = initial.lookup(JNDI_GROUP_EJB);
groupHome = (InterestGroupHome)PortableRemoteObject.narrow(objref,
InterestGroupHome.class);
} // end try
catch (Exception e)
{
e.printStackTrace();
} // end catch
} // end ejbCreate
public void onMessage(Message msg)
{
boolean entryFound = false;
GroupKey groupKey = null;
InterestGroup group = null;
ObjectMessage objMsg = null;
Vector tmpVector = null;
// Extract message from Topic
try
{
if (msg instanceof ObjectMessage)
{
objMsg = (ObjectMessage)msg;
groupKey = (GroupKey)objMsg.getObject();
System.out.println("JMS - id: " + groupKey.clientID + "\t" +
"group: " + groupKey.groupName + "\t" +
"email: " + groupKey.emailAddress);
} // end if
} // end try
catch (Exception e)
{
e.printStackTrace();
} // end catch
// Create "group" if no entry found
if (!entryFound)
{
try
{
group = groupHome.create(groupKey);
} // end try
catch (Exception e)
{
e.printStackTrace();
} // end catch
} // end if
} // end onMessage
} // end ServiceMonitorMessageBean
|
Нас интересуют два метода: ejbCreate и onMessage. Метод ejbCreate производит JNDI-поиск InterestGroupEntityBean. Этот код поиска похож на код в сессионных или entity-компонентах. Метод onMessage запускается, если найдено сообщение в CustomerServiceTopic. Этот метод - единственный способ доступа элементов к MDB, так как эти компоненты не вызываются напрямую. Листинг 8 показывает, как находить JMS-ресурсы из сессионного компонента ProcessCustomerSessionBean.
Листинг 8. doJNDILookups метод ProcessCustomerSessionBean.java
private void doJNDILookups()
throws NamingException, ClassCastException
{
Object objref = null;
InitialContext initial = null;
bundle = ResourceBundle.getBundle("customer", Locale.getDefault(),
ProcessCustomerSessionBean.class.getClassLoader());
JNDI_CUSTOMER_EJB = bundle.getString("jndi.customer.ejb");
JNDI_JMS_CONNECTOR = bundle.getString("jndi.jms.connector");
JNDI_JMS_TOPIC = bundle.getString("jndi.jms.topic");
initial = new InitialContext();
objref = initial.lookup(JNDI_CUSTOMER_EJB);
customerHome = (CustomerHome)PortableRemoteObject.narrow(objref,
CustomerHome.class);
System.out.println("looking up: " + JNDI_CUSTOMER_EJB);
objref = initial.lookup(JNDI_JMS_TOPIC);
receivingTopic = (Topic)PortableRemoteObject.narrow(objref, Topic.class);
System.out.println("looking up: " + JNDI_JMS_TOPIC);
objref = initial.lookup(JNDI_JMS_CONNECTOR);
factory = (ConnectionFactory)PortableRemoteObject.narrow(objref,
ConnectionFactory.class);
System.out.println("looking up: " + JNDI_JMS_CONNECTOR);
} // end doJNDILookups
|
Мастер соединений и receivingTopic получаются из JNDI и соответствуют ресурсам CustomerServiceConnectionFactory и CustomerServiceTopic, соответственно. Оба эти ресурса были созданы с использованием консоли Geronimo. Листинг 9 показывает, как использовать JMS-ресурсы, чтобы посылать сообщение на адрес назначения.
Листинг 9. sendMessage метод ProcessCustomerSessionBean.java
private void sendMessage(String groupName, String emailAddress)
{
boolean transacted = false;
Connection connection = null;
GroupKey groupKey = null;
MessageProducer producer = null;
ObjectMessage objMsg = null;
Session session = null;
groupKey = new GroupKey();
groupKey.clientID = generateSimpleID();
groupKey.groupName = groupName;
groupKey.emailAddress = emailAddress;
try
{
connection = factory.createConnection();
session = connection.createSession(transacted, Session.AUTO_ACKNOWLEDGE);
connection.start();
producer = session.createProducer(receivingTopic);
objMsg = session.createObjectMessage();
objMsg.setObject(groupKey);
producer.send(objMsg);
connection.close();
} // end try
catch (Exception e)
{
e.printStackTrace();
} // catch
} // end sendMessage
|
Метод sendMessage создает JMS-соединение к JMS-провайдеру ActiveMQ через мастера подключений. Далее он задает receivingTopic как адрес назначения для сообщений на время, требуемое, чтобы послать сообщение. После того, как сообщение послано, сессия закрывается.
Теперь утилита Customer Service готова для развертывания в Geronimo:
- Измените файл CustomerService/resources/build.properties и убедитесь, что позиции каталогов правильно заданы.
- Далее откройте консоль, измените каталоги на место расположения CustomerService/build.xml и напечатайте
ant. Это создаст и развернет приложение, которое может быть доступно на http://localhost:8080/service.
Рисунок 2 показывает, как должна выглядеть страница ввода данных.
Рисунок 2. Страница ввода данных для утилиты Customer Service
В базе данных хранится только самая важная информация о клиентах. Интересы клиента анализируются из сессионного компонента и сравниваются с известным списком субъектов, чтобы определить название группы. Рисунок 3 показывает результирующую таблицу, после того как все клиенты проанализированы или введены.
Рисунок 3. Представление таблицы групп интересов с точки зрения базы данных
После сравнения интересов каждого клиента со списком субъектов из файла customer.properties, становится очевидным, что существует некоторое наложение клиентских интересов. Это предполагалось, и дублирующие записи игнорируются после того, как специальная группа интересов была приписана клиенту. Вот все, что нужно для доступа к JMS-ресурсам в JNDI!
Заключение
Эта статья показала, как создавать группы ресурсов JMS, используя консоль Geronimo. Утилита Customer Service показала, как обращаться к этому ресурсу из JNDI, используя сессионный компонент, чтобы посылать сообщения в тему JMS, и MDB, чтобы получать эти сообщения асинхронно. В Части 3 мы обсудим JNDI-соединения с ресурсом почтовой сессии и интеграцию с примером приложения.
Загрузка | Описание | Имя | Размер | Метод загрузки |
|---|
| Исходный код программы, Часть 2 | CustomerService-part2.zip | 226KB | HTTP |
|---|
Ресурсы Научиться
- Оригинал статьи Apache Geronimo JNDI naming and Java resource connection pools, Part 2: Using Java Message Services.
- Прочитайте Apache Geronimo v1.1 - руководство пользователя, собранную Apache всю документацию для Apache Geronimo v1.1.
- Прочитайте
Apache Geronimo: Разработка и Развертывание
Аарона Малдера (Aaron Mulder), чтобы узнать, как устанавливать и настраивать сервер приложений Apache Geronimo.
- Прочитайте Учебное пособие по J2EE 1.4 от Sun для руководства по разработке корпоративных приложений для J2EE версии 1.4 и для первого знакомства с этой платформой.
- Прочитайте статью "Развертывание J2EE-приложений на Apache Geronimo".
- Посмотрите "Использование регулярных выражений".
- Присоединитесь к Списку рассылки Apache Geronimo.
- Просмотрите статьи, учебные пособия и другие ресурсы в разделе проекта Apache Geronimo developerWorks, что поможет вам начать разработку с Geronimo сегодня.
- Найдите полезные ресурсы для начинающих и опытных пользователей в разделе developerWorks Начните сейчас вместе с Apache Geronimo.
- Взгляните на предложение Поддержка IBM® для Apache Geronimo, которое позволяет вам разрабатывать Geronimo приложения, имеющие поддержку IBM мирового класса.
- Посетите раздел Open source developerWorks Россия для получения информации по методам разработки, инструментам и обновленным проектам, которые помогут вам в разработке при помощи технологий открытых исходных кодов и их использовании вместе с продуктами IBM.
- Найдите книги на эти и другие технические темы на Safari bookstore.
-
Семинары и обучение.
Получить продукты и технологии
Обсудить
Об авторе  | |  |
Дэйл де лос Рейес (Dale de los Reyes) получил степень по компьютерным технологиям в California Polytechnic State University, San Luis Obispo в 1996. Имеет опыт разработки приложений на платформах J2EE/Java EE, C++ for Microsoft® Windows® и COBOL for Mainframes. В свободное время занимается фотографией, боевыми искусствами и независимыми проектами. |
Выскажите мнение об этой странице
|  |