Lecture06

advertisement
Технологии программирования. Компонентный подход
В. В. Кулямин
Лекция 6. Архитектура распределенных приложений J2EE.
Общие принципы построения распределенных систем
Построение распределенных систем высокого качества является одной из наиболее
сложных задач по разработке ПО. Технология J2EE была создана как раз для того, чтобы
сделать разработку наиболее часто встречающегося вида распределенных систем — так
называемых бизнес приложений, поддерживающих решение наиболее часто
встречающихся бизнес-задач — достаточно простой и доступной любому программисту.
Чтобы лучше понять принципы J2EE, стоит разобраться в основных проблемах,
возникающих при построении распределенных систем и в том, как эти проблемы
решаются с использованием данной технологии.
Основная задача, которую пытаются решить с помощью распределенных систем —
обеспечение максимально простого доступа как можно большему числу пользователей ко
всем ресурсам, которые могут им понадобиться. При этом важными свойствами такой
системы являются следующие.
 Прозрачность.
Прозрачность проявляется как способность системы скрыть от пользователя физическое
распределение ресурсов, а также аспекты их перераспределения и перемещения между
различными машинами, репликацию ресурсов, трудности, возникающие при
одновременной работе нескольких пользователей с одним ресурсом, ошибки при
доступе к ресурсам и в работе самих ресурсов.
Степень прозрачности может быть различной, поскольку скрывать от пользователя все
эффекты, возникающие при работе распределенной системы, неразумно. Кроме того,
прозрачность системы и ее производительность обычно находятся в обратной
зависимости — например, при попытке скрыть отказы в соединении с сервером
большинтво Интернет-клиентов пытается установить это соединение несколько раз, а
для пользователя это выглядит как значительно замеделенная реакция системы.
 Открытость.
Открытость системы определяется как полнота и четкость описания интерфейсов
работы с ней и служб, которые она предоставляет через эти интерфейсы. Такое
описание должно включать в себя все, что необходимо знать для того, чтобы
пользоваться этими службами, независимо от реализации данной системы и базовой
платформы.
Открытость системы важна как для обеспечения ее переносимости, так и для
облегчения использования системы и возможности построения других систем на ее
основе.
 Масштабируемость.
Масштабируемость системы — это зависимость изменения ее характеристик от числа ее
пользователей, числа подключенных ресурсов, а также увеличения географической
распределенности системы. В число важных характеристик при этом попадают
производительность, стоимость, трудозатраты на разработку, на внесение изменений, на
сопровождение, на администрирование, удобство работы с системой. Для некоторых из
них почти наилучшая возможная масштабируемость обеспечивается линейной
зависимостью, для других хорошая масштабируемость означает, что показатель не
меняется вообще при изменении масштабов системы или изменяется незначительно.
Для масштабируемой производительности обычно требуется, чтобы параметры задач,
решаемых системой за одно и тоже время, можно было увеличивать достаточно быстро
(лучше — линейно или еще быстрее, но это возможно не для всех задач) при
возрастании количества имеющихся ресурсов, в частности, отдельных машин. В то же
время, очень плохо, если внесение изменений в систему становится все более
трудоемким при ее росте — желательно, чтобы трудоемкость внесения одного
изменения почти не возрастала.
Иногда в масштабируемость включают административную мастабируемость системы —
зависимость удобства работы с ней от числа административно независимых
организаций, связанных с этой системой.
При реализации очень больших (действующих в масштабах тысяч и более
пользователей) систем хорошая мастабируемость может быть достигнута с помощью
следующих методов.
o Децентрализация служб, состоящая в использовании нескольких машин для
обработки поступающих запросов.
o Децентрализация данных, состоящая в использовании нескольких хранилищ
данных или нескольких копий одного хранилища.
o Децентрализация алгоритмов, состоящая в использовании для обработки и
перенаправления запросов алгоритмов, не требующих полной информации о
состоянии системы, не разрушающихся при сбое одного из ресурсов системы и
не предполагающих единого хода времени на всех машинах, входящих в
систему.
o Использование, где это возможно, асинхронной связи в виде передачи
сообщений без приостановки работы до прихода ответа.
o Использование комбинированных систем организации взаимодействия,
основанных на иерархической организации систем (хорошо масштабирующей
задачи поиска), репликации (построении копий данных и их распределении по
системе для балансировки нагрузки на разные ее элементы) и его частном
случае, кэшировании (организующем хранение результатов наиболее часто
используемых запросов как можно ближе к клиенту), на взаимодействии точкаточка (обеспечивающем независимость от других машин в системе).
 Безопасность.
Так как распределенные системы вовлекают в свою работу множество пользователей,
машин и географически разделенных элементов, вопросы их безопасности получают
гораздо большее значение, чем при работе обычных систем, сосредоточенных на одной
физической машине.
Понятие безопасности включает следующие характеристики.
o Сохранность и целостность данных.
При обеспечении групповой работы многих пользователей с одними и теми же
данными нужно обеспечивать их сохранность, т.е. предотвращать исчезновение
данных, введенных одним из пользователей, и в тоже время целостность, т.е.
непротиворечивость, выполнение всех присущих данным ограничений.
Это непростая задача, не имеющая полного решения, удовлетворяющего все
стороны во всех ситуациях — при одновременном изменении одного и того же
элемента данных разными пользователями итоговый результат должен быть
непротиворечив, и поэтому часто может совпадать только с вводом одного из
них. Как будет обработана такая ситуация и возможно ли ее возникновение
вообще, зависит от дополнительных требований к системе, от принятых
протоколов работы, от того, какие риски — потерять данные одного из
пользователей или значительно усложнить методы работы пользователей с
системой — будут сочтены более важными.
o Защищенность данных и коммуникаций.
При работе с коммерческими системами, с системами, содержащими большие
объемы персональной и бизнес информации, с системами обслуживания
пользователей государственных служб достаточно важна защищенность
информации, как постоянно хранящейся в системе, так и информация одного
сеанса работы. Для распределенных систем обеспечить защищенность гораздо
сложнее, поскольку нельзя физически изолировать все элементы системы и
разрешить доступ к ней только особо проверенным людям.
o Отказоустойчивость и способность к восстановлению после ошибок.
Одним из достоинств распределенных систем является возможность
построения более надежно работающей системы из не вполне надежных
компонентов. Однако для того, чтобы это достоинство стало реальным,
необходимо тщательное проектирование систем для избежания зависимости
работоспособности системы от ее отдельных компонентов — иначе
достоинство превращается в недостаток, поскольку в распределенной системе
элементов больше и выше вероятность того, что хотя бы один элемент выйдет
из строя и хотя бы один ресурс окажется недоступным.
Еще важнее для распределенных систем уметь восстанавливаться после сбоев.
Уровни этого восстановления могут быть различными — так, обычно, данные
одного сеанса работы считается возможным не восстанавливать, поскольку
такие данные часто мало значимы или легко восстанавливаются (если это не так
— стоит серьезно рассмотреть необходимость восстановления сеансов), но так
называемые постоянно хранимые (persistent) данные обычно требуется
восстанавливать до последнего непротиворечивого их состояния.
Ниже перечислены основные аспекты, которые рассматривают при разработке и
анализе распределенных систем.
 Организация связи и передачи данных между отдельными компонентами системы.
В связи с этими вопросами определяются протоколы организации связи, способы
реализации обращения к процедурам и методам объектов некоторого потока из других,
организация синхронной и асинхронной передачи сообщений, организация сохранности
(асинхронных) сообщений в то время, когда и отправитель и получатель сообщения
могут быть неактивны, организация передачи непрерывных потоков данных (аудио- и
видеоданные, смешанные потоки).
 Организация работ в рамках процессов и потоков.
В рамках данного аспекта рассматривается разделение работ в вистеме на отдельные
процессы, определение ролей процессов (пример — клиентские и серверные), вопросы
организации исполняемых агентов, способных мигрировать между отдельными
машинами.
 Именование и поддержка поиска отдельных ресурсов внутри системы.
К этому аспекту относятся вопросы, связанные с присвоением имен и идентификаторов
различных ресурсов, с поиском ресурсов в системе по именам и атрибутам, с
размещением и поиском мобильных ресурсов, изменяющих в ходе работы свое
физическое положение.
Сюда же относят и организацию и поддержку сложных ссылочных структур вообще —
например, вопросы удаления ставших никому не доступными ресурсов.
 Синхронизация параллельно выполняемых потоков работ.
В рамках этого аспекта рассматриваются вопросы, связанные с организацией
взаимодействия между параллельно работающими для получения общего результата
потоками и процессами, алгоритмы синхронизации времени и организации работ в том
случае, если в системе нельзя непротиворечиво определить глобальное время, вопросы
организации транзакций.
 Поддержка целостности данных и непротиворечивости вносимых изменений.
Этот аспект касается способов организации целостности данных и поддерживаемых при
этом моделей непротиворечивости (определяющих, на основе каких требований
формируются результаты выполняемых одновременно изменений и что доступно
клиентам, выполнявшим эти изменения). В связи с этим определяются протоколы
обеспечения непротиворечивости, создания и согласования реплик и кэшей,
обеспечивающие выполнение этих моделей.
 Организация отказоустойчивой работы.
