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

Что такое ejb java

  • автор:

Enterprise JavaBeans

Что такое EJB (Enterprise JavaBeans) и для чего она нужна? Можно ли обойтись без EJB при разработке WEB-приложений? Что даёт EJB программистам?

В java WEB-приложении можно использовать JSP (JavaServer Pages), JavaServer Faces (JSF), Struts, GWT. Для работы с базой данных можно использовать JDBC (Java Database Connectivity) или JPA (JBoss Hibernate). Применение в WEB-приложении servlet’a позволяет перехватить обращение к серверу и выполнить определенные действия, т.е. выполнить фильтрацию сообщений. Где и как в WEB-приложении можно использовать EJB?

Опыт показывает, что типовые задачи имеют типовые решения. Это и обеспечивает EJB, который представляет набор «фиксированных» решений типовых проблем, возникающих при разработке серверных приложений, а также проверенная временем схема реализации серверных компонентов. Эти фиксированные решения, или службы, предоставляются «контейнером EJB». Для доступа к этим службам необходимо создать специализированные компоненты, используя декларативные и программные EJB API, и развернуть их на сервере приложений.

Сервер приложений Java EE (Enterprise Edition) включает два основных компонента : WEB-container (для использования JSP, JSF, Struts и т.д.) и EJB-container. Первый компонент используется для создания пользовательского интерфейса и слабо подходит для описания бизнес-логики WEB-приложения. Для этого используется EJB-container.

Enterprise JavaBeans имеет спецификацию написания и поддержки серверных компонентов, содержащих бизнес-логику. Данная технология применяется, как правило, в том случае, если бизнес-логика требует один или несколько следующих сервисов :

  • сервис распределённых транзакций;
  • сервис сохранности данных (persistence);
  • сервис управления данными;
  • сервис событий;
  • сервис именования и каталогов (JNDI);
  • сервис безопасности и ограничения доступа к данным.

Технологию EJB можно рассматривать в двух аспектах: фреймворк и компонент. С точки зрения фреймворка EJB — это технология, предоставляющая для серверной части WEB-приложения множество готовых решений (управление транзакциями, безопасность, хранение информации и т.п.). С точки зрения компонента EJB — это надстройка над POJO-классом, описываемая с помощью аннотаций.

Основные типы компонентов EJB

