В.В. Кулямин - Технологии программирования. Компонентный подход (1133554), страница 69
Текст из файла (страница 69)
75.Сеансовые компоненты могут поддерживать состояние сеанса, обеспечивая пользователювозможность получения результатов очередного запроса с учетом предшествовавшихзапросов в рамках данного сеанса, или не поддерживать. Во втором случае компонентреализует обработку запросов в виде чистых функций.Жизненный цикл сеансового компонента различается в зависимости от того, поддерживаетли компонент состояние сеанса или нет.247НесуществуетClass.newInstance()setEntityContext()Object.finalize ()unsetEntityContext()В пулеejbCreate()ejbPostCreate()ejbPassivate()ejbRemove()ejbActivate()АктивенejbLoad()обычные методыejbStore()Рисунок 75.
Жизненный цикл EJB компонента данных.Схема жизненного цикла сеансового компонента с состоянием показана на Рис. 76.Отличие от жизненного цикла компонента данных единственное — метод ejbCreate()сразу переводит компонент в активное состояние.НесуществуетejbRemove()Class.newInstance()setSessionContext()ejbCreate()тайм-аутили исключениеejbPassivate()АктивенПассивенejbActivate()обычные методыРисунок 76. Жизненный цикл сеансового компонента с состоянием.•Жизненный цикл сеансового компонента без состояния гораздо проще. Его схемапредставлена на Рис.
77.Класс первичного ключа (primary key class).Декларируется только для компонентов данных, если в этом качестве нельзя использоватьподходящий библиотечный класс.Определяет набор данных, которые образуют первичный ключ записи базы данных,соответствующей одному экземпляру компонента.Чаще всего это библиотечный класс, например, String или Integer. Пользовательскийкласс необходим, если первичный ключ составной, т.е.
состоит из нескольких значений248простых типов данных. В таком классе должен быть определен конструктор без параметрови правильно перегружены методы equals() и hashCode(), чтобы EJB-контейнер могкорректно управлять коллекциями экземпляров компонентов с такими первичнымиключами. Такой класс также должен реализовывать интерфейс java.io.Serializable.НесуществуетClass.newInstance()setSessionContext()ejbCreate()ejbRemove()Активенобычные методыРисунок 77. Жизненный цикл сеансового компонента без состояния.Ниже приведены примеры декларации класса компонента и интерфейсов для компонентовданных, соответствующих простой схеме из двух таблиц, которая изображена на Рис.
78.В рамках этой схемы, состоящей из таблиц, где хранятся данные книг и организацийиздателей, каждая книга связана с одним и только одним издателем, а каждый издатель можетиметь ссылки на некоторое множество книг (возможно, пустое). Каждая таблица имеет поле ID,являющееся первичным ключом. Таблица Book имеет поле PublisherID, содержащее значениеключа записи об издателе данной книги.BookPublisherID : intTitle : StringISBN : StringPublisherID : intID : intTitle : StringРисунок 78.
Пример схемы БД.Примеры кода удаленных интерфейсов для компонентов, представляющих данные о книгах ииздателях в рамках EJB-приложения.package ru.msu.cmc.prtech.examples;import java.rmi.RemoteException;import java.util.Collection;import javax.ejb.EJBObject;public interface PublisherRemote extends EJBObject{public String getTitle ()throws RemoteException;public voidsetTitle (String title) throws RemoteException;public Collection getBooks ()throws RemoteException;public voidsetBooks (Collection books)throws RemoteException;public void addBook(String title, String isbn)throws RemoteException;public void removeBook (String title, String isbn)throws RemoteException;}249package ru.msu.cmc.prtech.examples;import java.rmi.RemoteException;import javax.ejb.EJBObject;public interface BookRemote extends EJBObject{public String getTitle ()throws RemoteException;public voidsetTitle (String title) throws RemoteException;public String getISBN ()throws RemoteException;public voidsetISBN (String isbn) throws RemoteException;public PublisherRemote getPublisher () throws RemoteException;public voidsetPublisher (PublisherRemote publisher)throws RemoteException;}Примеры кода удаленных исходных интерфейсов.package ru.msu.cmc.prtech.examples;import java.rmi.RemoteException;import javax.ejb.CreateException;import javax.ejb.EJBHome;import javax.ejb.FinderException;public interface PublisherHomeRemote extends EJBHome{public PublisherRemote create (Integer id)throws CreateException, RemoteException;public PublisherRemote findByPK (Integer id)throws FinderException, RemoteException;}package ru.msu.cmc.prtech.examples;import java.rmi.RemoteException;import javax.ejb.CreateException;import javax.ejb.EJBHome;import javax.ejb.FinderException;public interface BookHomeRemote extends EJBHome{public BookRemote create (Integer id)throws CreateException, RemoteException;public BookRemote createBook (String title, String isbn)throws CreateException, RemoteException;public BookRemote findByPK (Integer id)throws FinderException, RemoteException;}Примеры кода классов компонентов.
Показано, как реализовывать дополнительные, неподдерживаемые контейнером автоматически, методы работы со связями между данными обиздателях и книгах и дополнительные методы создания компонентов.package ru.msu.cmc.prtech.examples;import java.util.Collection;import java.util.Iterator;250importimportimportimportimportjava.rmi.RemoteException;javax.ejb.CreateException;javax.ejb.EntityBean;javax.naming.InitialContext;javax.naming.NamingException;public abstract class PublisherBean implements EntityBean{public Integer ejbCreate (Integer pk){setId(pk);return null;}public void ejbPostCreate (Integer pk) { }public abstract Integer getId ();public abstract voidsetId (Integer pk);public abstract String getTitle ();public abstract voidsetTitle (String title);public abstract Collection getBooks ();public abstract voidsetBooks (Collection books);public void addBook (String title, String isbn){try{InitialContext context = new InitialContext();BookHomeRemote bookHome =(BookHomeRemote)context.lookup("BookHomeRemote");BookRemote book = bookHome.createBook(title, isbn);Collection books = getBooks();books.add(book);}catch (NamingException e) { e.printStackTrace(); }catch (CreateException e) { e.printStackTrace(); }catch (RemoteException e) { e.printStackTrace(); }}public void removeBook (String title, String isbn){Collection books = getBooks();Iterator it = books.iterator();try{while(it.hasNext()){BookRemote book = (BookRemote)it.next();if(book.getTitle().equals(title)&& book.getISBN().equals(isbn)){it.remove();break;}}}catch (RemoteException e) { e.printStackTrace(); }}}251package ru.msu.cmc.prtech.examples;import javax.ejb.EntityBean;public abstract class BookBean implements EntityBean{public Integer ejbCreate (Integer pk){setId(pk);return null;}public void ejbPostCreate (Integer pk) { }public Integer ejbCreateBook (String title, String isbn){setTitle(title);setISBN(isbn);return null;}public void ejbPostCreateBook (String title, String isbn) { }public abstract Integer getId ();public abstract voidsetId (Integer pk);public abstract String getTitle ();public abstract voidsetTitle (String title);public abstract String getISBN ();public abstract voidsetISBN (String isbn);public abstract PublisherRemote getOrganization ();public abstract voidsetOrganization (PublisherRemote pr);}Компоненты, управляемые сообщениямиКомпоненты, управляемые сообщениями, не доступны для удаленных вызовов, и поэтому неимеют удаленных и исходных интерфейсов.
Для создания такого компонента нужно определитьтолько его класс. Обращения к компоненту организуются в виде посылки сообщений к объектуэтого класса как к реализующему интерфейс javax.jms.MessageListener. Вместе с этиминтерфейсом класс компонента EJB, управляемого сообщениями, обязан реализовыватьинтерфейс javax.ejb.MessageDrivenBean.Первый интерфейс требует определения метода void onMessage(javax.jms.Message),который разбирает содержимое пришедшего сообщения и определяет способ его обработки.Кроме того, нужно определить методы void ejbCreate() для создания компонента и voidejbRemove() для освобождения ресурсов при его удалении.Жизненный цикл компонента, управляемого сообщениями выглядит в целом так же, как ижизненный цикл сеансового компонента без состояния (Рис.
77). Вся необходимая информацияпередается такому компоненту в виде данных обрабатываемого им сообщения.Пример реализации класса компонента, управляемого сообщениями, приведен ниже. Данныйкомпонент получает идентификатор издателя, название и ISBN книги и добавляет такую книгу ккнигам, изданным данным издателем.package ru.msu.cmc.prtech.examples;importimportimportimportimportjavax.ejb.EJBException;javax.ejb.MessageDrivenBean;javax.ejb.MessageDrivenContext;javax.jms.MapMessage;javax.jms.Message;252importimportimportimportjavax.jms.MessageListener;javax.naming.Context;javax.naming.InitialContext;javax.naming.NamingException;public class TransferProcessorBeanimplements MessageDrivenBean, MessageListener{Context context;public void setMessageDrivenContext (MessageDrivenContext mdc)throws EJBException{try { context = new InitialContext(); }catch (NamingException e) { throw new EJBException(e); }}public void ejbCreate() { }public void onMessage (Message msg){MapMessage message = (MapMessage)msg;try{Integer publisherPK = (Integer)message.getObject("Publisher");String title= (String)message.getObject("Title");String isbn= (String)message.getObject("ISBN");PublisherHomeRemote publisherHome =(PublisherHomeRemote)context.lookup("PublisherHomeRemote ");PublisherRemote publisher = publisherHome.findByPK(publisherPK);publisher.addBook(title, isbn);}catch (Exception e) { throw new EJBException(e); }}}public void ejbRemove () throws EJBException{try { context.close(); }catch (NamingException e) { }}Дескрипторы развертывания компонентов EJBПомимо декларации интерфейсов и классов компонентов, для построения EJB компонентанеобходимо написать дескриптор развертывания — XML файл в специальном формате,определяющий набор компонентов приложения и их основные свойства.Чаще всего дескрипторы развертывания не пишут вручную, их готовят с помощьюспециализированных инструментов для развертывания J2EE приложений или сред разработки(например, такими возможностями обладает среда NetBeans 4.0 [3]).
Здесь мы опишем толькочасть содержимого дескрипторов развертывания. Полное описание используемых в них тегов и ихназначения см. в [2].Дескриптор развертывания упаковывается вместе с байт-кодом классов компонентовприложения в JAR-архив. При развертывании такой архив помещают в выделенную директорию,в которой сервером J2EE ищет развертываемые приложения. После этого сервер сам осуществляетзапуск приложения и поиск кода компонентов, необходимых для обработки поступающихзапросов, на основании информации, предоставленной дескриптором.Заголовок дескриптора развертывания для набора EJB компонентов версии 2.1 выглядитследующим образом.253<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans2.0//EN" http://java.sun.com/j2ee/dtds/ejb-jar_2_1.dtd">Дескриптор развертывания содержит следующие теги.•<ejb-jar>Обязательный элемент.Это корневой тег дескриптора развертывания набора EJB компонентов, содержащий всеостальные теги.•<enterprise-beans>Обязательный элемент, должен появиться внутри <ejb-jar> ровно один раз.Содержит набор описаний отдельных EJB компонентов в виде элементов <entity>,<session>, <message-driven>.•<entity> и <session>Эти теги вложены в тег <enterprise-beans> и служат для описания, соответственно,компонентов данных и сеансовых компонентов.