К этому аспекту относятся вопросы обеспечения отказоустойчивости процессов,
обеспечения надежной связи, протоколы подтверждения, используемые для реализации
надежной двусторонней связи или надежных групповых рассылок, протоколы записей
промежуточных состояний и восстановления после сбоя.
 Организация защищенности данных и коммуникаций.
Сюда относятся вопросы обеспечения защищенности системы в целом (при этом
большее значение, чем технические аспекты, имеют проблемы определения процедур
проведения работ, обеспечивающих нужный уровень защищенности, и проблемы
соблюдения людьми этих процедур), организации защиты данных от
несанкционированного доступа, обеспечения защиты каналов связи с двух сторон —
препятствование в несанкционированном доступе к передаваемой информации и
препятствование в подмене информации в канале, вопросы аутентификации
пользователей, протоколы подтверждения идентичности и авторства.
Архитектура распределенных приложений на платформе J2EE
В рамках технологии J2EE большая часть вопросов подобного рода решается за счет
предоставления стандартных интерфейсов к механизмам, реализованных как часть
функций серверов J2EE, web-серверов и серверов хранения данных, а также, за счет
использования библиотек, входящих в состав J2EE,
J2EE содержит набор связанных стандартов, поддерживающих их инструментов и
библиотек, которые предназначены для разработки, развертывания и эксплуатации
приложений, построенных в стиле многоуровневой клиент-серверной архитектуры.
Большинство приложений J2EE разрабатываются в стиле трехуровневой архитектуры
данные-представление-обработчик, в которой
 Аспект данных представляется сервером данных (СУБД) и набором компонентов
EJB, чья связь с сервером данных обеспечивается EJB-контейнером. Компоненты
EJB реализуют значимые для предметной области данного приложения операции
над данными — то, что называется бизнес-логикой. При этом EJB-контейнер
отвечает за поддержку взаимодействия распределенных объектов, поддержку
транзакций, поддержку связи компонентов с СУБД, обеспечение защищенности и
безопасности, поддержку параллелизма и управление ресурсами.
 Аспект представления реализуется Web-браузером на машине клиента, с помощью
которого можно просматривать результаты обращений к системе в виде HTML
страниц. Сами эти страницы чаще всего динамически генерируются по запросам
клиента с помощью набора серверных страниц Java (Java Server Pages, JSP). Иногда в
HTML страницы включают Java-апплеты, работающие на машине клиента и
предоставляющие интерфейс, который тяжело или невозможно сделать с помощью
HTML. Связь между этими апплетами и серверной частью системы тоже
основывается на протоколе HTTP.
 Аспект обработки реализуется при помощи набора сервлетов (servlets) или
вспомогательных компонентов, из которых строятся сервлеты. Они предстают
клиенту в виде элементов управления на HTML страницах.
И сервлеты, и JSP работают в рамках Web-контейнера, выполняющего для них те же
функции, что EJB-контейнер для компонентов EJB и связывающего их с Webсервером, который непосредственно получает и отсылает HTTP-сообщения.
Общая архитектура J2EE приложений выглядит следующим образом.
Web-контейнер
сервлеты
Клиент —
Интернет браузер
HTTP
Web-сервер
JSP
HTML
страницы
Javaапплеты
EJB-контейнер
EJB
компоненты
Хранилище
данных
(СУБД)
Схема БД
Рисунок 1. Типовая архитектура J2EE приложения.
Цветом выделены компоненты, которые надо разрабатывать, в отличии от
предоставляемой платформой J2EE и поставщиками ПО поддержки инфраструктуры.
Web-сервер, Web-контейнер, EJB-контейнер и хранилище данных могут быть размещены
как на одной, так и на нескольких машинах, что позволяет строить более масштабируемые
по производительности и, иногда, более надежные приложения.
В результате J2EE предоставляет возможность создавать приложения, пользуясь уже
имеющимися реализациями механизмов решения почти всех вопросов, которые
возникают при создании распределенных систем.
Связь
Основную часть функций по обеспечению связи между отдельными компонентами
J2EE приложения эта платформа берет на себя. Обращение к EJB компонентам из
компонентов внутри Web-контейнера оформляется прозрачно, как обращение к
локальным объектам.
Передача данных между отдельными компонентами J2EE приложения организуется на
основе одной из реализаций протокола дистанционного или удаленного вызова методов
(Remote Method Invocation, RMI).
Этот протокол реализуется в виде набора классов, наследующих интерфейсы пакета
java.rmi.
В целом работа этого протокола организована следующим образом.
Компоненты в приниципе равноправны, но в рамках одного обращения компонент,
осуществляющий его, называется клиентом, а обрабатывающий — сервером. В рамках
клиентского процесса, в котором работает клиент, находится заглушка (stub) серверного
компонента, реализующего тот самый интерфейс, который серверный компонент
предоставляет для дистанционных вызовов. В рамках того же процесса, что и серверный
компонент, работает каркас (skeleton) серверного объекта.
Процесс клиента
Процесс сервера
Клиент
Сервер
1. Вызов
6. Результат
4. Результат
3. Вызов
Заглушка
Каркас
2. Передача данных вызова
5. Передача результата
Рисунок 2. Схема работы по протоколу RMI.
Вызов метода клиентским компонентом обрабатывается серверной заглушкой.
Делается это так. Значения аргументов вызова сериализуются с тем, чтобы быть
пересланы по сети (другое название процесса перевода значений аргументов в
передаваемую по сети форму — маршаллинг). При этом аргумент-ссылка на удаленный
объект представляется в виде однозначно разрешимого в рамках системы идентификатора
этого объекта. После этого устанавливается соединение с каркасом серверного объекта
(или используется уже установленное ранее соединение), и имя вызываемого метода и
значения аргументов его вызова передаются по сети.
Каркас представляет собой объект, ожидающей прихода сообщений на некоторый
порт. При поступлении такого сообщения, он извлекает из него имя вызываемого метода и
аргументы вызова, и выполняет соответствующий вызов серверного компонента.
Серверный компонент выполняет вызванный метод и возвращает результат каркасу.
Каркас сериализует результат вызова и передает его в виде ответного сообщения
заглушке.
Заглушка извлекает результат из сообщения и возвращает его вызвавшему метод
клиентскому компоненту.
В качестве типов параметров и результатов методов, вызываемых удаленно, должны
фигурировать только примитивные типы (boolean, char, byte, short, int, long,
float, double), сериализуемые типы, реализующие интерфейс java.io.Serializable
или типы самих объектов, способных быть вызванными удаленно, — все они реализуют
интерфейс java.rmi.Remote.
Процессы и потоки.
Разбиение приложения на набор взаимодействующих процессов и потоков
осуществляется автоматически Web- и EJB-контейнерами. Компоненты J2EE приложения,
работающие в их рамках не должны реализовывать собственные отдельные потоки.
Именование.
Вопросы поддержки именования (т.е. поиска ресурсов по именам и идентификаторам)
и службы каталогов (т.е. поддержки поиска ресурсов по набору значений их атрибутов) в
рамках J2EE и J2SE решаются при помощи интерфейса JNDI (Java Naming and Directory
Interface, Java интерфейс служб имен и каталогов).
Базовые интерфейсы и классы JNDI находятся в пакете javax.namimg
javax.naming.directory, javax.naming.event, javax.naming.ldap.
и его подпакетах
Основные сущности служб именования или каталогов, харанящие привязку ресурсов к
именам и наборам атрибутов, называются контекстами.
Базовый интерфейс всех контекстов определяется интерфейсом
javax.naming.Context. Основные классы, его реализующие —
javax.naming.InitialContext, javax.naming.directory.InitialDirContext,
javax.naming.ldap.InitialLdapContext. Основные методы этого интерфейса
перечислены ниже.
 void bind(String, Object) — связать данное имя с данным объектом
 Object lookup (String) — найти объект с данным именем
 void rebind(String, Object) — связать данное имя с данным объектом, даже если
оно уже имеется в этом контексте
 void rename(String, String) — связать с объектом, связанным с первым именем,
второе
 void unbind(String) — удалить связку объека с данным именем
 NamingEnumeration<Binding> listBinding(String) — возвращает список связок имяобъект для подконтекста с указанным именем
 Context createSubcontext(String) — создать подконтекст с данным именем
 void destroySubcontext(String) — удалить подконтекст с данным именем
