Язык Java позволяет создавать программы с помощью простого объединения уже проверенных и работающих компонентов. Это стало возможным благодаря тому, что даже в откомпилированной Java-программе содержатся имена классов, переменных и методов, которые другие компоненты могут прочитать уже во время исполнения программы. Данное свойство, называемое поздним связыванием объектов, позволяет писать на Java компоненты, способные подстыковываться к программе уже во время ее исполнения. Для этого компоненты должны быть построены по общим принципам компонентной модели. В прошлом номере JavaWorld я уже рассказал о компонентной модели JavaBeans, предложенной компанией JavaSoft для построения локальных приложений. В этой статье речь пойдет о компонентной модели для сетевых распределенных вычислений, получившей название Enterprise JavaBeans.
Разработка приложений для корпоративного использования - достаточно сложный процесс, поскольку такие системы необходимо делать гибкими и масштабируемыми. Обычно подобные приложения строятся по модульному принципу, позволяющему независимым разработчикам легко интегрировать в системы модули собственной разработки. Создатели спецификации Enterprise JavaBeans попытались стандартизировать не только интерфейсы, используемые для разработки вычислительных систем масштаба предприятия, но и роли, которые должны играть разработчики компонент для таких систем. Однако, основу спецификации составляет все-таки архитектура компонентной модели Enterprise JavaBeans.
В спецификации, опубликованной компанией Sun, выделяется три основных элемента компонентной модели Enterprise JavaBeans: собственно компоненты, реализующие определенные функции, контейнеры, предоставляющие компонентам сетевые сервисы (безопасности, контроля транзакций и аналогичные) и клиент, управляющий работой системы. Причем клиент и компонент взаимодействуют только с контейнером, но не напрямую друг с другом. Таким образом, модель Enterprise JavaBeans напоминает трехуровневую архитектуру клиент-сервер.
Взаимодействие "контейнер-компонент"
В спецификации Enterprise JavaBeans выделяются два типа компонентов:
Сеансовые компоненты реализуют логику отдельного сеанса связи между клиентом и вычислительной системой. Такие компоненты имеют следующие характерные черты: они работают лишь с одним клиентом; имеют небольшое время жизни; реализуют логику отдельной операции между клиентом и базой данных; уничтожаются при сбое сервера и не имеют представления в базе данных. Сущности же служат для представления данных в вычислительной системе. Они имеют такие особенности: представляют данные в базе данных; с ними можно выполнять транзакции; ориентированы на многопользовательский доступ; имеют длительный срок жизни; должны сохранять данные даже в случае выхода сервера из строя. В спецификации указано, что контейнеры, соответствующие стандарту Enterprise JavaBeans 1.0, обязаны поддерживать сеансовые компоненты, но могут не поддерживать сущностей. Во второй версии поддержка контейнером сущностей будет обязательной.
Сеансовые компоненты являются внутренними ресурсами системы, поэтому они не имеют собственной системы идентификации. Клиент не может обратиться к конкретному экземпляру сеансового компонента. Для каждого клиента создается свой экземпляр. По этой причине метод Container.getFinder для сеансового компонента всегда возвращает null.
Все сеансовые компоненты должны реализовывать интерфейс SessionBean, позволяющий контейнеру, а через него и клиенту, использовать функциональные возможности компонента. Метод setSessionContext позволят связать объекты Container и EJBObject с экземпляром сеансового компонента. Если контейнер должен уничтожить сеансовый компонент, то он вызывает метод ejbDestroy, но поскольку большинство сеансовых компонентов не связаны с постоянными данными, то этот метод компонента обычно остается пустым. Метод ejbPassivate переводит экземпляр компонента в пассивный режим, что может потребоваться, например, для экономии ресурсов системы. Чтобы вновь активировать экземпляр компонента, контейнер вызывает метод ejbActivate.
Сеансовые компоненты могут требовать синхронизации транзакций, что позволяет реализовать с их помощью откат транзакции. В данном случае они должны реализовать интерфейс SessionSynchronization. Если подобное происходит, то контейнер получает дополнительные возможности по его управлению. Например, в случае начала новой транзакции контейнер вызывает метод компонента, под именем beginTransaction. В этот момент компонент должен установить связь с необходимыми ему сущностями. Метод beforeCompletion вызывается контейнером перед завершением транзакции, когда изменение сущностей, связанное с сеансовым компонентом, должно быть записано в кэш. Завершение же транзакции выполняется методом afterCompletion, имеющим аргумент типа boolean. Если аргумент имеет значение true, то транзакция подтверждена, в противном случае должен быть реализован откат транзакции. Интерфейс SessionSynchronization не является обязательным при реализации сеансового компонента.
Контейнер, в свою очередь, должен реализовать интерфейс SessionContext, позволяющий сеансовому компоненту получить определенные сведения о контейнере и связанных с ним компонентах. Метод getContainer возвращает контейнер, который вызывает методы компонента. Метод getEJBObject возвращает ссылку на объект EJBObject, связанный с контейнером в данный момент и управляющий его работой. Список свойств, описыващих окружение компонента, получается с помощью вызова метода getEnvironment. Кроме того, можно получить информацию о клиенте, обращающемся к данному компоненту. Посредством getCallerIdentity узнается его идентификационный номер, а метод isCallerInRole позволяет проверить, выполняет ли клиент определенную роль (идентификатор роли передается методу в качестве аргумента).
Реализация описанных интерфейсов как со стороны сеансового компонента, так и со стороны контейнера, позволяет данным элементам компонентной модели Enterprise JavaBeans взаимодействовать между собой. Причем описанная модель взаимодействия учитывает тот факт, что сеансовые компоненты однопользовательские и имеют непродолжительный срок жизни, то есть не сохраняются в устройствах длительного хранения.
Сущности взаимодействуют с контейнером иначе. Сама сущность должна реализовать интерфейс EntityBean, который определяет вызываемые контейнером методы, аналогичные перечисленным в интерфейсе SessionBean. Однако, кроме этого интерфейс EntityBean включает методы, связанные с возможностью хранения сущностей длительное время. Это методы ejbStore и ejbLoad, обеспечивающие сохранение и восстановление компонентов соответственно. Кроме того, интерфейс предусматривает методы изменения контейнера, так как к одной и той же сущности могут обращаться разные контейнеры, что связано с возможностью многопользовательского доступа. Для установки связи между контейнером и сущностью используется метод setEntityContext, при вызове которого в качестве параметра передается объект, реализующий интерфейс связи с новым контейнером (EntityContext). Кроме того, контейнер может вызвать метод для разрыва связи - unsetEntityContext.
В свою очередь контейнер должен реализовать интерфейс EntityContext, позволяющий сущности получать некоторые сведения о нем и о представляемом им клиенте. Здесь есть все методы, входящие в интерфейс SessionContext, но добавляются и новые. Их присутствие связано с тем, что контейнер выдает каждому экземпляру сущности уникальный первичный ключ (primary key), позволяющий отличить один экземпляр от другого. С помощью первичного ключа контейнер и клиент могут установить, с каким экземпляром сущности им предстоит работать. Это связано с тем, что разные экземпляры сущности хранят разную информацию, и поэтому различны. Таким образом, в интерфейс EntityContext добавлены методы чтения и изменения первичного ключа (getPrimaryKey и setPrimaryKey соответственно), а также метод поиска в контейнере сущности, которая бы соответствовала указанному в параметре первичному ключу (метод getEJBObject(Object primaryKey)).
Описанный набор методов позволяет контейнеру взаимодействовать с сущностями. При этом учитывается связь сущности с несколькими контейнерами и длительное ее хранение. Обработка "многоконтейнерности" выполняется посредством методов setEntityContext и unsetEntityContext интерфейса EntityContext, а длительное хранение сущностей обрабатывается с помощью методов getPrimaryKey, setPrimaryKey и getEJBObject(Object primaryKey). Следует отметить, что оба типа компонентов должны преобразовываться в последовательную форму, то есть интерфейсы SessionBean и EntityBean расширяют интерфейс Serializable.
Взаимодействие "клиент-контейнер"
Для клиента компоненты представляются не напрямую, а как бы завернутыми в оболочку контейнера. Для клиента контейнер выполняет функции создания экземпляра компонента, его уничтожения и поиска. Поскольку инициатором всех процессов, происходящих в системе, является клиент, то сам он не должен реализовывать никаких интерфейсов. В то же время клиент имеет в своем распоряжении методы следующих интерфейсов:
Более подробно интерфейс между пользователем и клиентом описан во врезке "Методы, доступные клиенту". Таким образом, клиент получает все возможности для создания, управления и уничтожения компонентов, причем все эти операции можно выполнять удаленно.
Роли
Спецификация Enterprise JavaBeans определяет не только программные интерфейсы и элементы построения распределенной вычислительной системы, но и роли, выполняемые разработчиками ПО для создания, установки, отладки и эксплуатации системы, построенной на основе модели Enterprise JavaBeans. Ниже кратко описаны пять ролей, определенных в спецификации.
Разработчик компонентов. Разработчик компонентов создает сами компоненты Enterprise JavaBeans, выполняющие определенные функции. При этом он должен следовать стандартам на интерфейс для компонентов, но может не заботиться о безопасности, транзакциях, распределенных вычислениях и других сервисах, которые предоставляет контейнер.
Сборщик программы. Сам по себе компонент еще не может быть использован в реальной системе. Приложение создает сборщик программы, объединяющий компоненты в единый комплекс и создающий ПО для клиента. В результате его работы должно получиться законченное приложение, которое в дальнейшем будет элементом корпоративной вычислительной системы. Однако, сборщик программы не занимается настройкой своего приложения под условия конкретного предприятия.
Установщик. Настройкой отдельных приложений и обеспечением их совместного взаимодействия в системе предприятия занимается установщик. Он, например, определяет правила политики безопасности, которым должны подчиняться компоненты. Таким образом, установщик адаптирует приложения к условиям вычислительной среды предприятия.
Разработчик контейнеров. Разработкой же механизмов и методов взаимодействия компонентов системы между собой занимается разработчик контейнеров. Он должен разработать масштабируемые, безопасные и ориентированные на транзакции контейнеры, опираясь на низкоуровневые компоненты, поставляемые разработчиками серверных компонентов.
Разработчик серверных компонентов. Разработчик контейнеров должен использовать интерфейс взаимодействия с операционной системой, базой данных и серверов приложений. Поэтому производители перечисленных систем должны создавать соответствующие элементы. В результате они выступают в роли разработчика серверных компонентов.
Таким образом, спецификация Enterprise JavaBeans разделяет задачи, которые должен решать каждый из участников разработки вычислительной системы предприятия.
Разработчики спецификации Enterprise JavaBeans попытались комплексно определить технологию построения вычислительной системы предприятия. Получилось ли у них то, что они задумали - покажет время. Пока же еще нет окончательной версии спецификации, но она, видимо, появиться в феврале или марте текущего года.
Методы, доступные клиенту
Интерфейс Container
Интерфейс ContainerMetaData
Интерфейс Factory
Кроме перечисленных методов наследник Factory должен определять еще несколько методов create с различными аргументами.
Интерфейс Finder (только для сущностей)
Интерфейс EJBObject
Все перечисленные интерфейсы расширяют java.rmi.Remote, что позволяет запускать описанные ими методы удаленно с другого компьютера.