В языке EJB под компонентом подразумевается зерно (Bean), а под Enterprise подразумевают «корпоративный». EJB делит компоненты (зерна) на несколько типов, исходя из их предназначения :

  • сессионные (Session Beans), которые могут быть
    • stateful — с сохранением текущего состояния;
    • stateless — без сохранения состояния;
    • singleton — один объект для всех приложений (начиная с EJB версии 3.1).

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

    Сеансовый компонент с сохранением состояния EJB stateful автоматически сохраняет свое состояние между обращениями к нему от одного и того же клиента, и завершает свое существование либо по таймауту, либо по явному запросу клиента. Типичным примером компонента с сохранением состояния является корзина с покупками в интернет-магазине.

    Сеансовые компоненты без сохранения состояния EJB stateless не хранят никакой информации о своем состоянии и являются прикладными службами, которые выполняют все необходимые действия в рамках запроса. EJB stateless можно использовать для реализации таких операций, как перевод средств на кредитную карту или проверку кредитной истории клиента. На основе stateless-бинов проектируются WEB-сервисы.

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

    Сеансовые компоненты могут вызываться локально или удаленно, посредством Java RMI. Компоненты-одиночки и компоненты без сохранения состояния могут также экспортироваться в виде веб-служб SOAP (Simple Object Access Protocol) или REST (Representational State Transfer).

    Компоненты управляемые сообщениями MDB (Message-Driven Bean) подобно сеансовым компонентам реализуют некоторую прикладную логику и имеют одно важное отличие: клиенты никогда не вызывают методы MDB напрямую. Вместо этого компоненты MDB вызываются для обработки отправленных на сервер сообщений, что позволяет организовать асинхронный обмен сообщениями между частями системы. Типичными примерами подобных серверов сообщений могут служить IBM WebSphere MQ, Oracle Advanced Queueing и TIBCO. Компоненты MDB, как правило, применяются для повышения надежности интеграции систем и асинхронной обработки данных. Примером MDB сообщения может быть запрос на доставку товарных запасов от автоматизированной системы розничной торговли к системе управления поставками.

    Entity Bean и Java Persistence API

    EJB тесно связан с двумя спецификациями : с JPA, которая является стандартом хранения данных для Java EE и с CDI (Contexts and Dependency Injection) которая обеспечивает возможность внедрения зависимостей и предоставляет службы управления контекстом для всех компонентов Java EE, включая EJB.

    Возможность автоматического сохранения объектов в реляционной БД с использованием технологии объектно-реляционного маппинга (ORM) — так называемый механизм работы с persistence объектами, является одним из главных достоинств EJB. В контексте EJB persistence провайдер — это ORM-фреймворк, который поддерживает JPA, определяющий стандарт для :

    • конфигурации маппинга Entity Bean и его отображения в БД;
    • EntityManager API — стандартный API для CRUD (Create, Read, Update, Delete) операций над сущностями;
    • Java Persistence Query Language (JPQL) — для поиска и получения данных приложения.

    EntityManager API — это интерфейс, который связывает класс сущности приложения (Entity Bean) и её представления в БД. EntityManager знает как нужно добавлять сущности в БД, обновлять и удалять их, а также предоставляет механизмы для настройки производительности, кэширования, транзакций и т.д. Для этого используется язык запросов JPQL, очень похожий на SQL.

    Контейнеры EJB-container

    Java-приложениям для работы нужна виртуальная машина JVM (Java Virtual Machine). Сеансовым компонентам и компонентам MDB для работы точно также необходим контейнер EJB. Можно считать EJB-container развитием базовой идеи JVM. Так же, как JVM прозрачно управляет памятью, EJB-container обеспечивает компоненты EJB такими службами, как обработка транзакций, поддержка безопасности, удаленные взаимодействия и веб-службы.

    Согласно спецификации EJB3 контейнер предоставляет службы, применимые только к сеансовым компонентам и MDB. Операция добавления компонента EJB3 в контейнер называется развертыванием (deployment). После того, как EJB-компонент благополучно развернут в контейнере, он готов к использованию приложениями.

    В Java технологиях контейнеры не ограничены только EJB3. Контейнер Java EE – это сервер приложений с поддержкой EJB3, веб-контейнеров (сервлеты, JSP, JSF, Struts, GWT) и других Java EE API и служб. Примерами реализаций контейнеров Java EE могут служить следующие серверы приложений : Oracle WebLogic, GlassFish, IBM WebSphere, JBoss и Caucho Resin.

    Определение наименования компонентов EJB

    Формат именования компонентов EJB имеет следующий вид :

    • workspace — пространство имен;
    • app-name — наименование приложения;
    • module-name — наименование модуля;
    • ejb-name — наименование компонента;
    • object-name — полное наименование объекта.

    В представленном формате именования компонентов EJB ряд элементов (workspace, module-name, ejb-name) присутствуют в имени всегда и являются обязательными. А элементы [app-name] и [!object-name] могут отсутствовать и считаются необязательными.

    workspace

    Сервер Java EE может включать четыре пространства имен workspace, каждое из которых представляет свою область видимости.

    java:comp Область видимости компонента. Все компоненты EJB из WAR-файла попадают в одно общее пространство имен java:comp. Скорее всего Вам редко придется пользоваться этим пространством имен, поскольку основное его предназначение – сохранение обратной совместимости с версиями Java EE 6 и ниже, где java:comp было единственным стандартным пространством имен.
    java:module Область видимости модуля. Все компоненты модуля попадут в одно пространство имен java:module. Для обратной совместимости java:comp и java:module интерпретируются в веб-модулях как одно пространство имен. Когда это возможно, вместо java:comp следует использовать java:module.
    java:app Область видимости приложения. Компоненты из всех модулей одного приложения размещаются в общем пространстве имен java:app. Примером приложения может служить архив EAR. Все WAR- и EJB-компоненты, развертываемые из EAR-архива попадают в это пространство имен.
    java:global Глобальное пространство имен. В java:global размещаются все компоненты из всех модулей и всех приложений.
    app-name

    Значение наименования приложения [app-name] не является обязательным и присутствует только в именах тех компонентов EJB, которые развертываются на сервере приложений из EAR-архива. Если архив EAR не использовался, тогда [app-name] отсутствует в переносимых именах JNDI компонентов EJB. По умолчанию в качестве значения [app-name] выбирается имя EAR-файла без расширения .ear. Переопределить это умолчание можно в файле application.xml.

    module-name

    Значение module-name всегда присутствует в наименовании ресурса и является обязательным. Оно зависит от того, как развертываются модули, содержащие компоненты EJB. Если компоненты развертываются из отдельных файлов EJB-JAR (JAR-файлы развертываются непосредственно), в качестве значения module-name выбирается имя EJB-JAR файла без расширения .jar. Это умолчание можно переопределить с помощью элемента module-name в конфигурационном файле META-INF/ejb-jar.xml.

    Если развертываются компоненты, являющиеся частью веб-модуля (WAR-файл), по умолчанию в качестве значения module-name выбирается имя WAR-файла без расширения .war. Это умолчание можно переопределить с помощью элемента module-name в конфигурационном файле WEB-INF/web.xml. Если развертываются компоненты, являющиеся частью приложения (EAR-файл), по умолчанию значение module-name определяется в зависимости от того, являются ли компоненты EJB частью EJB-JAR или WAR в EAR. При развертывании компонентов из WAR-файла, выбор значения module-name выполняется в соответствии с правилом для WEB-модулей, которое можно переопределить в дескрипторе WEB-INF/web.xml. При развертывании компонентов из файлов EJB-JAR, значением module-name становится полный путь к каталогу, где находится файл EJB-JAR внутри EAR, плюс имя файла EJB-JAR без расширения .jar, которое можно переопределить в файле META-INF/ejb-jar.xml.

    ejb-name

    Значение наименования компонента ejb-name является обязательным и всегда присутствует в наименовании ресурса. Для компонентов EJB, помеченных аннотациями @Stateless, @Stateful или @Singleton, в качестве значения ejb-name по умолчанию выбирается имя класса компонента. Это значение можно переопределить с помощью атрибута name() аннотации. Для компонентов EJB, объявленных в файле ejb-jar.xml, значение ejb-name определяется с помощью элемента bean-name.

    object-name

    Значение полного наименования объекта [!object-name] является обязательным, и переносимые имена компонентов EJB с этим элементом всегда будут присутствовать в JNDI. Но сервер EE также требует, чтобы в JNDI присутствовало имя без этого значения. Такое «усеченное» наименование может пригодиться, когда доступ к компоненту осуществляется через единственный интерфейс (или если компонент вообще не имеет интерфейса).

    Что такое ejb java

    Enterprise Java Beans — EJB

    EJB — корпоративный java-компонент.

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

    Все технологии, рассмотренные до этого не позволяли распределять бизнес-логику (кроме вебсервисов). EJB предназначено для Java и делает распределение бизнес-логики легче.

    EJB предоставляет фреймворк, упрощающий разработку распределенных приложений.

    Спецификация EJB определяет три типа распределенных компонентов

    1. sessions beans — сеансовые
    2. message-driven beans (MDB) — компоненты управляемые сообщениями
    3. entity beans — компоненты сущностей

    sessions beans

    sessions beans могут быть 3 видов:

    • stateless — без сохранения состояния, при обращении к методам которых состояние не сохраняется. Это позволяет использовать один и тот же экземпляр для обработки зарпосов от разных клиентов.
    • stateful — с состоянием. Это сеансовые компоненты, которые сохраняют диаологове состояние (состояние, которое должно быть сохранено между обращениями клиента). По сути можно считать такой компонент аналогом сессии в jsp
    • singleton. Создается единственный экземпляр в конейнере и все запросы обрабатываются им, при чем он должен обеспечить параллельный доступ.

    stateless

    Компоненты stateless по настоящему позволяют кое-что сохранять в своих полях, но оно не может иметь отношение для взаимодействия с конкретным клиентом. Сохранять такие компоненты stateless могут, например, подключение к БД. Но следует иметь в виду, что такие сохраненные переменные нельзя передавать клиенту в качестве результата выполнения какого-либо метода, потому-что контейнер, внутри которого работают все эти компоненты, может направить вызов обращения клиента к любому из имеющихся экземпляров компоненты stateless. Контейнер определяем к какому компоненту обратиться на основании того, какой компонент является наименее загруженным.

    Важно, еще раз, все компоненты stateless должны быть идентичны и обращение клиента может быть направлено к любому из этих компонент.

    Вообще, контейнер управляет всеми видами компонентов EJB.

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

    Stateless компоненты требуют меньше ресурсов и позволяют более гибко управлять нагрузкой. Поэтому, когда возможно, лучше использовать именно их. Но есть и другая сторона: если компонент не хранит данные в себе, значит их хранит клиент и код клиента усложняется.

    Компоненты stateless не должны реализовывать javax.ejb.SessionSynchronization. Также на основе stateless компоненты можно реализовать веб-сервис.

    stateful

    Сеансовые компоненты stateful предназначены для кеширования информации между запросами клиента в течении клиентского сеанса. Для каждого клиента создается свой собственных экземпляр компонента stateful. Экзпмляр stateful уничтожается либо по запросу клиента, либо после определенного периода неактивности. Возможны ситуации, когда некоторые экземпляры простаивают и расходуют ресурсы, что неэффективно. Чтобы с этой ситуацией бороться, существует рабочий набор — компоненты, используемые в данный момент. Если компонент не используется некоторое время, то он может быть исключен из рабочего процесса (сериализован, сохранен в специальное хранилище и выгружен из ОЗУ). С этим могут быть проблемы, если компонент держит внутри себя соединения или открытые сокеты, поэтому, желательно, прежде чем компонент будет выгружен из рабочего набора, чтобы он эти ресурсы освободил.

    Перевод из рабочего процесса называют passication (instance passivation), а обратный перевод называется активацией (activation).

    Чтобы дать компоненту освободить ресурсы, перед переводом в пассивное состояние, контейнер вызывает метод, помеченный аннотацией @PrePassivate и реализовать этот функционал должен программист.

    При переводе из пассивного состояния в активное, конейтенр вызовет метод @PostActivate, который должен восстановить компонент.

    На перевод компонентов в пассивное состояние накладываются ограничения, например:

    • нельзя перевести компонент в пассивное состояние, если выполняется транзакция
    • если один из сущностных компонентов, принадлежащих сеансовуму компоненту, не сериализуем

    В этих случаях контейнер просто не может перевести в пассивное состояние. Сеансовые компоненты без сохранения состояния не переводятся, а просто уничтожаются.

    singleton

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

    Экземпляр singleton разделяется между всеми клиентами, в отличии от двух предыдущих типов сеансовых компонентов.

    JMS

    Компоненты управляемыми сообщениями были введенеы в спецификацию EJB2. Они позволяют получать и обрабатывать jms-сообщения (Java Messaging Services). JMS, в отличие от сокетов, обеспечивает надежную передачу сообщения. Например, если клиент, для которого предназначено сообщение, не работает, то сообщение будет сохранено на сервере и будет передано клиенту когда он заработает. JMS поддерживает два типа сообщений:

    Очередь — каждое сообщение обрабатывается только одним получателем.

    Тема — каждое сообщение отправляется всем подпищикам данной темы.

    Компоненты, ориентированные на сообщения, работают в асинхронном режиме, выполняют какие-либо действия по получению сообщения, являются относительно короткоживущими (в отличие от entity-компонент), т.е. при завершении работы контейнера, при завершении компонента, он теряет свое состояние (как и ранее рассмотренные).

    Также компоненты могут поддерживать транзакцию и не предоставляют напрямую работу с БД (работа с БД осуществляется с помощью entity-компонент).

    Контейнер может поддерживать пул MDB-компонентов.

    Любой MJB-компонент может быть удаленным или локальным. Удаленный расположен за пределами текущего EJB-контейнера (на другой машине). Локальный расположен в том-же контейнере, где и обращающийся к нему компонент. Такое разделение сделано, чтобы можно было экономить ресурсы при обращении к локальным компонентам.

    Для того, чтобы объявить удаленный компонент, можно поступить 2 способами.

    Способ 1: объявить Java-интерфейс и пометить его аннотацией @Remote из javax.ejb.Remote

    Класс, реализующий этот интерфейс будет удаленным компонентом.

    Способ 2: пометить сам класс аннотацией @Remote и в качестве параметра передать название интерфейса

    @Remote(InterfaceName) public calss . 

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

    Объявление локального интерфейса осуществляется аналогично, только используется аннотация @Local, а не @Remote.

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

    Один и тот же компонент может быть и удаленным и локальным, для этого надо будет реализовывать два интерфейса, т.к. один и тот же интерфейс не может быть и удаленным и локальным. (как правило так не делают).

    В принципе, ejb-компонент без сохранения состояния может являться веб-сервисом, и обращаться к нему можно как к обычному веб-сервису, но для этого он должен быть помечен javax.jws.WebService.

    Сеансовые компоненты могут быть трех разным типов и помечаются аннотациями из javax.ejb.Stateless javax.ejb.Statefull, javax.ejb.Singleton

    В случае с компонентами управляемыми сообщениями, новый интерфейс реализовывать не обязательно. Сам класс должен быть помечен аннотацией javax.ejb.MessageDriven, но он должен реализовать существующий интерфейс javax.jms.MessageListener (это интерфейс должен реализовывать события, чаще всего это onMessage).

    Требования, предъявляемые к компонентам

    Класс, описывающий сеансовый компонент:

    1. должен быть public
    2. не может быть final
    3. должен быть public-конструктор без параметров
    4. не должен определяться метод finalize()

    При этом надо иметь в виду, что поддержка аннотаций появилась только с EJB 3 версии. В более ранних версиях требовалось, чтобы компонент наследовался от EJBObject или EJBLocalObject.

    Клиенты

    Клиенты, работающие с EJB-компонентами, никогда не работают с этими компонентами напрямую. Они работают с заглушкой. Заглушка уже преобразует обращения в соответствии с протоколом передачи распределенных объектов по сети, там запросы демаршализируются и контейнером производится обращение к нужным EJB-компонентам.

    Клиент не знает конкретную реализацию EJB компонента и можно даже поменять тип компонента с stateless на stateful без изменения кода переменных.

    Как клиенты могут работать с объектами? (в качестве клиента могут быть и сервлеты, и jsp-страницы и javaSE и другой EJB-компонент).

    В случае с stateless компонентом: он может быть представлен как веб-сервис и, тогда, работа с ним будет аналогично вебсервисам:

    @WebServiceRef public StockQuesteService stockServie . .. получается port, работаем с port как обычно. 

    Со всеми остальными типами компонентов используется два способа для работы клиентов с этими компонентами (нужно получить ссылку на компонент).

    Впрыскивание ресурсов (DI):

    @EJB Cart cart; 

    При этом можно передавать имя компонента, но, если имя не передано, то конейнет выполнит поиск EJB-компонента соответствующему типа, перед которым стоит аннтация (у нас Cart) и ссылка будет присвоена переменной.

    @Resource SessionContext ctx; Cart cart = (Cart) ctx.lookup("cart"); 

    После того, как получена ссылка, с ней можно обращаться как с POJO-объектом.

     @EJB Cart cart; cart.startShopping(); card.addItem(633); 

    Возможно еще получать ссылку не на интерфейс EJB-объекта, а на класс EJB-объекта. (В предыдущем примере Cart определяло интерфейс, который реализован пусть будет CartBean).

    Это можно сделать так:

    @EJB CartBean cart; 

    Это называют безинтерфейсным представлением объекта.

    @Remote public interface MathOperationRemote 
    @Stateless public class MathOperatonBean implements MathOperationRemote 

    К бизнес-методам есть тоже ограничения

    1. не рекомендуется начинать название метода с ejb
    2. метод должен быть public
    3. не может быть final или static

    Получение ссылки на EJB-объекты

    Несколько подробнее про получение ссылок на EJB-объекты: DI можно использовать не всегда, эти зависимости должен кто-то инжектить. Как правило это делает контейнер (сервер приложений). Т.о. если клиентом является сервлет или другой EJB-объект, то DI использовать можно, т.к. он будет выполняться контейнером и он произведет впрыскивание. Если же клиентом является обычное JavaSE-приложение, то DI не сработает и, в этом случае, необходимо использовать JNDI. Кроме того, существует так называемый клиентский контейнер (ACC application client container). Этот клиентский контейнер может провести впрыскивание зависимостей в обычное JavaSE приложение. Вместо с glassfish поставляется клиентский контейнер appplitn и чтобы ее использовать можно использовать appclient -client EJBMathOperClient.jar, где в jar-файле находится сам клиент.

    Не рекомендуется в одном архиве компоновать и клиентскую часть и разворачивание ее на сервере. Т.е не должно быть клиентской части и класса main от JavaSE.

    В случае использования JNDI тоже не все так просто. Если приложение работает в конейнере, то никаких проблем.

    Contex ctn = new InitialContext() mops = (MathOperationRemote) ctx.lookup(jngiName); 

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

    Пример для JBOSS:

    properties p new Properties(); p.put (Cntext.INITIAL_CONText_FACTORY, "org.jnp.interface.NamingContextFactory"); p.put( context.URL. . 
    Context ctx = getInitialContext() jndiName = MathOperationRemote.class.getName(); MathOperationRemote mops = (Math. ) ctx.lookup(jndiName); 

    Об именовании EJB-объектов:

    К каджому EJB-объекту можно получить ссылку по нескольким именам. (через интерфейс и через название класса, реализующего интерфейс). Но есть еще способы:

    1) Через глобальный контекст

    где испоьзуется если приложение было упакован в ear -архив (

    поиск при этом проводится в контексте текущего приложения

    это если ищем внутри текущего сервера

    Пример: пусть есть fooejb.jar без дескрипотора как stan-alone модуль

    java:global/fooejb/FooBean java:global/fooejb/FooBean!com.acme.Foo java:app/fooejb/FooBean java:module/FooBean java:module/FooBean!com.acme.Foo

    В случае если fooejb.jar внутри fooapp.ear, то обращение

    java:global/fooapp/fooejb/FooBean java:app/fooejb/FooBean java:module/FooBean

    Что такое ejb java

    Основная идея, лежавшая в разработке технологии Enterprise JavaBeans — создать такую инфраструктуру для компонент, чтобы они могли бы легко «вставляться» («plug in») и удаляться из серверов, тем самым увеличивая или снижая функциональность сервера. Технология Enterprise JavaBeans похожа на технологию JavaBeans в том смысле, что она использует ту же самую идею (а именно, создание новой компоненты из уже существующих, готовых и настраиваемых компонент, аналогиично RAD-системам), но во всем остальном Enterprise JavaBeans — совершенно иная технология.

    Отличие от JavaBeans

    Технология Enterprise JavaBeans описывается не спецификацией JavaBeans Component Specification, а совсем другим документом — Enterprise JavaBeans Specification. Если JavaBeans имеют дело лишь с клиентскими компонентами (как правило, это GUI-компоненты, или компоненты, с ними связанные), то EJB описывает каким образом внутри EJB-системы взаимодействуют между собой клиенты и серверы, как EJB-системы взаимодействуют с другими системами и какова роль различных компонент этой системы.

    Цели, лежащие в основе технологии EJB

    • облегчить разработчикам создание приложений, избавив их от необходимости реализовывать с нуля такие сервисы, как транзакции (transactions), нити (threads), загрузка (load balancing) и другие. Разработчики могут сконцентрироваться на описании логики своих приложений, оставляя заботы о хранении, передаче и безопасности данных на EJB-систему. При этом все равно имеется возможность самому контролировать и описывать порученные системе процессы.
    • описать основные структуры EJB-системы, описав при этом интерфейсы взаимодйествия (contracts) между ее компонентами.
    • EJB преследует цель стать стандартом для разработки клиент/сервер приложений на Java. Таким же образом, как исходные JavaBeans (Delphi, или другие) компоненты от различных производителей можно было составлять вместе с помощью соответствующих RAD-систем, получая в результате работоспособные клиенты, таким же образом серверные компоненты EJB от различных производителей также могут быть использованы вместе. EJB-компоненты, будучи Java-классами, должны без сомнения работать на любом EJB-совместимом сервере даже без перекомпиляции, что практически нереально для других систем.
    • EJB совместима с Java API, может взаимодействовать с другими (не обязательно Java) приложениями, а также совместима с CORBA.
    Основы технологии EJB

    Для того чтобы понять, как работает и как устроена EJB-система, сначала необходимо рассмотреть ее основные части: EJB-компоненту (component), EJB-контейнер (container) и EJB-объект (object).

    EJB-компонента (The Enterprise JavaBeans component)

    Отдельная EJB-компонента представляет цобой компоненту в том же смысле что и традиоционный JavaBeans «bean» («зерно»). Компоненты EJB выполняются внутри EJB-контейнера, который, в свою очередь, выполняется внутри EJB-сервера. Любой сервер, который в состоянии поддерживать EJB-контейнеры и предоставлять им необходимые сервисы, может быть EJB-сервером (то есть многие из существующих серверов могут быть просто расширены до поддержки Enterprise JavaBeans).

    EJB-компонента представляет из себя Java-класс, который реализует некоторую бизнес-логику. Все остальные классы в EJB-системе либо реализуют поддержку клиент/сервер взаимодйествий между компонентами, либо реализуют некоторые сервисы для компонент.

    EJB-контейнер (The Enterprise JavaBeans container)

    EJB-контейнер — это то место, где «живет» EJB-компонент. EJB-контейнер реализует для находящихся в нем компонент такие сервисы как транзакции (transaction), управление ресурсами, управление версиями компонент, их мобильностью, настраиваемостью, мобильностью, жизненным циклом. Так как EJB-контейнер реализует все эти функции, то разработчик EJB-компонент может не реализовывать их самостоятельно, а просто вызывать соответсвующие методы у контейнера (правила вызова методов у контейнера описываются в спецификации). Как правило, в одном EJB-контейнере живет несколько однотипных EJB-компонент.

    EJB-объект (EJB-object) и удаленный интерфейс (remote interface)

    Клиентские приложения вызывают методы на удаленных EJB-компонентах через EJB-объект (EJB-object). EJB-объект реализует «удаленный интерфейс» EJB-компоненты на сервере. Суть в том, что находящаяся на сервере EJB-компонента, помимо бизнес-функций, ради которых она была разработана, должна реализовывать также некоторые функции, определяемые спецификацией, которые служат для «управления» EJB-компонентой со стороны контейнера. EJB-объект реализует лишь бизнес-интерфейс для EJB-компоненты, являясь, в некотором смысле, «промежуточным» звеном между клиентом и EJB-компонентой.

    EJB-объекты и EJB-компоненты представляют собой разные классы, хотя «снаружи» (при взгляде на их интерфейсы), они выглядят одинаково. Это происходит потому, что они реализуют один и тот же интерфейс (а именно, интерфейс, описанный для EJB-компоненты). Однако при этом они выполняют совершенно разные функции. EJB-компонента выполняется на сервере, внутри EJB-контейнера и реализует бизнес-логику, в то время как EJB-объект выполняется у клиента и удаленно вызывает методы у EJB-компоненты.

    В качестве поясняющего примера рассмотрим видеомагнитофон. Предположим, что он является EJB-компонентой. EJB-объект, в таком случае, является аналогом пульта управления (remote control) этого магнитофона. У пульта управления есть все те же кнопки, что и на передней панели видеомагнитофона. Нажатие кнопки на пульте управления приведет к такому же эффекту, что и нажатие кнопки, расположенной на самом магнитофоне, но в результате только магнитофон, а не пульт управления, начнет выполнять функции.

    Как работает система

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

    Session и Entity beans

    Существует два различных типа «бинов»: session и entity. Рассмотрим их более подробно.

    Session bean

    Session bean представляет собой EJB-компоненту, связанную с одним клиентом. «Бины» этого типа, как правило, имеют ограниченный срок жизни (хотя это и не обязательно), и редко участвуют в транзакциях. В частности, они обычно не восстанавливаются после сбоя сервера. В качестве примера session bean можно взять «бин», который живет в веб-сервере и динамически создает HTML-страницы клиенту, при этом следя за тем, какая именно страница загружена у клиента. Когда же пользователь покидает вэб-узел, или по истечении некоторого времени, session bean уничтожается. Несмотря на то, что в процессе своей работы, session bean мог сохранять некоторую информацию в базе данных, его предназачение заключается все-таки не в отображении состояния или в работе с «вечными объектами», а просто в выполнении некоторых функций на стороне сервера от имени одного клиента.

    Entity bean

    Entity bean, наоборот, представляет собой компоненту, работающую с постоянной (persistent) информацией, хранящейся, например, в базе данных. Entity beans ассоциируются с элементами баз данных и могут быть доступны одновременно нескольким пользователям. Так как информация в базе данных является постоянной, то и entity beans живут постоянно, «выживая», тем самым, после сбоев сервера (когда сервер восстанавливается после сбоя, он может восстановить «бин» из базы данных).

    Например, entity bean может представлять собой строку какой-нибудь таблицы из базы данных, или даже результат операции SELECT. В объектно-ориентированных базах данных, entity bean может представлять собой отдельный объект, со всеми его атрибутами и связями.

    Клиент создает на EJB-сервере объекты и работает с ними так же, как если бы это были локальные объекты. Это до предела упрощает разработку клиентов — практически нет разницы между написанием клиента для локальной машины и для клиента EJB-сервера. Разработчик может легко создавать, использовать и уничтожать объекты, а эти объекты, в свою очередь, выполняются на сервере. Итак, session beans в большинстве своем работают с информацией, относящейся к «диалогу» между клиентом и сервером, в то время как entity beans представляют собой экземпляры данных и работают с «постоянными» данными из баз данных.

    Каждый экземпляр «бина» имеет свой уникальный идентификатор. Для entity объектов уникальный идентификатор, как правило, совпадает с (или каким-либо образом получается из) уникального идентификатора данных, которые он представляет. То есть в данном случае существует нечто вроде первичного ключа для всех entity beans. Идентификатор же session beans может быть практически любым — например он может состоять из имени сервера и порта удаленного соединения, а может создаваться случайным образом.

    Создание серверных объектов

    Итак, приложение-клиент соединяется с EJB-сервером и посылает ему запрос на создание «бина» (Enterprise JavaBean) для обработки своих запросов. Сервер отвечает на такой запрос созданием объекта на стороне сервера (экземпляр EJB-компоненты) и возвращает клиенту прокси-объект (EJB-объект), чей итерфейс совпадает с интерфейсом созданной EJB-компоненты и чьи методы перенаправляют вызовы собственно экземпляру компоненты. После этого приложение-клиент работает с EJB-объектом как с локальным объектом, даже и не подозревая, что всю работу выполняет не EJB-объект, а удаленная компонента на сервере. Необходимо заметить, что созданием и удалением EJB-компонент на сервере занимается EJB-контейнер.

    Home interface

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

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

    Когда приложение-клиент хочет создать «бин» на сервере, оно вначале использует Java naming and Directory Interface (JNDI) для нахождения объекта с искомым интерфейсом. JNDI представляет собой стандартное расширение Java-пакета и предоставляет глобальный сервис для любого Java-окружения, позволяя Java-программам находить и использовать существующие ресурсы просто по имени, а также позволяя просто извлекать информацию о ресурсах. Использование EJB сервисов, аналогичных JNDI, еще раз подчеркивает тот факт, что цель EJB-систем — полная совместимость с Java API.

    Когда клиент получил ссылку на объект с «родным» интерфейсом, он вызывет у этого объекта любой из методов create для создания экземпляра компоненты на сервере. При получении вызова create локальный (находящийся у клиента) объект с «родным» интерфейсом удаленно вызывает метод у EJB-контейнера, который и создает экземпляр «бина», возвращая клиенту EJB-объект. После этого приложение-клиент вызывает у EJB-объекта методы, исполнение которых перенаправляется на сервер (сначала контейнеру, а потом уже компоненте). Контейнер, при получении вызова к компоненте, просто вызывает у нее соответствующий метод. Единственное, за что ответственен при этом контейнер — обработка некоторых ошибок (таких, например, как отсутствие искомого экземпляра класса) и соответственное возбуждение исключительных ситуаций.

    Entity «бины», помимо упоминавшихся, имеют дополнительный интерфейс finder, который используется для поиска по уникальному ключу экземпляра «бина».

    И, как уже говорилось, «родной интерфейс» включает в себя методы, сообщающие контейнеру о необходимости удалить экземпляр компонента. В этом случае сервер производит удаление экземпляра, и любая следующая попытка вызвать метод у удаленной компоненты приведет к возникновению исключительной ситуации.

    Литература
    1. Mark Johnson, A beginner’s guide to Enterprise JavaBeans, JavaWorld, October 1998.
    2. Sun Microsystems, Enterprise JavaBeans Specification version 1.0, 1998.
    3. Sun Microsystems, JavaBeans Component Specification.
    4. Anne Thomas, Enterprise JavaBeans Server Component Model for Java (Whitepaper), 1997.

    Enterprise JavaBeans

    Enterprise JavaBeans (EJB) — это управляемый компонент, принадлежащий стороне сервера для модульного конструирования промышленного приложения [7].

    Итак, начнем наш тур по Enterprise JavaBeans путем исследования значения этих слов.

    «Компонент» — означает, что EJB распространяются в бинарном формате и могут настраиваться, так что клиентский программист может использовать их для создания собственного приложения. Эта та же самая концепция, которую мы видели в графических компонентах JavaBean: вы покупаете компонент JavaBean для создания части графической круговой диаграммы, вы встраиваете его в ваше приложение (обычно при использовании RAD инструмента, такого как JBuilder), вы настраиваете параметры компонента по своему желанию (например, цвет фона), и вы используете его в вашем приложении, создающем отчеты. У вас нет исходного кода для компоненты круговой диаграммы, тем не менее, вы можете подстроить его в соответствии со своими нуждами. Способ, которым вы внедряете, настраиваете и используете Enterprise JavaBeans, не похож на то, как вы работаете с обычным JavaBeans, но концепция сущности компонента та же самая. Это важно для создания терминологического различия между компонентами и экземплярами: мы используем термин Enterprise JavaBeans для указания типа компонента — например EJB, представляющий банковский счет; и мы используем термин «экземпляр EJB» для указания на объект, который представляет специфический банковский счет с уникальным номером счета.

    «Сторона сервера» означает, что объект EJB размещается в процессе на том же сервере, а не на клиентской машине. EBJ может предлагать удаленный просмотр, локальный просмотр или оба. Термин «просмотр» не относится к графическому просмотру; здесь мы говорим о способе, которым EJB предоставляет свой интерфейс. Если EJB представляет удаленный просмотр, все вызовы методов между клиентским кодом и удаленным экземпляром происходят через протокол удаленного вызова процедур, такого как RMI-IIOP. Если EJB представляет локальный просмотр, клиентский код должен быть в том же процессе, что и EJB объект, а все вызовы являются прямыми вызовами методов.

    Как вы видели в Главе 16, способность выполнять удаленный вызов процедур подразумевает присутствие двух фундаментальных частей архитектуры: именованные службы и RPC прокси. Именованные службы — это сетевые службы, которые клиентский программист использует для нахождения гетерогенных ресурсов сети. RPC прокси — это программно сгенерированный Java класс, созданный на клиентской стороне, который представляет тот же самый интерфейс, что и специфичный удаленный компонент. Так как он предоставляет тот же интерфейс, клиенты не могут общаться один с другим, а прокси может претендовать на роль удаленного экземпляра. Но поскольку это прокси, он делегирует все вызовы удаленному компоненту, реализующему сетевой код и прячущий все сложность от клиента. В EJB мы имеем аналогичную концепцию именованных служб и RPC прокси, но они слегка отличаются от того, что мы видели с Java Remote Method Invocation (RMI).

    И последнее, и наиболее важное, EJB могут управляться. Это означает, что когда объект EJB активен в памяти, ведущий его процесс делает с ним немого больше, чем то, что обычно делает JVM. Процесс, обслуживающий EJB, вызывается EJB контейнером и является стандартизированной средой времени выполнения, которая предоставляет управляющую функциональность. Так как спецификация EJB стандартизирует службы, предоставляемые EJB контейнером, мы имеем поставщика, реализующего эту функциональность в коммерческих продуктах, таких как WebSphere и WebLogic, два наиболее известных коммерческих EJB контейнера от IBM и BEA Systems, соответственно. В EJB контейнере службы, такие как удаленные объекты и нейтралитет клиентского протокола, неявно используют JAVA RMI; другие службы (перечисленные ниже) являются стандартными. Такие службы, как кластеризация и поддержка при поломках, являются дополнительными и предлагаются определенными поставщиками.

    Вот службы, предоставляемые всеми контейнерами EJB:

    • Постоянство Объекта
    • Декларативный Контроль Безопасности
    • Декларативный Контроль Транзакций
    • Управление Конкурированием (Параллельностью)
    • Управление Масштабированием

    Постоянство объекта означает, что вы можете отразить состояние EJB экземпляра на данные в некотором постоянном хранилище — обычно, но не обязательно, в реляционной базе данных. Контейнер хранит состояние в постоянном хранилище, синхронизированным с состоянием EJB экземпляра, даже когда этот объект используется и модифицируется параллельно несколькими клиентами.

    Декларативный Контроль Безопасности дает вам возможность позволить Контейнеру проверять, что клиент, вызывающий определенный метод для некоторого EJB, был авторизован и принадлежит к роли, которую вы ожидаете; если это не так, будет сгенерировано исключение и метод не будет выполнен. Выгода от декларативного контроля безопасности в том, что вам не нужен код для любой логики безопасности в вашем EJB — вы просто определяете роли и «говорите» контейнеру каким ролям позволяется вызывать какие методы. Так как нет жестко закодированной логики безопасности в вашем EJB, легко произвести изменения в соответствии с требованиями безопасности без перекомпиляции.

    Декларативный Контроль Транзакций это аналогичный механизм: вы говорите Контейнеру, чтобы он сделал что-то для вашей пользы, но вы предоставляете информацию о том, что должно происходить в терминах разграничения транзакций, когда вызывается определенный метод. Например, вы можете проинструктировать Контейнер начать новую транзакцию, когда вызывается метод, или вы можете проинструктировать его использовать существующую транзакцию и отклонить вызов метода, если один из методов еще не активен (транзакция проходит по цепочке вызовов методов). Контейнер будет автоматически подтверждать транзакцию, когда метод, вызвавший запуск транзакции, завершиться корректно, или он откатит транзакцию, если будет перехвачено исключение, выброшенное в то время, когда транзакция была активна. Опять таки, польза от декларативного управления транзакциями в том, что нет логики транзакций в исходном коде вашего EJB, так что это не только упрощает жизнь разработчику, но это также облегчает изменение поведения транзакции вашего EJB без перекомпиляции.

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

    Управление конкурированием синхронизирует параллельные вызов методов, приходящие от различных удаленных клиентов, и направляет их к одному и тому же EJB объекту. На практике, это гарантирует, что компонент может быть безопасно использован в наследуемой многопоточной среде сервера приложений (довольно полезное свойство).

    Управлению масштабируемостью адресуется проблема увеличения выделяемых ресурсов, которая возникает при увеличении количества одновременно работающих клиентов. Ресурсы выделяются для клиента, а не для EJB объектов, есть такие поддерживаемые ресурсы, как соединения с базой данных, нити (threads), сокеты, очередь сообщений и так далее. Например, если число одновременно работающих клиентов увеличивается от 100 до 1000, вы можете получить 1000 EJB объектов в памяти, которые могут открыть 1000 соединения с базой данных; это может быть слишком много для количества имеющейся у вас памяти, и это определенно слишком тяжелая нагрузка на ваш сервер базы данных. EJB Контейнер может решить заняться этой проблемой, предоставив объединенные (pooling) экземпляры EJB и объединенные (pooling) соединения с базой данных. Таким образом, Контейнер будет хранить только ограниченное количество экземпляров или соединений, живущих в памяти, и будет присваивать им различных клиентов только во время, когда клиент действительно будет нуждаться в этом.

    Красота Enterprise JavaBean

    Мы знаем, что EJB суть компоненты. Однако, термин «компонент» один из наиболее перегруженных в индустрии, и разные люди (и разные поставщики) имеют разные идеи о том, что такое компонент. Для платформы .NET от Microsoft компонент является чем-то, что компилируется с (D)COM(+) бинарной спецификацией, а природа и тип компонента определяется стандартными интерфейсами, которые он предоставляет. Однако, нет архитектурной модели, предписанной платформой Microsoft, или это преднамеренно не оговорено. Другими словами, существует очень мало предписанных архитектурных требований, которые говорят, как разрабатывать и собирать компоненты для создания промышленного приложения.

    Платформа J2EE, с другой стороны, предоставляет вам более точную архитектуру рабочей среды, в которой вы создаете и используете компоненты. Это особенно верно для EJB, для которых спецификация определяет три разных EJB типа, с разными характеристиками отклика и разными областями применения. Вот эти три EJB типа:

    1. Entity Beans
    2. Session Beans
    3. Message-Driven Beans

    Entity beans представляет объекты в том смысле, что Контейнер прозрачно хранит их состояния синхронно с данными в некотором другом постоянном хранилище, обычно это реляционная база данных. По этой причине entity beans используются для представления бизнес-сущностей, таких как клиент, корзина покупок и так далее. Entity beans не реализуют бизнес-логику, за исключением самосодержащейся логики напрямую связанной с внутренними данными. Хотя все entity beans открыты для клиентского кода одинаковым образом, они могут быть реализованы при использовании двух разных живучих стратегий. Разработчик имеет два варианты: 1) позволить Контейнеру заботиться о перемещении состояний между EJB объектом и базой данных, или 2) позаботиться об этом механизме и реализовать код, который перемещает состояние EJB от и к постоянному хранилищу. В первом случае мы говорим, что мы применяем Живучесть Управляемая Контейнером [Container Managed Persistence] (CMP), так что entity beans является CMP bean; во втором случае мы используем Живучесть Управляемая Компонентом [Bean Managed Persistence] (BMP), и мы получаем BMP bean. Опять таки, хотя есть существенное практическое отличие между CMP и BMP в стратегиях реализации, это не влияет на способ использования клиентом entity bean.

    Обсуждения всех хитросплетений при предпочтении CMP перед BMP выходит за пределы этой главы. Однако важно объяснить, что большинство полезных свойств CMP не в том, как вы можете подумать, что вам не нужен код логики самосохранения. Реальная выгода от CMP состоит в том, что компонент может портироваться под любую систему постоянного хранения. Если вы думали об этом, CMP работает потому, что Контейнер генерирует весь код, необходимый для переноса состояния между EJB объектом и постоянным хранилищем. Это означает, что Контейнер знает, как взаимодействовать с таким специальным хранилищем. Реализация EJB Контейнера поддерживает различные хранилища, обычно все основные RDBMS, такие как Oracle, DB2, MySQL и т.п. Так как логика сохранения не закодирована жестко в CMP entity bean, а она предоставляется Контейнером, вы можете использовать тот же самый бинарный компонент в разных рабочих средах и все еще пользоваться поддержкой хранения объекта, предоставляемой Контейнером. Если вы используете BMP, логика хранения (например SQL выражения, если вы программируете для специфической RDBMS) будет встроена в ваш компонент, что затруднит использования другого хранилища.

    Таким образом, вы используете CMP для того, чтобы повысить уровень портируемости; и вы используете BMP, если ваш entity bean связан с некоторой системой, не поддерживаемой платформой J2EE (например, это CICS приложение в IBM майнфрейме) или по другим особым причинам, которые мы не может предположить здесь.

    Session beans по сути это другой род. Они не постоянны, и вместо дискретных компонентов, которые реализуют бизнес-логику, реализуют, например, шаги, требуемые для проверки и покупки некоторых продуктов в виртуальную корзину. Session beans могут взаимодействовать с другими session beans, чтобы получить доступ к дополнительной бизнес-логике, и они используют entity beans, когда им нужно получить доступ к хранимой информации. Session beans получаются в двух разных вариантах: session beans не имеющий состояний (Stateless session beans — SLSB) и session beans поддерживающие состояния (Stateful session beans — SFSB).

    Различия между этими двумя типами в том, что session beans с поддержкой состояний хранит диалоговую информация во время обмена с клиентом, в то время, как session beans без поддержки состояний этого не делает. Диалоговая информация о состоянии — это информация, которая передается от клиента к session beans при последовательном вызове методов.

    Например, вам нужен session beans, реализующий логику резервирования и покупки билетов в кино по сети. Клиенты должны подключиться и использовать один из экземпляров такого компонента; экземпляр должен предоставить два метода, reserveSeats(& ) и purchase(& ). Первый метод принимает количество мест, которые нужны покупателю для покупки, и резервирует их в системе; второй метод принимает информацию о кредитной карте покупателя, проверяет сумму на ней и выполняет процесс покупки. Если Компонент является session beans с поддержкой состояний, то когда будет вызван метод reserveSeats(& ), экземпляр session bean «запомнит» количество мест, и вам не нужно будет опять передавать эту информацию в метода purchase(& ). Если session beans не поддерживает состояния, Компонент не будет хранить в памяти информацию, переданную в предыдущем вызове метода, так что клиенту нужно будет передавать ее при каждом вызове метода. Существует несколько стратегий для реализации и оптимизации передачи состояний, но состояния все равно должно передаваться при каждом вызове метода.

    Причина существования этих двух типов session beans достаточно проста: неотъемлемой частью некоторых бизнес-процессов является отсутствие состояний (особенно это относится к тем процессам, которые вплотную привязаны к HTTP), а для других процессов неотъемлемо наличие состояний; архитектура призвана отразить это различие.

    Есть технический подтекст в использовании одного типа session bean или другого, но так же, как и в случае сравнения CMP и BMP, детальное обсуждение выходит за рамки этой главы. Одна вещь, которую вы должны запомнить, состоит в том, что session bean без состояний ведут себя лучше при работе с экземпляром механизма пулинга (объединения) Контейнера, по сравнения с session bean с поддержкой состояний. Так как экземпляр session bean без поддержки состояний не предназначен для запоминания любой информации о состоянии, передаваемой клиентом, при завершении работы метода, этот же самый экземпляр может легко быть переназначен другому клиенту для вызовов других методов. Если экземпляр session bean поддерживает состояния, Контейнер не может переназначить его другому клиенту до тех пор, пока он не переместит информацию о состояниях в какое-то временное хранилище для последующего использования (в EJB этот процесс называется активация и пассивация). В этом заключено общее непонимание того, что при объединении (pooling) экземпляров, session beans без состояний улучшают масштабируемость, но это не при любых условиях. Ваш Контейнер должен быть очень хорошо оптимизирован относительно механизма активизации и пассивации, так чтобы перемещение состояния session bean с поддержкой состояний в некоторое вторичное хранилище и извлечение из него оказывало очень малое воздействие на производительность. Во-вторых, если вы используете session bean без поддержки состояний, вы должны передать состояние клиента в session bean при каждом вызове, и если клиент и session bean находятся на двух различных распределенных уровнях, то процесс постоянной передачи состояния, а также сериализация и десериализация параметров метода, как этого требует RMI, может стать критическим местом.

    Интересно замечание относительно объединения (pools) экземпляров. Дело в том, что ваш Контейнер может их вообще не понимать. Если принять во внимание недавнюю оптимизацию, введенную в компиляторы Java и JVM, современные Контейнеры могут решить, что выделение блока памяти и сборка мусора более эффективное занятие, чем управление объединением.

    И наконец, третий тип Enterprise JavaBean: Message-Driven Beans (MDB). MDB работает в кооперации с системой сообщений JAVA [Java Messaging System](JMS), которая является абстрактным API для системы Message-Oriented Middleware (MOM), более-менее похожую на то, как JDBC является абстрактным API поверх SQL базы данных.

    Еще раз, у нас нет места в этой главе для полного описания системы MOM. Коротко, система MOM предоставляет модель сообщений с публичной подпиской, основанной на асинхронной, распределенной очереди сообщений. MOM сообщение суть пакет информации, которое кто-то публикует, а кто-то другой интересуется им и принимает его. MOM сообщения публикуются в и извлекаются из очереди или канала. Источник сообщения и подписчик могут находиться в двух разных процессах, а сообщение посылается в MOM очередь, которая гарантирует, что оно будет доставлено, даже в случае сбоя системы. MOM система чрезвычайно полезна для реализации любой формы распределенного, асинхронного процесса — она чем-то похожа на обработку событий в самостоятельном приложении.

    MBD являются приемниками MOM сообщений, приходящих через JMS API. MDB обычно реализуются для выполнения некоторых действий при получении сообщений и выступают в роли объектно-ориентированных точек соединения между подсистемами, взаимодействующих посредством JMS. Например, MDB может реализовать посылку электронной почты администратору (используя JavaMail API), когда будет получено сообщение об определенном событии.

    Отличие MDB от session beans и entity beans состоит в том, что они не предоставляют никаких удаленных или локальных представлений. Другими словами, клиентский код не может получить доступ к MDB, но MDB может использовать другие EJB и другие службы.

    EJB Роли

    Тот факт, что EJB является компонентом архитектуры, неявно означает, что есть различные моменты во времени жизни EJB компонента: разработка, установка, конфигурация и использование. EJB спецификация доходит вплоть до определения организации работы и вовлекаются различные роли во время жизни EJB компонента. Спецификация различает следующие роли:

    • Enterprise Bean Provider. Реализует EJB компонент и оформляет его для распространения. Знает о прикладной области, но может не знать об операционной среде, в которой компонент будет использоваться.
    • Deployer. Для EJB, развертывание состоит в процессе установки одного или нескольких EJB компонентов в специфическом EJB Контейнере. Deployer является экспертом в специфической рабочей среде и отвечает за связывание EJB со всеми ресурсами, которые ему нужны для работы (соединение с базой данных, таблицы, другие EJB и тому подобное).
    • Application Assembler. Использует различные развернутые компоненты в специфической среде для создания полного приложения.
    • System Administrator. Он отвечает за создание и поддержку пользователей, баз данных и за общую инфраструктуру ресурсов, необходимых для специфической рабочей среды.

    Важно помнить, что это роли, а не персоны. Для небольшого проекта одна и та же персона может выступать в любой или во всех ролях сразу. Для больших проектов, когда компоненты могут разрабатываться другими отделениями или приходить от третьих сторон, разные персоны могут выступать в разных ролях.

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

    Аналогично, EJB спецификация принуждает, чтобы отдельный кусок не стандартной конфигурационной информации был определен в отдельном исходном файле. Это необходимо потому, что различные реализации EJB Контейнера могут предоставлять различные возможности, которые обычно конфигурируются человеком посредством XML файлов. Если определенная особенность специфична для одного Контейнера, она будет определена в файлах, специфичных для Контейнера.

    Основное API

    EJB API — это не только платформа Java2 Enterprise Edition. Это горадно больше: Java Transaction API (JTA), Java Messaging System API (JMS), Сервлеты, JavaServer Pages (JSP) и Java Database Connectivity (JDBC) вот несколько названий. Прежде чем мы приступим к рассмотрению реализации и использованию EJB, нам необходимо коротко рассмотреть пару фундаментальных API: Java Naming and Directory Interface (JNDI) и актуальный EJB API.

    JNDI

    JNDI — это абстрактный API над различными службами именования, такими как RMI Naming Service или COS Naming Service в CORBA, или служба директорий аналогичная Lightweight Directory Access Protocol (LDAP). Служба имен и служба каталогов концептуально это не одно и то же, но, как я уже говорил ранее, здесь не место для обсуждения различий. То, что мы сделаем — это просто взглянем на то, что в них общего.

    Все службы именования и директорий позволяют вам заполнять и искать репозитории некоторого рода. Более подробно, они позволяют вам ассоциировать (или связывает, если хотите) имя с объектом, а затем искать этот объект (или рассматривать его), используя это имя. По аналогии, он может использоваться как директорий белых страниц, когда физический телефон, на который вы хотите перевести звонки (уникально идентифицируемый номером), ассоциируется с именем клиента. В качестве более приближенного к Java примера можно привести RMI: вы можете зарегистрировать объект с именем (строка) в RMI Naming Service, так что другие приложения могут производить поиск этого имени в службе. Это в точности совпадает с тем, что вы можете делать с COS Naming Service. Таким образом, почему JNDI является достаточно удобной абстракцией: он предоставляет вам однообразный интерфейс к различным службам именования и директорий, точно так же, как вы используете JDBC для доступа к различным базам данных.

    Как и JDBC, JNDI является абстрактным API, и вы можете найти в JNDI пакете javax.naming только Java интерфейсы. Действительная реализация этих интерфейсов должна быть предоставлена некоторым поставщиком, который хочет предоставить поддержку JNDI для своей службы. Для действительного использования этой службы вам необходим Поставщик Услуг (Service Provider) JNDI — точно так же, как вам необходим соответствующий JDBC драйвер для доступа к определенной базе данных.

    JDK1.4 поставляется с четырьмя стандартными Поставщиками Служб JNDI, которые предоставляют доступ к RMI, DNS, COS Naming Service и LDAP. Существует также дополнительный Поставщик Службы JNDI, который вы можете получить с вебсайта Sun, который предоставляет JNDI просмотр вашей локальной файловой системы (так что вы можете искать файлы и директории, используя JNDI).

    JNDI поддерживает концепцию пространства имен, которое является логическим пространством, в котором могут определяться имена. Два одинаковых имени, определенных в разных пространствах имен не будут являться причиной неоднозначности или коллизии имен, а пространства имен могут быть вложенные. Эту концепцию вы можете видеть в нескольких различных ситуациях — ваша локальная файловая система использует вложенное пространство имен (директории), DNS Интернета использует вложенное пространство имен (домены и субдомены).

    В JNDI пространство имен представлено интерфейсом Context, наиболее часто используемым элементом API. Существуют разные классы, реализующие интерфейс Context, в зависимости от реальной службы, к которой вы обращаетесь посредством JNDI. Интерфейс Context имеет методы для связывания имени и объекта, для удаления связывания, для переименования, для создания и удаления подконтекста, для поиска имен, для получения списка имен и так далее. Вы также должны заметить что, так как контекст в Java является объектом, он может быть зарегистрирован в другом контексте под своим собственным именем. Другими словами, мы можем получить вложенный субконтекст, начав с родительского контекста, и создав связывание между именем субконтекста и вложенным объектом Context.

    Другая фундаментальная концепция в JNDI API состоит в том, что независимо от реально используемой вами службы, для того, чтобы получить возможность создать связывание, выполнить поиск и тому подобное, вам нужно начать с некоторого «корневого» контекста. Класс InitialContext — это то, что вы используете, чтобы получить экземпляр Контекста, которые представляет родителя всех субконтекстов, к которым вы можете получить доступ.

    Хотя определенные операции в JNDI применимы только к определенному Поставщику Службы, концепция распространяется так далеко, что становится общей и широко применимой. Рассмотрим пример. Приведенный ниже код показывает, как получить список всех имен, зарегистрированных в корне используемой вами службы. Обратите внимание, что конструктор InitialContext получает в качестве аргумента тип Property. Я объясню это немного позже. Сейчас же просто взглянем на приведенный ниже код.

    Context context = new InitialContext ( props ) ;
    Enumeration names = context.list ( «» ) ;
    while ( names.hasMoreElements ())
    System.out.println ( names.nextElement ()) ;

    Вы видите, что это достаточно просто. Мы создаем объект Context, который представляет корень вашей службы именования или директории, получаете перечисление всех элементов этого контекста (пустая строка в вызове метода list( ) означает, что вы не ищете определенное имя), затем перебираете и печатаете все элементы из перечисления.

    Абстракция и модель программирования достаточно проста. Нас должен волновать вопрос: какую службу именования или директорий мы используем? Ответ содержится в контейнере параметров, который мы передаем в конструктор InitialContext( ). JNDI API полностью определяет несколько стандартных имен параметров (декларированных, как константные Строки и интерфейсе Context), а значения этих параметров определяют природу службы, которую вы будете использовать. Из этих параметров два являются фундаментальными: INITIAL_CONTEXT_FACTORY и PROVIDER_URL. Первая из них указывает класс, который будет производить экземпляр JNDI Context. Если вам нужен DNS, например, вы будете указывать класс, который производит Context, способный взаимодействовать с DNS сервером. Второй параметр — это расположение службы, следовательно, это URL. Формат этого URL зависит от определенной службы.

    Ниже приведен законченный пример, который использует JNDI для просмотра содержимого корня контекста в DNS сервере (возможно, вам понадобиться использовать другие IP адреса для вашего DNS, в зависимости от конфигурации вашей сети).

    //: c18:jndi:SimpleJNDI.java
    import javax.naming.*;

    public class SimpleJNDI <
    public static void main ( String [] args ) throws Exception <
    Properties props = new Properties () ;
    props.put ( Context.INITIAL_CONTEXT_FACTORY,
    «com.sun.jndi.dns.DnsContextFactory» ) ;
    props.put ( Context.PROVIDER_URL, «dns://207.155.183.72» ) ;
    Context context = new InitialContext ( props ) ;
    Enumeration names = context.list ( «» ) ;
    while ( names.hasMoreElements ())
    System.out.println ( names.nextElement ()) ;
    >
    >
    // /:~

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

    Существует другой способ предоставления значений параметров JNDI. Вы можете использовать внешний файл параметров, который является текстовым файлом, который ассоциирует названия параметров с их значениями. Этот файл должен называться jndi.properties, и он должен быть расположен в CLASSPATH вашего JNDI приложения. Отличие состоит в том, что вы не можете использовать константы названия параметров, определенных в интерфейсе Context, а вместо этого вы должны использовать их «строковые» значения (которые вы можете найти в стандартной документации по JDK). Содержимое нашего файла jndi.properties будет следующим:

    java.naming.factory.initial=com.sun.jndi.dns.DnsContextFactory
    java.naming.provider.url=dns: //207.155.183.72

    Если вы выберете этот подход, вам больше ненужно будет передавать контейнер параметров в конструктор InitialContext. Другими словами, то, что вы поместите в файл jndi.properties, то вы и установите для JNDI в качестве значений по умолчанию.

    Последнее, и наиболее важное, что я хотел бы рассказать о JNDI, это как ищутся имена. Как я упоминал, существует метод lookup( ) в интерфейсе Context. В следующем примере мы предполагаем, что существует внешний файл jndi.properties, в котором определено, какую службу мы просматриваем.

    //: c18:jndi:SimpleJNDI2.java
    import javax.naming.*;

    public class SimpleJNDI2 <
    public static void main ( String [] args ) throws Exception <
    System.out.println ( new InitialContext () .lookup ( args [ 0 ])) ;
    >
    >
    // /:~

    Мы создали новый InitialContext и вызвали метод lookup( ) для него, передав Строку в качестве аргумента имени. Так как метод lookup( ) предназначен для работы с различными службами, он возвращает общий класс Object. Однако, реальный тип времени выполнения, возвращаемый из lookup( ) определяется специфичной службой, которую вы используете. Ради этого примера мы игнорируем тип и просто печатаем результат.

    В программировании EJB мы используем JNDI для нахождения всех видов ресурсов, включая EJB, пул соединений с базой данных, информацию об окружении и многое другое. Другими словами, из окна EJB Контейнера вы можете взглянуть на остальной мир посредством JNDI. Клиентское приложение тоже использует JNDI для получения соединения с фабрикой EJB (подробнее об этом позже).

    Таким образом, если мы используем JNDI с EJB, что такое Поставщик Услуг JNDI? Ответ состоит в том, что EJB Контейнер работает и выставляет свою собственную службу JNDI, специализированную для работы с ресурсами, управляемыми Контейнером. Другими словами, эта служба EJB JNDI предназначена, чтобы позволить клиентам и Enterprise JavaBean’ам находить ресурсы по JNDI именам. Помните, когда вы запускаете ваш EJB Контейнер, вы также неявно запускаете EJB JNDI службу, которая доступна вам через стандартный JNDI API.

    EJB

    Другой API, на который нам нужно взглянуть, это сам EJB API. Он определен в пакете javax.ejb, который, что само по себе интересно, состоит только из интерфейсов и нескольких классов исключений.

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

    Другой набор элементов, который нужен нам для короткого обзора, это классы исключений. Вы можете видеть на приведенной ниже диаграмме наиболее часто встречающиеся исключения (их существует немного больше в пакете, пожалуйста, обратитесь к документации по JDK за более детальной информацией).

    Как вы можете видеть, есть две главных категории: исключения, наследуемые от java.lang.Exception напрямую, и другие исключения, наследуемые от java.lang.RuntimeException. Или, в терминах Java, проверяемые и не проверяемые исключения, соответственно.

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

    Фактически, использование проверяемых исключений EJB указано в определениях EJB интерфейсов, используемых клиентом, что предопределяется спецификацией. Например, CreateException должно быть указано в спецификации метода, который клиент использует для создания экземпляра EJB. Аналогично, FinderException должно быть указано в спецификации метода, который клиент использует для нахождения существующего, постоянного объекта EJB. А RemoveException должно указываться в спецификации метода, который удаляет EJB объект.

    Не проверяемые исключения используются во внутренней реализации EJB. Другими словами, если какая-то попытка вашего EJB провалилась, вы выбрасываете EJBException или какое-либо наследуемое от него исключение. Причина, по которой EJBException не проверяется состоит в том, что в большинстве случаев они представляют некоторые ошибки, которые приходят из одной подсистемы в другую внутри EJB Контейнера. Даже если вы попытаетесь перехватить эти исключения, вы не сможете сделать это, так как EJB существует только внутри EJB Контейнера, который управляет экземплярами и заботится обо всем, что с ними происходит. Вот пример: у вас есть реализация EJB, который обращается к базе данных и в определенный момент база данных генерирует SQLException. Бессмысленно возвращать это исключение удаленному клиенту, поскольку клиент не может знать, что делать с ним. С другой стороны, вы можете захотеть прервать текущую операцию и известить Контейнер, что что-то пошло не так, таким образом должна существовать какое-либо корректное действие, подобное откату текущей транзакции.

    Вы не можете просто позволить SQLException’у остаться в Контейнере, поскольку Контейнер имеет общий способ получения исключений уровня системы — так что он может постоянно принимать исключения от различных, даже будущих, подсистем. Что вы должны сделать — это обернуть проверяемое SQLException в не проверяемое EJBException, используя конструктор, который принимает java.lang.Exception, а затем вы выбросите EJBException.

    Все исключения, которые поступают наружу из вашего EJB объекта, перехватываются Контейнером. Действия, предпринимаемые в этом случае Контейнером, могут сильно зависеть от условий, но в общем Контейнер автоматически откатит текущую транзакцию (если она есть) и передаст исключение клиенту. Если исключение, сгенерированое внутри вашего EJB, было типа EJBException, клиент получит общее RMI RemoteException, что проинформирует его, что метод окончился неудачей. Если исключение, сгенерированое в EJB, было исключением уровня приложения, таким как FinderException, Контейнер передаст это исключение клиенту.

    EJB Контейнер изнутри

    Вы знаете, что EJB — это управляемые компоненты, и теперь пришло время более пристально взглянуть на то, как контейнер управляет вашими EJB объектами. Это позволит нам лучше понимать службы, которые предоставляет Контейнер, примеры кода и процесс разработки.

    Основная идея достаточно проста. Когда бы клиентское приложение ни запросило ссылку на EJB объект, Контейнер реально возвращает ссылку на сгенерированный Контейнером прокси, который принимает все клиентские запросы, выполняет некоторые вспомогательные действия и, в конечном итоге, делегирует их объекту, реализованному BeanProvider’ом. Мы вызываем формирователь EJB Объекта, а затем EJB Экземпляра. Пожалуйста, не путайте их: EJB Объект генерируется Контейнером, в то время как EJB экземпляр реализуется Bean Provider’ом.

    Вы также должны заметить, что наличие и роли EJB Объекта являются стандартом EJB спецификации. Также обратите внимание на то, что стратегия реализации и способ взаимодействия с экземпляром EJB специфичны для контейнера.

    И наконец, сгенерированный контейнером прокси (EJB Объект) не нужно путать с RMI прокси, который также представлен. Приведенная ниже диаграмма разъясняет эту архитектуру.

    Диаграмма показывает архитектурное позиционирование EJB объекта относительно других главных составных частей. На диаграмме я показал случай удаленного клиентского приложения, но вы должны держать в уме, что всегда существует перехватывающий EJB объект, даже когда вызов метода приходит из кода в этом же контейнере (например, другого EJB).

    RMI прокси и заглушка (stub), с одной стороны, присутствуют только когда клиент обращается к удаленному представлению EJB компонента. Пожалуйста, обратите внимание, что RMI прокси и заглушка автоматически герерируются Контейнером, хотя различные Контейнеры выполняют это действие, применяя разные стратегии.

    Пунктирная стрелка представляет зависимости, и она означает, что если, например, реализация Экземпляра EJB изменится, EJB Объект также должен быть изменен, поскольку он полагается на специфическую реализацию метода для этого Экземпляра EJB.

    Тот факт, что Экземпляр EJB никогда не доступен напрямую, является основным механизмом, которым Контейнер управляет экземплярами вашего компонента. На языке EJB мы говорим, что Контейнер вставляет код между клиентом (если он присутствует) и EJB Экземпляром, а мы вызываем EJB Объект — артефакт Контейнера. Вставленный Контейнером код представляет EJB экземпляр с промежуточными службами, такими как объединение экземпляров, управление распараллеливанием, управления транзакциями и постоянством объектов.

    Таким образом, с ваших плеч снимается огромная ноша — представьте себе сколько кода вам было бы необходимо написать для реализации некоторых или всех этих служб самостоятельно. Но так как у вас есть EJB Контейнер, вы можете просто сказать ему, что ему нужно делать для вашей пользы — например, запустить новую транзакцию, когда вызывается определенный метод.

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

    Теперь вы знаете весь архитектурный фон, который вам нужен для начала рассмотрения того, как мы реализуем и используем Enterprise JavaBean. Я дам вам более подробную информацию об EJB Объектах и внутренних способах работы Контейнера тогда, когда мы будем рассматривать примеры кода.

    Программное обеспечение

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

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

    Все EJB примеры из этой главы были проверены на сервере приложений JBoss 3.0.3 (JBoss — это лидирующий EJB Контейнер с открытыми исходниками, и он может быть загружен с http://www.jboss.org). Хотя я использовал JBoss, и более специфичную версию, которая поставляется со встроенным Контенером Tomcat Сервера, количество информации, специфичной для JBoss, в этой главе и в примерах сведено к минимуму, так что перенесение этих примеров на другие Контейнеры должно потребовать очень небольшие изменения кода или вообще не потребуют их. Однако, пожалуйста, обратите внимание, что если вы используете отличную от JBoss версию, вам может понадобиться изменить соответствующие скрипты построения Ant.

    Другие программные продукты, которые вам понадобятся: Java SDK1.4 или более позднюю версию; Ant и JUnit (которые вероятно уже установлены у вас, если вы запускали другие примеры из этой книги); и XDoclet, другая утилита с открытыми исходниками, которая поможет нам в разработке ваших EJB.

    Инструкция по установке, которую вы найдете в директории кода c18, предоставит вам все дополнительные детали.

    Пример приложения

    Все примеры этой главы следуют общей теме, иллюстрирующей концепцию. Мы построим простое приложение, называющуюся JavaTheater, для продажи и покупки билетов в кино. Приложение будет строится инкрементально, пример за примером, и мы будет иметь дело с адресами кинотеатров, шоу, билетами, покупателями, кредитными картами и так далее. У меня нет места, чтобы показать вам, как создавать полновесное законченное приложение, так что я сфокусируюсь на ядре функциональности.

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

    Прежде чем посмотрим на реальный код, я хочу упомянуть некоторые вещи относительно структуры исходных текстов. Все примеры находятся в директории c18 исходного кода для этой книги. Каждый поддиректорий является отдельным, самодостаточным примером, который собирается в корне поддиректория. На верхнем уровне директории каждого примера есть Ant скрипт для сборки. На этом же уровне есть директорий src, который содержит все файлы исходного кода для этого примера. Директорий src содержит поддиректории ejb-tier и rmiclient, в которых содержится код для реализации EJB и для клиентского приложения (включая JUnit тест), соответственно. Также, обратите внимание, когда вы будете собирать пример, что Ant скрипт будет создавать рабочий директорий для компиляции и сборки (смотрите комментарии в скрипте Ant более подробно), но ничего не создает и не изменяет в src директории. И, наконец, все наши классы определены в пакете javatheater, содержащем вложенные пакеты, которые тоже стоит упомянуть: javatheater.client содержит клиентское приложение; javatheater.test содержит классы JUnit тест; javatheater.ejb содержит наши EJB интерфейсы и связанные классы, а javatheater.ejb.implementation содержит классы реализации EJB.

    Наш первый пример, example01, не использует никакую EJB функциональность. Это обычное и простое Java приложение, которое мы использует в качестве прототипа фундаментального класса. Наипростейший класс — это Movie, и вы можете видеть его реализацию ниже:

    //: c18:example01:src:javatheater:Movie.java
    //
    package javatheater;

    public class Movie <
    int id;
    String title;

    public Movie ( int id, String title ) <
    this .id = id;
    this .title = title;
    >

    public int getId () <
    return id;
    >

    public void setId ( int id ) <
    this .id = id;
    >

    public String getTitle () <
    return title;
    >

    public void setTitle ( String title ) <
    this .title = title;
    >

    public String toString () <
    return «[» + id + «] » + title;
    >
    >
    // /:~

    Здесь определен класс Movie, который содержит два поля: id (типа int) и title (типа String). Класс также определяет конструктор плюс сеттеры и геттеры (setters и getters) для доступа к значениям полей. Конечно, более реалистичный объект фильма должен содержать много больше информации, такой как актеры, директор, рейтинг, краткий обзор; но реализация всего этого добавит ненужную сложность в пример.

    Другой класс — это Show, который также достаточно прост: у него есть id (типа int), время начала показа time (строка) и количество мест (типа int). Он также содержит ссылку на объект Movie, что означает, что у нас есть однонаправленное отношение многие к одному между показами и фильмами: фильм может быть связан с многими показами, но на одном показе может быть только один фильм. Вот реализация класса Show:

    //: c18:example01:src:javatheater:Show.java
    //
    package javatheater;

    public class Show <
    int id;
    Movie movie;
    String showtime;
    int availableSeats;

    public Show ( int id, Movie movie, String showtime, int availableSeats ) <
    this .id = id;
    this .movie = movie;
    this .showtime = showtime;
    this .availableSeats = availableSeats;
    >

    public int getId () <
    return id;
    >

    public void setId ( int id ) <
    this .id = id;
    >

    public Movie getMovie () <
    return movie;
    >

    public void setMovie ( Movie movie ) <
    this .movie = movie;
    >

    public String getShowtime () <
    return showtime;
    >

    public void setShowtime ( String showtime ) <
    this .showtime = showtime;
    >

    public int getAvailableSeats () <
    return availableSeats;
    >

    public void setAvailableSeats ( int availableSeats ) <
    this .availableSeats = availableSeats;
    >

    public String toString () <
    return «[» + id + «] » + movie + «, » + showtime + «, »
    + availableSeats;
    >
    >
    // /:~

    Так как example01 это простейшее Java приложение, оно не употребляет многие службы, предоставляемые архитектурой EJB. Определенно, мы не имеет поддержки постоянства. Я не использовал JDBC для хранения и получения объектов из базы данных (что должно означать отображения объект/отношения, нечто более сложное для нашего первого примера), также в примере есть класс, называемый Storage, который эмулирует наличие постоянного хранилища данных.

    //: c18:example01:src:javatheater:Storage.java
    //
    package javatheater;

    public class Storage <
    private static Storage ourInstance;
    private Movie [] movies;
    private Show [] shows;

    public synchronized static Storage getInstance () <
    if ( ourInstance == null ) <
    ourInstance = new Storage () ;
    >
    return ourInstance;
    >

    private Storage () <
    movies = new Movie [] < new Movie ( 1 , "Return of the JNDI" ) ,
    new Movie ( 2 , «A Bug’s Life» ) , new Movie ( 3 , «Fatal Exception» ) ,
    new Movie ( 4 , «Silence of the LANs» ) ,
    new Movie ( 5 , «Object of my Affection» ) > ;
    shows = new Show [] < new Show ( 1 , movies [ 0 ] , "5:30pm" , 100 ) ,
    new Show ( 2 , movies [ 0 ] , «7:00pm» , 300 ) ,
    new Show ( 3 , movies [ 1 ] , «6:00pm» , 200 ) ,
    new Show ( 4 , movies [ 4 ] , «9:00pm» , 200 ) > ;
    >

    public synchronized Movie findMovieById ( int id ) <
    for ( int i = 0 ; i < movies.length; i++ ) <
    Movie movie = movies [ i ] ;
    if ( movie.id == id )
    return movie;
    >
    return null ;
    >

    public synchronized Movie findMovieByTitle ( String title ) <
    for ( int i = 0 ; i < movies.length; i++ ) <
    Movie movie = movies [ i ] ;
    if ( movie.title == title )
    return movie;
    >
    return null ;
    >

    public synchronized Movie [] findAllMovies () <
    return movies;
    >

    public synchronized Show findShowById ( int id ) <
    for ( int i = 0 ; i < shows.length; i++ ) <
    Show show = shows [ i ] ;
    if ( show.id == id )
    return show;
    >
    return null ;
    >

    public synchronized Show findShowByMovie ( Movie movie ) <
    for ( int i = 0 ; i < shows.length; i++ ) <
    Show show = shows [ i ] ;
    if ( show.movie.id == movie.id )
    return show;
    >
    return null ;
    >

    public synchronized Show [] findAllShows () <
    return shows;
    >
    >
    // /:~

    Этот класс реализует дизайнерский шаблон Синглетона (при котором существует только одно хранилище данных в вашей системе). Его приватный конструктор заполняет фиктивную базу данных с фильмами по Java тематике и показами. Он также предоставляет публичный метод для получения фильмов и показов из фиктивной базы данных по разным критериям. В нем нет методов для обновления нашей фальшивой базы данных, но вы вольны добавить их в качестве упражнения, если чувствуете в этом необходимость.

    Последний класс в example01 — это наш главный клас, ShowListing, который просто использует хранилище для нахождения группы показов, и распечатывает их.

    //: c18:example01:src:javatheater:ShowListing.java
    //
    package javatheater;

    public class ShowListing <
    static Storage storage = Storage.getInstance () ;

    public static void main ( String [] args ) <
    Show [] shows = storage.findAllShows () ;
    for ( int i = 0 ; i < shows.length; i++ ) <
    Show show = shows [ i ] ;
    System.out.println ( show ) ;
    >
    >
    >
    // /:~

    Повторюсь, этот пример умышленно упрощен, но он вводит несколько фундаментальных концепций, которые мы применим при использовании EJB архитектуры: постоянство объектов, разделение сущностных классов он логики бизнес классов и связи между объектами. Плюс к этому, пример подал нам идею того, чем будет приложение, чтобы охватить все вопросы.

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

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