В дополнение к этим методам классы InitialDirContext и InitialLdapContext
реализуют интерфейс контекста службы каталогов DirContext, имеющий методы void
bind(String, Object, Attributes) для привязки набора атрибутов к объекту,
Attributes getAttributes(String) для получения набора атрибутов объекта по
указанному имени и NamingEnumeration<SearchResult> search(String, Attributes)
для поиска объектов по указанному набору атрибутов в контексте с указанным именем.
При загрузке виртуальной машины механизм инициализации JNDI конструирует
начальный контекст по JNDI свойствам, задаваемым во всех файлах с именем
jndi.properties, находящихся в директориях, перечисленных в classpath.
Стандартный набор JNDI свойств, которые могут быть установлены для Java
приложения или апплета, включает
o java.naming.factory.initial (соответствует константе
Context.INITIAL_CONTEXT_FACTORY) — имя класса фабрики для создания
начальных контекстов, обязательно должно быть установлено
o java.naming.provider.url (соответствует константе Context.PROVIDER_URL)
— URL сервера каталогов или имен
o java.naming.dns.url (соответствует константе Context.DNS_URL) — URL для
определения DNS узла, используемого для получения адреса JNDI URL
o java.naming.applet (соответствует константе Context.APPLET) — объектапплет, используемый для получения JNDI свойств
o java.naming.language (соответствует константе Context.LANGUAGE) — список,
через запятую, предпочтительных языков для использования в данной службе
(пример: en-US, fr, ja-JP-kanji). Языки описываются в соответствии с RFC
1766.
Пример использования JNDI для распечатки содержимого директории c:/tmp. Для
работы с файловой системой через JNDI используется реализация службы именования на
основе файловой системы от Sun (http://java.sun.com/products/jndi/serviceproviders.html).
package ru.msu.cmc.prtech.examples;
import java.util.Properties;
import
import
import
import
import
javax.naming.Binding;
javax.naming.Context;
javax.naming.InitialContext;
javax.naming.NamingEnumeration;
javax.naming.NamingException;
/**
* @author Victor Kuliamin
*/
public class JNDIExample
{
public static void main (String[] args)
{
Properties env = new Properties();
env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.fscontext.RefFSContextFactory");
env.put(Context.PROVIDER_URL, "file://c:/tmp");
try
{
Context cntx = new InitialContext(env);
NamingEnumeration list = cntx.listBindings("");
while(list.hasMore())
{
Binding bnd = (Binding)list.next();
System.out.println(bnd.getName());
}
}
catch (NamingException e)
{
e.printStackTrace();
}
}
}
Синхронизация
Синхронизация работы потоков, как и само разбиение работ на потоки, выполняется
автоматически.
Целостность и непротиворечивость
Целостность и непротиворечивость данных при работе J2EE приложений
поддерживается с помощью механизма транзакций. Построение транзакций определяется
в декларативном стиле в дескрипторах развертывания EJB компонентов, определяющих
действия с данными приложения. Методы работы с EJB компонентами могут иметь
различные политики работы с транзакционными контекстами: не поддерживать работу в
рамках транзакции, включаться в имеющуюся транзакцию, стартовать новую, вложенную
транзакцию, и т.п (см. далее).
Отказоустойчивость
Отказоустойчивость J2EE приложений не обеспечивается дополнительными
средствами (такими, как репликация, надежная передача сообщений и пр.).
Защищенность
Защищенность J2EE приложения поддерживается механизмом определения ролей,
доступности различных методов для ролей и политик переноса или создания ролей при
совместной работе нескольких методов. Роли, политики их переноса и правила доступа
различных ролдей к методам описываются в дескрипторах развертывания EJB
компонентов (см. далее).
Разработка компонентов Enterprise Java Beans
После рассмотрения построения J2EE приложений в целом, перейдем к изучению
техник разработки их отдельных компонентов.
Компонент Enterprise Java Beans представляет собой компонент, который представляет
в J2EE приложении элемент данных или внутренней, невидимой для пользователя логики
приложения и для которого определен жизненный цикл в рамках рабочего процесса
приложения — набор состояний через которые проходит один экземпляр такого
компонента.
EJB-контейнер, управляющий жизненным циклом EJB компонентов, несет
ответственность за следующее.
 Он отвечает за управление набором имеющихся компонентов и жизненным
циклом отдельного компонента — инициализацию и уничтожение, построение и
поддержку пула компонентов для обеспечения большей производительности
 Он обеспечивает передачу вызовов извне к EJB компонентам и между ними, в
том числе и удаленных вызовов. Несколько EJB контейнеров, работающих на
разных машинах, обеспечивают взаимодействие наборов компонентов,
управляемых каждым из них.
 Он обеспечивает поддержку параллельной обработки запросов
 Он поддерживает связь между EJB компонентами и базой данных приложения и
синхронизует их данные
 Он поддерживает целостность данных приложения с помощью механизма
транзакций
 Он обеспечивает защищенность приложения с помощью механизма ролей, их
передачи между компонентами и проверки корректности обращения в данной
роли к методу компонента
Для разработки компонента EJB нужно сделать следующее.
 Один или несколько классов и интерфейсов Java, обеспечивающих как
реализацию самой функциональности компонента, так и реализацию заглушек и
каркасов для удаленных обращений к нему
 Дескриптор развертывания — xml файл в специальном формате, описывающий
o Набор EJB компонентов приложения
o Совокупность элементов кода Java, образующих один компонент
o Связь свойств компонента с полями таблиц БД и связями между
таблицами
o Набор ролей, привязку их к различным методам, а также набор правил
доступа различных ролей к методам компонентов
o Политику компонентов и их методов по отношению к транзакциям
Правила создания EJB компонента, т.е. набор классов и интерфейсов, которые должны
или могут быть декларированы как части его описания, зависят от вида самого
компонента. Различают три вида компонентов:
 Компоненты данных (сущностные, entity beans)
Они представляют данные приложения и основные методы работы с ними.
 Сеансовые компоненты (session beans)
Они представляют независимую от пользовательского интерфейса и каких-либо
конкретных данных логику работы приложения в рамках сеанса, называемую
иногда бизнес-логикой
 (введены в EJB 2.0)
Компоненты, управляемые сообщениями (message driven beans).
Это вспомогательный вид компонентов, основное назначение которых —
обработка асинхронных сообщений, в отличии от остальных, работающих в
синхронном режиме. Клиент, вызывающий метод в компоненте данных или
сеансовом компоненте, ждет, пока вызванный компонент не завершит свою
работу. Компоненту же, управляемому сообщениями, можно отослать сообщение
и продолжать работу сразу после окончания его передачи.
При построении компонентов разрабатываются следующие их составляющие.
 Для компонентов данных
Всегда должны быть декларированы класс компонента и по крайней мере один из
интерфейсов — удаленный или локальный.
o Удаленный или дистанционный интерфейс (remote interface)
Этот интерфейс определяет методы компонента, к которым можно
обращаться извне его собственного процесса.
Он должен наследовать интерфейс javax.ejb.EJBObject (наследующий
java.rmi.Remote). Обычно такой интерфейс определяет набор свойств
(свойством типа <type> с именем <name> считается пара методов <type>
get<Name>() и void set<Name>(<type>)), связанных с представляемыми
компонентом данными. Некоторые из свойств могут быть не связаны
непосредственно с каким-то хранимым в БД элементом данных, а
вычислимы на основе нескольких таких элементов.
o Удаленный внешний интерфейс (remote home interface)
Этот интерфейс определяет методы для построения и поиска компонентов с
определенными свойствами, в том числе и извне процесса, в котором они
будут находится.
Он должен наследовать интерфейс javax.ejb.EJBHome (наследующий
java.rmi.Remote). Чаще всего такой интерфейс определяет метод создания
компонента по значению первичного ключа, метод поиска компонента по
значению первичного ключа, и, может быть, дополнительные методы
поиска по разным комбинациям свойств.
o Класс компонента (bean class)
Должен быть декларирован.
Этот класс определяет свойства компонента (обычно соответсвующие его
данным) и реализацию методов удаленного и локального интерфейсов.
В EJB 2.0 он должен быть абстрактным классом, реализующим интерфейс
javax.ejb.EntityBean (в EJB 1.1 и ранее — неабстрактным классом).
Этот класс должен реализовывать методы, декларированные в удаленном и
локальном интерфейсах (но недолжен реализовывать сами эти интерфейсы!)
и некоторые другие, например, методы установки значений свойств,
являющихся частями первичного ключа. Свойства, соответствующие
хранимым элементам данных должны быть оформлены в виде абстракных
методов get<Name> и set<Name> — их взаимодействие с элементами данных
автоматически поддерживается EJB контейнером (в EJB 1.1 эти методы
неабстрактны и возвращают/изменяют значения соответсвующих полей,
также можно поступить и в EJB 2.0, если вы не хотите использовать
автоматическое управление синхронизацией с БД). Вычислимые свойства
оформляются в виде неабстрактных методов.
Кроме того, этот класс может реализовывать служебные методы поддержки
жизненного цикла (эти методы вызываются EJB контейнером на
определенных фазах жизненного цикла компонента и могут быть
использованы для каких-то специфических действий по мониторингу
состояния компонента или управления дополнительными ресурсами,
которыми контейнер не управляет автоматически).

public <Primary key class> ejbCreate(<Primary key class>)
Должен быть реализован. Устанавливает значения свойств,
являющихся частями первичного ключа. Если первичный ключ
составной, в качестве параметров этот метод должен использовать
набор значений свойств, образующих первичный ключ.
Должен возвращать null.
Вызывается при создании в БД записи, соответсвующей создавемому
экземпляру компонента.
Этот метод может быть вызван контейнером на уже созданном
экземпляре, находящемся в пуле, для установления его соответсвия
новой записи в базе данных.
Дополнительно, для каждого метода внешних интерфейсов с именем
create<Suffix>(…) и результатом — локальным или удаленным
интерфейсом — должен быть определен соответствующий метод
<Primary key class> ejbCreate<Suffix>(…) с теми же
параметрами.

public void ejbPostCreate(<Primary key class>)
Вызывается сразу после создания соответсвтующей экземпляру
компонента записи в БД.

public void setEntityContext(javax.ejb.EntityContext)
Вызывается сразу после инициализации экземпляра компонента,
перед передачей его в пул.
Может быть использован для чтения каких-то данных из контекста,
являющихся общими для всех компонентов данного процесса.

public void unsetEntityContext()
Вызвается после удаления экземпляра компонента из пула и перед
передачей его сборщику мусора для зачистки.

public void ejbActivate()
Вызывается при активизации экземпляра компонента —
вытаскивании его из пула построенных, но не готовых к работе
компонентов, перед использованием для обработки пришедшего
запроса.

public void ejbPassivate()
Вызывается при переводе экземпляра компонента из активного
состояния в пул.

public void ejbLoad()
Вызывается при загрузке данных экземпляра компонента из БД.

public void ejbStore()
Вызывается при сохранении данных экземпляра компонента в БД.

public void ejbRemove()
Вызывается перед удалением данных экземпляра компонента из БД.
В целом жизненный цикл компонента данных выглядит следующим
образом.
Не
существует
Class.newInstance()
setEntityContext()
Object.finalize ()
unsetEntityContext()
В пуле
ejbPassivate()
ejbCreate()
ejbPostCreate()
ejbRemove()
ejbActivate()
Активен
ejbLoad()
обычные методы
ejbStore()
Рисунок 3. Жизненный цикл компонента данных.
o Класс первичного ключа (primary key class)
Должен быть декларирован, но в качестве такого класса может
использоваться и существующий класс.
Этот класс определяет набор данных, входящих в первичный ключ записи,
соответсвующей компоненту EJB.
Должен реализовывать интерфейс java.io.Serializable.
Чаще всего в качестве класса первичного ключа используются
библиотечные класы Integer и String.
Необходимость в декларации своего класса возникает, только если
первичный ключ составной. При этом поля этого класса должны иметь
соответствующие по именам управляемые EJB контейнером свойства в
классе компонента. В таком классе также должен быть определен
конструктор без параметров. Кроме того, важно правильно определить
методы equals() и hashCode(), чтобы контейнер мог автоматически
управлять коллекциями компонентов с такими первичными ключами.
o Локальный интерфейс (local interface)
Введен в EJB 2.0.
Определяет набор методов компонента, к которым можно обращаться
только из того же процесса, где этот компонент находится.
Соответственно, с одной стороны, при работе через локальный интерфейс
обеспечивается большая эффективность, с другой — необходимо быть
уверенным, что этот компонент управляется тем же самым EJB
контейнером, что и обращающийся к нему.
o Локальный внешний интерфейс (local home interface)
Введен в EJB 2.0.
Определяет набор методов создания и поиска компонентов по свойствам.
Тоже может быть использован только в рамках одного процесса с
компонентом, который ищется или строится.
Помимо синхронизации данных комопнентов и соответствующих данных БД, EJB
контейнер берет на себя синхронизацию ссылок между EJB компонентами и
ссылочных полей в БД.
Для этого декларируются свойства (пары get и set методов), имеющие в качестве типов
локальные интерфейсы компонентов, с которыми мы собираемся поддерживать связи,
если связь единичная, либо коллекции (java.util.Collection), если связь
множественная. Типы удаленных интерфейсов в качестве типов свойств,
соответсвующих связям в БД, не поддерживаются. Если нужен доступ по связи в
удаленном интерфейсе, приходится делать особый метод, возвращающий набор
данных, соответсвующий связанному экземпляру, и реализовывать его в классе
компонента.
Пример декларации набора классов и интерфейсов для компонентов, представляющих
клиентов некоторой организации, их счета и сами организации. Часть
соответствующей схемы БД, включающая таблицу клиентов и связанные с ней,
показана на рисунке.
Client
ID : int
FirstName : String
LastName : String
OrganizationID : int
clients
accounts
0..*
0..*
Organization
Account
ID : int
Name : String
ID : int
Balance : double
Credit : double
Status : int
ClientID : int
Рисунок 4. Пример схемы БД.
Примеры кода удаленных интерфейсов. Они декларированы для организаций и
клиентов.
package ru.msu.cmc.prtech.examples.ejb;
import java.rmi.RemoteException;
import java.util.Collection;
import javax.ejb.EJBObject;
public interface OrganizationRemote extends EJBObject
{
public String getName()
throws RemoteException;
public void
setName(String name) throws RemoteException;
}
package ru.msu.cmc.prtech.examples.ejb;
import java.rmi.RemoteException;
import javax.ejb.EJBObject;
public interface ClientRemote extends EJBObject
{
public String getFirstName()
throws RemoteException;
public void
setFirstName(String name) throws RemoteException;
public String getLastName ()
throws RemoteException;
public void
setLastName (String name) throws RemoteException;
}
Примеры кода локальных интерфейсов. Они декларированы для всех компонентов.
package ru.msu.cmc.prtech.examples.ejb;
import java.util.Collection;
import javax.ejb.EJBLocalObject;
public interface OrganizationLocal extends EJBLocalObject
{
public String getName()
;
public void
setName(String name);
public Collection getClients()
;
public void
setClients(Collection clients);
public void addClient(String firstName, String lastName);
public void removeClient(String firstName, String lastName);
}
package ru.msu.cmc.prtech.examples.ejb;
import java.util.Collection;
import javax.ejb.EJBLocalObject;
public interface ClientLocal extends EJBLocalObject
{
public String getFirstName()
;
public void
setFirstName(String name);
public String getLastName ()
;
public void
setLastName (String name);
public OrganizationLocal getOrganization()
;
public void
setOrganization(OrganizationLocal org);
public Collection getAccounts()
;
public void
setAccounts(Collection accs);
public void
addAccount (double initialBalance);
}
package ru.msu.cmc.prtech.examples.ejb;
import javax.ejb.EJBLocalObject;
public interface AccountLocal extends EJBLocalObject
{
public double getBalance()
;
public void
setBalance(double balance);
public double getCredit()
public void
setCredit(double credit)
;
;
public int
public void
;
;
getStatus()
setStatus(int status)
public ClientLocal getClient()
;
public void
setClient(ClientLocal client);
}
Примеры кода внешних интерфейсов. Удаленные внешние интерфейсы декларированы
для клиентов и организаций. Локальные внешние интерфейсы декларированы для
клиентов и счетов, они содержат дополнительные методы создания компонентов.
package ru.msu.cmc.prtech.examples.ejb;
import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.EJBHome;
import javax.ejb.FinderException;
public interface OrganizationHomeRemote extends EJBHome
{
public OrganizationRemote create (Integer id)
throws CreateException, RemoteException;
public OrganizationRemote findByPK(Integer id)
throws FinderException, RemoteException;
}
package ru.msu.cmc.prtech.examples.ejb;
import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.EJBHome;
import javax.ejb.FinderException;
public interface ClientHomeRemote extends EJBHome
{
public ClientRemote create (Integer id)
throws CreateException, RemoteException;
public ClientRemote findByPK(Integer id)
throws FinderException, RemoteException;
}
package ru.msu.cmc.prtech.examples.ejb;
import javax.ejb.CreateException;
import javax.ejb.EJBLocalHome;
import javax.ejb.FinderException;
public interface ClientHomeLocal extends EJBLocalHome
{
public ClientLocal create(Integer id)
throws CreateException;
public ClientLocal createClient(String firstName, String lastName)
throws CreateException;
public ClientLocal findByPK(Integer id)
throws FinderException;
}
package ru.msu.cmc.prtech.examples.ejb;
import javax.ejb.CreateException;
import javax.ejb.EJBLocalHome;
public interface AccountHomeLocal extends EJBLocalHome
{
public AccountLocal createAccount(double initialBalance)
throws CreateException;
public AccountLocal findByPK(Integer pk)
throws FinderException;
}
Примеры кода классов компонентов. Показано, как реализовывать дополнительные, не
поддерживаемые контейнером автоматически, методы работы со связями в классе
организаций и дополнительный метод создания в классе клиентов.
package ru.msu.cmc.prtech.examples.ejb;
import java.util.Collection;
import java.util.Iterator;
import
import
import
import
javax.ejb.CreateException;
javax.ejb.EntityBean;
javax.naming.InitialContext;
javax.naming.NamingException;
public abstract class OrganizationBean
implements EntityBean
{
public Integer ejbCreate(Integer pk)
{
setId(pk);
return null;
}
public void ejbPostCreate(Integer pk)
{ }
public abstract Integer getId ();
public abstract void
setId (Integer pk);
public abstract String getName ();
public abstract void
setName (String name);
public abstract Collection getClients ();
public abstract void
setClients (Collection clients);
public void addClient(String firstName, String lastName)
{
try
{
InitialContext context = new InitialContext();
ClientHomeLocal clientHome =
(ClientHomeLocal)context.lookup("ClientHomeLocal");
ClientLocal client = clientHome.createClient(firstName, lastName);
Collection clients = getClients();
clients.add(client);
}
catch (NamingException e)
{
e.printStackTrace();
}
catch (CreateException e)
{
e.printStackTrace();
}
}
public void removeClient(String firstName, String lastName)
{
Collection clients = getClients();
Iterator it = clients.iterator();
while(it.hasNext())
{
ClientLocal client = (ClientLocal)it.next();
if(
client.getFirstName().equals(firstName)
&& client.getLastName().equals(lastName)
)
{
it.remove();
break;
}
}
}
}
package ru.msu.cmc.prtech.examples.ejb;
import javax.ejb.EntityBean;
public abstract class ClientBean
implements EntityBean
{
public Integer ejbCreate(Integer pk)
{
setId(pk);
return null;
}
public void ejbPostCreate(Integer pk)
{ }
public Integer ejbCreateClient(String firstName, String lastName)
{
setFirstName(firstName);
setLastName(lastName);
return null;
}
public void ejbPostCreateClient(String firstName, String lastName)
{ }
public abstract Integer getId ();
public abstract void
setId (Integer pk);
public abstract String getFirstName ();
public abstract void
setFirstName (String name);
public abstract String getLastName ();
public abstract void
setLastName (String name);
public abstract OrganizationLocal getOrganization ();
public abstract void
setOrganization (OrganizationLocal
org);
public abstract Collection getAccounts()
;
public abstract void
setAccounts(Collection accs);
public void addAccount(double initialBalance)
{
try
{
InitialContext context = new InitialContext();
AccountHomeLocal accountHome =
(AccountHomeLocal)context.lookup("AccountHomeLocal");
AccountLocal account = accountHome.createAccount(initialBalance);
Collection accounts = getAccounts();
accounts.add(account);
}
catch (NamingException e)
{
e.printStackTrace();
}
catch (CreateException e)
{
e.printStackTrace();
}
}
}
package ru.msu.cmc.prtech.examples.ejb;
import javax.ejb.EntityBean;
public abstract class AccountBean implements EntityBean
{
public Integer ejbCreate(Integer pk)
{
setId(pk);
return null;
}
public void ejbPostCreate(Integer pk)
{ }
public Integer ejbCreateAccount(double initialBalance)
{
setBalance(initialBalance);
setCredit(0.0);
setStatus(0);
return null;
}
public void ejbPostCreateAccount(double initialBalance)
{ }
public abstract Integer getId ();
public abstract void
setId (Integer pk);
public abstract double getBalance ();
public abstract void
setBalance (double balance);
public abstract double getCredit ();
public abstract void
setCredit (double credit);
public abstract int getStatus ();
public abstract void setStatus (int status);
public abstract ClientLocal getClient ();
public abstract void setClient (ClientLocal client);
}
Компоненты данных могут также самостоятельно, не полагаясь на EJB контейнер
упарвлять связью своих внутренних данных с записями в БД.
 Для сеансовых компонентов
Всегда должны быть декларированы класс компонента и по крайней мере один из
интерфейсов — удаленный или локальный.
o Удаленный или дистанционный интерфейс (remote interface)
Этот интерфейс определяет методы компонента, к которым можно
обращаться извне его собственного процесса.
Он должен наследовать интерфейс javax.ejb.EJBObject. Обычно такой
интерфейс определяет набор методов, реализующих бизнес-логику,
требующую манипуляций с несколькими компонентами данных.
o Удаленный внешний интерфейс (remote home interface)
Этот интерфейс определяет методы для построения и поиска компонентов с
определенными свойствами, в том числе и извне процесса, в котором они
будут находится.
Он должен наследовать интерфейс javax.ejb.EJBHome. Чаще всего такой
интерфейс определяет метод создания компонента.
o Класс компонента (bean class)
Должен быть декларирован.
Этот класс определяет свойства компонента (обычно соответсвующие его
данным) и реализацию методов удаленного и локального интерфейсов.
Он должен быть неабстрактным классом, реализующим интерфейс
javax.ejb.SessionBean.
Этот класс должен реализовывать методы, декларированные в удаленном и
локальном интерфейсах (но недолжен реализовывать сами эти
интерфейсы!).
Сеансовые компоненты могут связываться с БД, но эта связь должна быть
обеспечена кодом самого класса компонента, например, с использованием
JDBC. Она не поддерживается контейнером автоматически.
Кроме того, этот класс может реализовывать служебные методы поддержки
жизненного цикла (эти методы вызываются EJB контейнером на
определенных фазах жизненного цикла компонента и могут быть
использованы для каких-то специфических действий по мониторингу
состояния компонента или управления дополнительными ресурсами,
которыми контейнер не управляет автоматически).

public void ejbCreate(…)
Служит для создания сеансового компонента по набору параметров,
является реализацией соответсвующего метода локального или
удаленного внешнего интерфейса.
Этот метод вызывается при инициализации экземпляра компонента.

public void setSessionContext(javax.ejb.SessionContext)
Вызывается еще до инициализации экземпляра компонента.
Может быть использован для чтения каких-то данных из контекста,
являющихся общими для всех компонентов данного процесса.

public void ejbActivate()
Вызывается при активизации экземпляра компонента —
вытаскивании его из пула построенных, но не готовых к работе
компонентов, перед использованием для обработки пришедшего
запроса.

public void ejbPassivate()
Вызывается при переводе экземпляра компонента из активного
состояния в пул.

public void ejbRemove()
Вызывается перед удалением данных экземпляра компонента из БД.
Жизненный цикл сеансового компонента существенно зависит от того,
имеет ли он состояние или нет. Сеансовый компонент с состоянием
используется для отслеживания состояния сеанса, связанного с одним
клиентским соединением. Такой компонент не может быть помещен в пул.
Сеансовый компонент без состояния может быть помещен в пул — один
экземпляр используется для обработки многих запросов.
Не
существует
Class.newInstance()
setSessionContext()
ejbCreate()
тайм-аут
или исключение
ejbRemove()
ejbPassivate()
Активен
Пассивен
ejbActivate()
обычные методы
Рисунок 5. Жизненный цикл сеансового компонента с состоянием.
Не
существует
Class.newInstance()
setSessionContext()
ejbCreate()
ejbRemove()
Активен
обычные методы
Рисунок 6. Жизненный цикл сеансового компонента без состояния.
o Локальный интерфейс (local interface)
Введен в EJB 2.0.
Определяет набор методов компонента, к которым можно обращаться
только из того же процесса, где этот компонент находится.
Соответственно, с одной стороны, при работе через локальный интерфейс
обеспечивается большая эффективность, с другой — необходимо быть
уверенным, что этот компонент управляется тем же самым EJB
контейнером, что и обращающийся к нему.
o Локальный внешний интерфейс (local home interface)
Введен в EJB 2.0.
Определяет набор методов создания и поиска компонентов по свойствам.
Тоже может быть использован только в рамках одного процесса с
компонентом, который ищется или строится.
Пример кода сеансового компонента, реализующего метод перевода денег с одного счета
клиента на другой (в принципе, этот метод мог бы быть реализован и в компоненте,
представляющем клиента).
package ru.msu.cmc.prtech.examples.ejb;
import javax.ejb.EJBLocalObject;
public interface TransferLocal extends EJBLocalObject
{
public boolean tranfer(ClientLocal client, double sum,
AccountLocal sender, AccountLocal reciever)
throws IllegalArgumentException, SuspendedAccountException,
TooSmallBalanceException;
}
package ru.msu.cmc.prtech.examples.ejb;
import javax.ejb.CreateException;
import javax.ejb.EJBLocalHome;
public interface TransferHomeLocal extends EJBLocalHome
{
public TransferLocal create() throws CreateException;
}
package ru.msu.cmc.prtech.examples.ejb;
import java.util.Collection;
import javax.ejb.EJBException;
import javax.ejb.SessionBean;
public abstract class TransferBean implements SessionBean
{
public void ejbCreate ()
{ }
public boolean tranfer (ClientLocal client, double sum,
AccountLocal sender, AccountLocal recipient)
throws IllegalArgumentException, SuspendedAccountException,
TooSmallBalanceException
{
if(client == null)
throw new IllegalArgumentException("Argument client is null");
else if(sender == null)
throw new IllegalArgumentException("Argument sender is null");
else if(recipient == null)
throw new IllegalArgumentException("Argument recipient is null");
try
{
Collection accounts = client.getAccounts();
if(!accounts.contains(sender))
throw new IllegalArgumentException
("sender account is not owned by the client");
else if(!accounts.contains(recipient))
throw new IllegalArgumentException
("recipient account is not owned by the client");
if(sender.getStatus() != 0)
throw new SuspendedAccountException
("Suspended account is used as sender");
else if(recipient.getStatus() != 0)
throw new SuspendedAccountException
("Suspended account is used as recipient");
double senderBalance = sender.getBalance();
double recipientBalance = recipient.getBalance();
if(senderBalance + sender.getCredit() < sum)
throw new TooSmallBalanceException
("Balance of sender is insufficient for transfer");
sender.setBalance(senderBalance - sum);
recipient.setBalance(recipientBalance + sum);
return true;
}
catch(IllegalArgumentException e)
{
throw e;
}
catch(SuspendedAccountException e)
{
throw e;
}
catch(TooSmallBalanceException e)
{
throw e;
}
catch(Exception e)
{
throw new EJBException(e);
}
}
}
 Для управляемых сообщениями компонентов
Компонент, управляемый сообщениями, недоступен для удаленных вызовов через
RMI, и поэтому не имеет удаленных и локальных интерфейсов. Обращения к
экземпляру такого компонента организуются в виде посылки сообщений в
соответствиии с интерфейсом Java Message Service (JMS), основные интерфейсы и
классы которого находятся в пакете javax.jms.
Такой компонент требует определения класса компонента, реализующего
интерфейсы javax.ejb.MessageDrivenBean и javax.jms.MessageListener.
Последний интерфейс требует определения метода void onMessage(Message),
который разбирает содержимое пришедшего сообщения и определяет способ его
обработки.
Кроме того, требуется определить еще методы void ejbCreate() для создания
компонента и void ejbRemove() для освобождения ресурсов при его удалении.
Пример реализации класса управляемого сообщениями, использующего описанный
выше сеансовый компонент для обработки асинхронных запросов на перевод денег
между счетами, приведен ниже. В отличие от сеансового компонента, этот получает
всю информацию о необходимом переводе в виде частей сообщения.
package ru.msu.cmc.prtech.examples.ejb;
import
import
import
import
import
import
import
javax.ejb.EJBException;
javax.ejb.MessageDrivenBean;
javax.ejb.MessageDrivenContext;
javax.jms.MapMessage;
javax.jms.Message;
javax.jms.MessageListener;
javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class TransferProcessorBean
implements 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 clientPK
= (Integer)message.getObject("Client");
Integer senderPK
= (Integer)message.getObject("Sender");
Integer recipientPK = (Integer)message.getObject("Recipient");
Double sum
= (Double) message.getObject("TransferSum");
ClientHomeLocal
clientHome
= (ClientHomeLocal)
context.lookup("ClientHomeLocal");
AccountHomeLocal accountHome = (AccountHomeLocal)
context.lookup("AccountHomeLocal");
TransferHomeLocal transferHome =
(TransferHomeLocal)context.lookup("TransferHomeLocal");
TransferLocal transfer = transferHome.create();
ClientLocal client
= clientHome.findByPK(clientPK);
AccountLocal sender
= accountHome.findByPK(senderPK);
AccountLocal recipient = accountHome.findByPK(recipientPK);
transfer.tranfer(client, sum.doubleValue(), sender, recipient);
}
catch (Exception e)
{
throw new EJBException(e);
}
}
public void ejbRemove () throws EJBException
{
try
{
context.close();
}
catch (NamingException e)
{ }
}
}
Дескрипторы развертывания компонентов EJB
Помимо декларации интерфейсов и классов компонентов, для построения EJB
компонента его необходимо написать дескриптор развертывания — XML файл в
специальном формате, определяющий набор компонентов приложения и их основные
свойства.
Чаще всего дескрипторы развертывания не пушится вручную, их подготовку берут на
себя специализированные инструменты для развертывания J2EE приложений. В рамках
данного курса содержимое дескрипторов развертывания будет затронуто в рамках
рассматрения поддержки платформой J2EE инфраструктуры распределенных бизнесприложений и основных служб, в наличии которых такие приложения нуждаются.
Дескриптор развертывания упаковывается вместе с байт-кодом классов компонентов
приложения в JAR-архив, который служит для развертывания этого приложения. При
развертывании такой архив помещают в выделенную директорию, используемую
сервером J2EE для поиска развертываемых приложений, и сервер осуществляет
развертывание (запуск приложения, поиск кода необходимых компонентов при
поступлении запросов) на основании информации, предоставленной дескриптором.
Начало декскриптора развертывания для набора EJB компонентов декларирует тот
факт, что данный файл является дескриптором развертывания EJB компонентов и
составлен в соответствии с определенной версией формата таких дескрипторов. Оно
выглядит следующим образом.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans
2.0//EN" http://java.sun.com/j2ee/dtds/ejb-jar_2_0.dtd">
 Тело дескриптора развертывания набора EJB компонентов заключено внутри тегов
<ejb-jar> </ejb-jar>. Оно может содержать следующие элементы (теги XML).
o <description>
Необязательный элемент.
Содержит описание данное дескриптора развертывания для человека.
Такой же элемент может содержаться внутри многих других тегов,
обрамляющих описание отдельного компонента, связи, роли и пр.,
предоставляя описание этих соответвтующих сущностей.
o <display-name>
Необязателен.
Является меткой, используемой интрументами, работающими с
дескрипторами. Такой же элемент может использовтаься внутри тегов
<entity>, <session>, <message-driven>.
o <small-icon>, <large-icon>
Необязательны.
Указывают файлы иконок внутри JAR-архива приложения, куда входит и
данный дескриптор. Иконки используются для представления JAR-архива в
визуальной среде разработки. Эти же элементы используются внутри
элементов <entity>, <session>, <message-driven> для определения
иконок, представляющих отдельные компоненты.
o <enterprise-beans>
Обязательный элемент, должен появиться ровно один раз.
Содержит набор описаний EJB компонентов в виде элементов <entity>,
<session>, <message-driven>.
o <relationships>
Необязателен, появился в EJB 2.0.
Описывает набор отношений между компонентами, которые соответствуют
связям в схеме БД и автоматически поддерживаются контейнером.
o <ejb-client-jar>
Необязателен.
Содержит путь к JAR-файлу, содержащему классы, необходимые для
работы клиентского приложения, — заглушки, удаленные интерфейсы и пр.
o <assembly-descriptor>
Необязателен.
Содержит элементы, описывающие правила использования компонентов —
роли, политики предоставления доступа, политики создания транзакций и
включения в имеющиеся транзакции и пр.
 Элементы <entity> и <session>.
Служат для описания компонентов данных и сеансовых компонентов. Могут
содержать теги <description> с общим описанием компонента, <display-name> с
меткой, используемой инструментами, <small-icon> и <large-icon> с указанием на
файлы иконок для представления компонента в среде разработки.
o <ejb-name>
Требуется ровно один.
Задает имя компонента.
o <home>
Необязателен в EJB 2.0. В EJB 1.1 требуется ровно один.
Указывает полное имя удаленного внешнего интерфейса.
o <remote>
Необязателен в EJB 2.0. В EJB 1.1 требуется ровно один.
Указывает полное имя внешнего интерфейса.
o <local-home>
Необязателен, появился в EJB 2.0.
Указывает полное имя локального внешнего интерфейса.
o <local>
Необязателен, появился в EJB 2.0.
Указывает полное имя локального интерфейса.
o <ejb-class>
Требуется ровно один.
Указывает полное имя класса компонента.
o <primkey-field>
Необязателен, используется в описании компонентов данных.
Указывает имя поля-первичного ключа, если оно одно и связью компонента
с БД управляет контейнер.
o <prim-key-class>
Требуется ровно один, используется в описании компонентов данных.
Указывает имя класса первичного ключа. Можно отложить точное
определение класса первичного ключа до развертывания, тогда в этом поле
указывается java.lang.Object.
o <persistence-type>
Требуется ровно один, используется в описании компонентов данных.
Имеет значения Bean или Container, в зависимости от того, управляется ли
связь компонента с БД самим компонентом иил контейнером.
o <reentrant>
Требуется ровно один, используется в описании компонентов данных.
Имеет значения true или false, в зависимости от того, допускает ли
компонент повторные вхождения (обращения из экземпляров компонентов,
вызванных данным экземпляром, кроме него самого — автоматическое
управление набором экземпляров EJB компонентов накладывает
ограничения на такие вызовы, поскольку компонент не должен различать
обращения клиентов; использование реентерабельных компонентов не
рекомендуется).
o <cmp-version>
Необязателен, появился в EJB 2.0.
Указывает версию спецификаций EJB, в соответствии с которой разработан
компонент, что определяет способ управления этим компонентом. Может
иметь значения 2.x и 1.x.
o <abstract-schema-name>
Необязателен, появился в EJB 2.0.
Задает уникальный идентификатор компонента для использования в
запросах на языке EJB QL, который используется для описания вручную
запросов к схеме данных при реализации компонента данных,
самостоятельно управляющего связью с СУБД.
o <cmp-field>
Может быть несколько, используется в описании компонентов данных.
Каждый такой элемент описывает одно поле данных, синхронизация
которого с СУБД управляется EJB контейнером. Он может содержать тег
<description> с понятным человеку описанием поля и должен содержать
тег <field-name> с именем поля. В EJB 2.0 это имя совпадает с именем
абстрактного свойства (для которого в классе компонента декларированы
методы get<Name> и set<Name>), а в EJB 1.1 — с именем одного из полей
класса компонента.
o <env-entry>
Может быть несколько, необязателен.
Описывает элемент окружения, доступный через JNDI.
Такой тег может содержать тег <description> (необязателен) и теги <enventry-name> (обязателен), <env-entry-type> (обязателен, это может быть
String или один из тпов-оберток примитивынх типов Java), <env-entryvalue> (необязателен), определяющие имя, тип и значение элемента
окружения. Из кода компонента значение такого элемента можно получить
через интерфейс JNDI используя имя "java:comp/env/<name>".
o <ejb-ref>
Может быть несколько, необязателен.
Предназначен для определения ссылок на другие компоненты, которые
можно использовать для получения удаленного доступа к ним через JNDI.
Такой тег может содержать тег <description> (необязателен) и теги <ejbref-name> (обязателен), <ejb-ref-type> (обязателен, имеет значения
Entity или Session), <remote> (обязателен, определяет имя удаленного
интерфейса компонента), <home> (обязателен, определяет имя удаленного
внешнего интерфейса компонента), <ejb-link> (необязателен, определяет
имя копонента, если он описан в том же дексрипторе развертывания).
Доступ к компоненту можно получить через интерфейс JNDI используя имя
"java:comp/env/<name>". Рекомендуется использовать подконтекст "ejb/".
o <ejb-local-ref>
Может быть несколько, необязателен, появился в EJB 2.0.
Объявляет имя компонента, которое можно использовать для получения
локального доступа к нему через JNDI.
По содержанию аналогичен <ejb-ref>, но вместо удаленных интерфейсов с
помощью тегов <local>, <local-home> описывает локальные.
o
<resource-ref>
Может быть несколько, необязателен.
Описывает ссылки на внешние ресурсы (классы фабрик ресурсов), которые
можно использовать для получения к ним доступа через JNDI.
Такой тег может содержать тег <description> (необязателен) и теги <resref-name> (обязателен), <res-type> (обязателен, указывет полное имя
класса фабрики ресурса), <res-auth> (обязателен, указывает ответственного
за аутентификацию ресурса — Container или Application, т.е. контейнер
или смо приложение).
Доступ к ресурсу можно получить через интерфейс JNDI используя имя
"java:comp/env/<name>". Рекомендуется использовать подконтексты
"jdbc/" для ресурсов подключений к БД, например DataSource,
"jms/" для ресурсов, связанных с передачей сообщений, например
QueueConnectionFactory и TopicConnectionFactory,
"mail/" для ресурсов службы JavaMail,
"url/" для ресурсов, связанных с доступом по Интернет, например
javax.net.URL.
Если несколько компонентов в рамках одной транзакции должны работать
содним ресурсом, в EJB 2.0 этот ресурс должен быть объявлен разделяемым
при помощи необязательного тега <res-sharing-scope>, который может
иметь значения Shareable и Unshareable.
o
<resource-env-ref>
Может быть несколько, необязателен, появился в EJB 2.0.
Описывает ссылки на дополнительные объекты, необходимые для работы с
ресурсами.
Может содержать тег <description> (необязателен) и теги <resource-envref-name> (обязателен), <resource-env-res-type> (обязателен, указывет
полное имя класса дополнительного объекат).
В качестве дополнительных объектов могут фигурировать, например, темы
сообщений, с которыми компонент хочет работать через службу JMS.
o
<security-role-ref>
Может быть несколько, необязателен.
Указывает роли безопасности, используемые данным компонентом. Они
при работе приложения служат для проверки безопасности —
сопоставляются с ролями, которым разрешен доступ к тому или иному
методу, чтоможно проверить при помощи метода
EJBContext.isCallerInRole(String).
Может содержать тег <description> (необязателен) и теги <role-name>
(обязателен), <role-link> (необязателен, служит для сопоставления
указанного имени роли с логической ролью, описанной в <security-role>
раздела <assembly-descriptor>).
o
<security-identity>
Необязателен, появился в EJB 2.0.
Определяет, какую логическую роль будет играть данный компонент при
обращениях к другим компонентам. Для этого может быть использован
вложенный тег <run-as><role-name>…</role-name></run-as> для
указания имени конкретной логической роли, или <use-caller-identity/>
для указания того, что нужно использовать роль вызывающего клиента.
o
<session-type>
Требуется ровно один, используется в описании сеансовых компонентов.
Имеет значения Stateful или Stateless, в зависимости от того, с
состоянием ли данный компонент или без.
o
<transaction-type>
Требуется ровно один, используется в описании сеансовых компонентов.
Имеет значения Container или Bean, в зависимости от того, управляет ли
транзакциями данного компонента контейнер или он сам. В первом случае
соответсвующие транзакции должны быть описаны в разделе <assemblydescriptor>.
o
<query>
Может быть несколько, необязателен, появился в EJB 2.0.
Используется для описания запросов, с помощью которых реализуются
некоторые методы компонентов данных. Запросы описываются на языке
EJB QL, привязыются к методам с пмощью тегов <query-method>.
Например
<query>
<query-method>
<method-name>findByName</method-name>
<method-params>
<method-param>java.lang.String</method-param>
</method-params>
</query-method>
<ejb-ql>
<!CDATA[
SELECT OBJECT(c) FROM Client c WHERE c.name = ?1
]]
</ejb-ql>
</query>
 Элементы <message-driven>
Служат для описания компонентов данных и сеансовых компонентов. Могут
содержать теги <description> с общим описанием компонента, <display-name> с
меткой, используемой инструментами, <small-icon> и <large-icon> с указанием на
файлы иконок для представления компонента в среде разработки.
o <ejb-name>
Требуется ровно один.
Задает имя компонента.
o <ejb-class>
Требуется ровно один.
Указывает имя класса компонента.
o
<env-entry>, <ejb-ref>, <ejb-local-ref>, <resource-ref>, <resourceenv-ref>, <security-identity>, <transaction-type>
Все эти теги имеют то же значение, что и для сеансовых компонентов и
компонентов данных.
o <message-selector>
Селектор сообщений может использовать свойства сообщений в условынх
выражениях для отбрасывания тех из них, которые не должны
обрабатываться данным компонентом. Поскольку синтаксис этих
выражений может конфликтовать с синтаксисом XML, их надо помещать в
специальные разделы CDATA, которые не анализируются XMLпроцессорами.
Например,
<message-selector>
<![CDATA[
(Sum > 500.00) AND (Sum/Count >= 75.00) AND (State IN ('F', 'S',
'A', 'X'))
]]>
</message-selector>
o <acknowledge-mode>
Учитывается, только если компонент сам управляет транзакциями.
Определяет используемый тип подтверждения получения соощений. Может
иметь значения Auto-acknowledge, что означает немедленное
подтверждение получения сообщения, или Dups-ok-acknowledge, если
подтверждение может откладываться — это может повысить
производительность, но и привести к повторным подтверждениям.
o <message-driven-destination>
Определяет тип подписки — на определенную очередь сообщений или на
определенную тему, соответственно, javax.jms.Queue или
javax.jms.Topic.
 Раздел <relationships>
Служит для описания отношений между компонентами, управляемых контейнером.
Для каждого отношния в этот раздел вносится тег <ejb-relation>, который может
иметь следующие элементы.
o <ejb-relation-name>
Необязателен, только один.
Задает имя отношения.
o <ejb-relationship-role>
Обязательно два.
Описывает одну роль в рамах отношения.

<ejb-relationship-role-name>
Определяет имя роли в рамах отношения.

<multiplicity>
Определяет множественность — сколько экземпляров компонента
могут участвовать в этой роли в рамках отношения с одним
экземпляром в другой роли.
Имеет возможные значения One или Many.

<relationship-role-source>
Определяет имя компонента, экземпляры которого играют данную
роль в этом отношении. Внутри тега <ejb-name> задается имя,
которое присвоено компоненту в рамках данного дескриптора.

<cmr-field>
Определяет имя поля, хранящего ссылки, поддерживающие данное
отношение — оно задается в теге <cmr-field-name> и для него в
класе компонента должно быть определено абстрактное свойство с
тем же именем.
 Раздел <assembly-descriptor>
Этот раздел содержит дополнительные указания для сборки компонентов, в
частности следующие.
o <container-transaction>
Возможно несколько, необязателен.
Содержит необязательный элемент <description>, а также приведенные
ниже.
Для компонента данных должно быть по одному такому элементу на
каждый метод удаленных интерфейсов. Сеансовые компоненты,
транзакциями которых управляет контейнер, также должны подчиняться
этому правилу.

<method>
Возможно несколько.
Содержит теги <ejb-name>, указывающий имя компонента, и
<method-name>, указывающий имя метода или знак *, который
обозначает применение указанного атрибута ко всем методам.
Может также включать элементы <description>, <method-params> и
<method-intf>, который может иметь значения Remote, Home, Local,
Local-Home, в зависимости от того, в каком интерфейсе этот метод
декларирован — для поддержки возможности декларировать методы
с одним именем и набором параметром в разных интерфейсах.

<trans-attribute>
Ровно один.
Определяет атрибут транзакции, управляющий политикой включения
в транзакции или создания новых.
Для компонентов данных атрибуты транзакции должны быть
определены для всех методов удаленного интерфейса и методов
декларированных во внешнем интерфейсе, для сеансовых
компонентов — для всех методов удаленных интерфейсов.
Может иметь следующие значения.

NotSupported
Означает, что транзакции не распространяется на данный
метод. Если он вызывается из транзакции, она
приостанавливается до окончания его работы.

Supports
Такой метод продолжает существующую транзакцию и не
создает новой, если был вызван вне транзакции.

Required
Такой метод продолжает существующую транзакцию и
создает новую, если был вызван вне транзакции.

RequiresNew
Такой метод при любых обстоятельствах создает новую
транзакцию, в рамках которой проходит вся его работа.

Mandatory
Такой метод всегда должен вызывться из транзакции. Если он
вызван вне транзакции, создается исключение.

Never
Такой метод всегда должен вызываться вне транзакции. Он
создает исключение, будучи вызван в контексте транзакции.
o <security-role>
Возможно несколько, необязателен.
Определяет роли безопасности, служащих для контроля доступа к методам
компонентов.
В каждом таком элементе могут содержаться теги <description>
(необязателен) и <role-name> (обязателен, он и определяет имя роли).
o <method-permission>
Возможно несколько, необязателен.
Указывает правила доступа ролей, определенных в тегах <security-role>,
к методам компонентов.
Содержит необязательный тег <description> , несколько или один тег
<role-name> и несколько или один тег <method>, кроме того, может
присутствовать тег <unchecked/>, который обозначает отсутствие проверки
прав доступа во время работы, даже если они описаны.
Каждый тег <method> содержит тег <ejb-name>, указывающий имя
компонента, и <method-name>, указывающий имя метода или знак *,
который обозначает применение указанного атрибута ко всем методам.
o <exclude-list>
Необязателен.
Содержит один или несколько тегов <method>, определяющих методы,
которые не должны вызываться при работе приложения. Каждый вызов
такого метода создает исключительную ситуацию.
Пример дескриптора развертывания для описанных ранее компонентов данных приведен
ниже.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans
2.0//EN" http://java.sun.com/j2ee/dtds/ejb-jar_2_0.dtd">
<ejb-jar>
<enterprise-beans>
<entity>
<ejb-name>OrganizationEJB</ejb-name>
<home>ru.msu.cmc.prtech.examples.ejb.OrganizationHomeRemote</home>
<remote>ru.msu.cmc.prtech.examples.ejb.OrganizationRemote</remote>
<local>ru.msu.cmc.prtech.examples.ejb.OrganizationLocal</local>
<ejb-class>ru.msu.cmc.prtech.examples.ejb.OrganizationEJB</ejb-class>
<primkey-field>id</primkey-field>
<prim-key-class>java.lang.Integer</prim-key-class>
<persistence-type>Container</persistence-type>
<reenterant>false</reenterant>
<cmp-field>
<field-name>id</field-name>
</cmp-field>
<cmp-field>
<field-name>name</field-name>
</cmp-field>
</entity>
<entity>
<ejb-name>ClientEJB</ejb-name>
<home>ru.msu.cmc.prtech.examples.ejb.ClientHomeRemote</home>
<remote>ru.msu.cmc.prtech.examples.ejb.ClientRemote</remote>
<local-home>ru.msu.cmc.prtech.examples.ejb.ClientHomeLocal</local-home>
<local>ru.msu.cmc.prtech.examples.ejb.ClientLocal</local>
<ejb-class>ru.msu.cmc.prtech.examples.ejb.ClientEJB</ejb-class>
<primkey-field>id</primkey-field>
<prim-key-class>java.lang.Integer</prim-key-class>
<persistence-type>Container</persistence-type>
<reenterant>false</reenterant>
<cmp-field>
<field-name>id</field-name>
</cmp-field>
<cmp-field>
<field-name>firstName</field-name>
</cmp-field>
<cmp-field>
<field-name>lastName</field-name>
</cmp-field>
</entity>
<entity>
<ejb-name>AccountEJB</ejb-name>
<home>ru.msu.cmc.prtech.examples.ejb.AccountHomeRemote</home>
<remote>ru.msu.cmc.prtech.examples.ejb.AccountRemote</remote>
<local-home>ru.msu.cmc.prtech.examples.ejb.AccountHomeLocal
</local-home>
<local>ru.msu.cmc.prtech.examples.ejb.AccountLocal</local>
<ejb-class>ru.msu.cmc.prtech.examples.ejb.AccountEJB</ejb-class>
<primkey-field>id</primkey-field>
<prim-key-class>java.lang.Integer</prim-key-class>
<persistence-type>Container</persistence-type>
<reenterant>false</reenterant>
<cmp-field>
<field-name>id</field-name>
</cmp-field>
<cmp-field>
<field-name>balance</field-name>
</cmp-field>
<cmp-field>
<field-name>credit</field-name>
</cmp-field>
<cmp-field>
<field-name>status</field-name>
</cmp-field>
</entity>
</enterprise-beans>
<relationships>
<ejb-relation>
<ejb-relation-name>Client-Organization</ejb-relation-name>
<eib-relationship-role>
<eib-relationship-role-name>Organization-has-Clients
</eib-relationship-role-name>
<multiplicity>One</multiplicity>
<relationship-role-source>
<ejb-name>OrganizationEJB</ejb-name>
</relationship-role-source>
<cmr-field>
<cmr-field-name>clients</cmr-field-name>
</cmr-field>
</eib-relationship-role>
<eib-relationship-role>
<eib-relationship-role-name>Client-belongs-to-an-Organization
</eib-relationship-role-name>
<multiplicity>Many</multiplicity>
<relationship-role-source>
<ejb-name>ClientEJB</ejb-name>
</relationship-role-source>
<cmr-field>
<cmr-field-name>organization</cmr-field-name>
</cmr-field>
</eib-relationship-role>
</ejb-relation>
<ejb-relation>
<ejb-relation-name>Client-Account</ejb-relation-name>
<eib-relationship-role>
<eib-relationship-role-name>Client-has-Accounts
</eib-relationship-role-name>
<multiplicity>One</multiplicity>
<relationship-role-source>
<ejb-name>ClientEJB</ejb-name>
</relationship-role-source>
<cmr-field>
<cmr-field-name>accounts</cmr-field-name>
</cmr-field>
</eib-relationship-role>
<eib-relationship-role>
<eib-relationship-role-name>Account-belongs-to-a-Client
</eib-relationship-role-name>
<multiplicity>Many</multiplicity>
<relationship-role-source>
<ejb-name>AccountEJB</ejb-name>
</relationship-role-source>
<cmr-field>
<cmr-field-name>client</cmr-field-name>
</cmr-field>
</eib-relationship-role>
</ejb-relation>
</relationships>
<assembly-descriptor>
<container-transaction>
<method>
<ejb-name>OrganizationEJB</ejb-name>
<method-name>*</method-name>
</method>
<trans-attribute>Required</trans-attribute>
</container-transaction>
<container-transaction>
<method>
<ejb-name>ClientEJB</ejb-name>
<method-name>*</method-name>
</method>
<trans-attribute>Required</trans-attribute>
</container-transaction>
<container-transaction>
<method>
<ejb-name>AccountEJB</ejb-name>
<method-name>*</method-name>
</method>
<trans-attribute>Required</trans-attribute>
</container-transaction>
<security-role>
<role-name>administrator</role-name>
</security-role>
<security-role>
<role-name>user</role-name>
</security-role>
<method-permission>
<role-name>administrator</role-name>
<method>
<ejb-name>OrganizationEJB</ejb-name>
<method-name>*</method-name>
</method>
<method>
<ejb-name>ClientEJB</ejb-name>
<method-name>*</method-name>
</method>
<method>
<ejb-name>AccountEJB</ejb-name>
<method-name>*</method-name>
</method>
</method-permission>
<method-permission>
<role-name>user</role-name>
<method>
<ejb-name>OrganizationEJB</ejb-name>
<method-name>getName</method-name>
</method>
<method>
<ejb-name>OrganizationEJB</ejb-name>
<method-name>getClients</method-name>
</method>
<method>
<ejb-name>ClientEJB</ejb-name>
<method-name>*</method-name>
</method>
<method>
<ejb-name>AccountEJB</ejb-name>
<method-name>*</method-name>
</method>
</method-permission>
</assembly-descriptor>
</ejb-jar>
Литература
[1] Э. Таненбаум, М. ван Стеен. Распределенные системы. Принципы и парадигмы.
СПб, Питер, 2003.
[2] http://java.sun.com/j2ee/1.4/docs/index.html
Документация по платформе J2EE
[3] Р. Монсон-Хейфел. Enterprise JavaBeans. СПб., Символ-Плюс, 2002.
[4] П. Аллен, Дж. Бамбара, М. Ашнаульт, Зияд Дин, Т. Гарбен, Ш. Смит. J2EE.
Разработка бизнес-приложений. ДиаСофт, 2002.
[5] B. A. Tate, J. Gehtland. Better, Faster, Lighter Java. O’Reilly, 2004.
[6] http://java.sun.com/j2se/1.5.0/docs/index.html
Документация по платформе J2SE
[7] Э. Гамма, Р. Хелм, Р. Джонсон, Дж. Влиссидес. Приемы объектноориентированного проектирования. Паттерны проектирования. Питер-ДМК, 2001.
Download