Здравствуйте, подскажите пожалуйста где можно достать материалы по курсу Кросс-платформенные и многозвенные технологии, о которых говориться, к примеру, в Лекции 2. Пример "Служба мгновенных сообщений" |
Технология Enterprise Java Beans. Часть 1
Пример "Точное время"
Компонент
Разработаем сеансовый компонент без состояния (Stateless Session Bean),который в ответ на запрос будет возвращать текущее серверное время. Назовем компонент CorrectTimeBean.Вначале рассмотрим исходный код компонента и консольного клиента, при помощи которого мы будет узнавать точное время сервера, а затем покажем, как можно реализовать данный пример при помощи среды разработки Eclipse-WTP.Все классы и интерфейсы, относящиеся непосредственно к компоненту, размещены в пакете correctTimeBean.
Удаленный интерфейс
Сначала определим удаленный интерфейс (Remote Interface) разрабатываемого компонента в файле CorrectTimeRemote.java. Все виды удаленных интерфейсов расширяют (extends) интерфейс javax.ejb.EJBObject.
package correctTimeBean; import java.rmi.RemoteException; import java.util.Date; import javax.ejb.EJBObject; public interface CorrectTimeRemote extends EJBObject { public Date obtainCorrectTime() throws RemoteException; }
Здесь присутствует единственный метод obtainCorrectTime() - он возвращает текущее время сервера. Этот метод может генерировать исключение RemoteExcepton,требуемое для всех методов удаленных интерфейсов.
Домашний интерфейс
Теперь определим домашний интерфейс (Home Interface) в файле CorrectTimeHomejava.
package correctTimeBean; import java.rmi.RemoteException; import javax.ejb.CreateException; import javax.ejb.EJBHome; public interface CorrectTimeHome extends EJBHome { public CorrectTimeRemote create() throws RemoteException, CreateException; }
Метод create () отвечает за инициализацию экземпляра разрабатываемого компонента. При необходимости метод create() может быть перегружен (overloaded),то есть, определены методы create с возможно другим списком формальных параметров. Вызов метода create() возвращает удаленную ссылку компонента CorrectTimeBean.
Класс компонента
Теперь рассмотрим класс, реализующий разрабатываемый компонент (файл CorrectTimeBean.java ).
package correctTimeBean; import java.rmi.RemoteException; import java.util.Date; import javax.ejb.EJBException; import javax.ejb.SessionBean; import javax.ejb.SessionContext; public class CorrectTimeBean implements SessionBean { public static final long serialVersionUID = 6066529880637710711L; public void ejbCreate() { } public Date obtainCorrectTime() { return new Date(); } public void ejbActivate() throws EJBException, RemoteException { } public void ejbPassivate() throws EJBException, RemoteException { } public void ejbRemove() throws EJBException, RemoteException { } public void setSessionContext(SessionContext arg0) throws EJBException, RemoteException { } }
В этом классе определен метод obtainCorrectTime(). Он возвращает текущее время на сервере. Помимо этого, в данном компоненте определены еще несколько методов.
- void ejbCreate(). Компоненты EJB никогда не должны определять конструкторы. Необходимая инициализация проводится в методе ejbCreate (). Для сеансового компонента без состояния (Stateless Session Bean) этот метод вызывается всего один раз - непосредственно при создании его сервером. Когда клиент при помощи домашнего интерфейса (Home Interface) получает ссылку на объект сеансового компонента без состояния, метод ejbCreate() не вызывается.
- void ejbActivate() throws EJBException, RemoteException. Этот метод используется для сеансовых компонентов с состоянием (Stateful Session Beans) и объектных компонентов (Entity Beans).В сеансовых компонентах без состояния (Stateless Session Beans) не используется.
- void ejbPassivate() throws EJBException, RemoteException. Этот метод используется для сеансовых компонентов с состоянием и объектных компонентов. В сеансовых компонентах без состояния (Stateless Session Beans) не используется.
- void ejbRemove() throws EJBException, RemoteException. Этот метод для сеансового компонента без состояния вызывается один раз -при его удалении.
- void setSessionContext(SessionContext arg0).
Также определено поле public static final long serialVersionUID. Начиная с Java 1.5 во всех классах, которые имплементируют (implements) интерфейс java.io.Serializable, рекомендуется определить это поле с уникальным 64-битным ключом. В случае если это поле не будет определено, то Java -машине придется генерировать его при сериализации объекта. Это поле генерируется по классу объекта. Если оно генерируется автоматически, то может возникнуть следующая ситуация - версии классов при сериализации и десериализации могут отличаться (могут быть, к примеру, немного по-другому реализованы методы). Тогда при десериализации будет сгенерирована исключительная ситуация. Если же поле было определено разработчиком, то такой ситуации можно было бы избежать. Поле serialVersionUID может быть сгенерировано при помощи встроенного в Eclipse-WTP механизма (см. далее), либо при помощи утилиты serialver , входящей в любой Java Development Kit.Более подробно про механизм сериализации можно прочитать в Java Object Serialization Specification.
Дескриптор развертывания
Теперь необходимо определить XML -дескриптор развертывания компонента (Deployment descriptor).Компоненты бывают различных типов, могут взаимодействовать со службой безопасности, транзакциями, системой имен и другими общими для распределенных объектных систем сервисами. Контейнер EJB управляет компонентами автоматически, но ему необходимо предоставить дополнительную информацию о каждом компоненте. Для этого используются дескрипторы развертывания. Изначально в EJB 1.0 в качестве дескрипторов использовались специальные классы, потом эти классы были заменены XML -файлами. Для EJB 2.1 дескриптор развертывания для CorrectTimeBean выглядит следующим образом:
<?xml version="1.0" encoding="UTF-8"?> <ejb-jar id="ejb-jar ID" version="2.1" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2 0 01/XMLSchema-instance" xsi:schemaLocation= "http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/ejb-jar 2 1.xsd"> <description>First session bean</description> <display-name>CorrectTimeProject</display-name> <enterprise-beans> <session> <ejb-name>CorrectTimeBean</ejb-name> <home>correctTimeBean.CorrectTimeHome</home> <remote>correctTimeBean.CorrectTimeRemote</remote> <ejb-class>correctTimeBean.CorrectTimeBean</ejb-class> <session-type>Stateless</session-type> <transaction-type>Container</transaction-type> </session> </enterprise-beans> </ejb-jar>
В этом дескрипторе развертывания имеются следующие элементы:
- <ejb-jar> - корневой элемент. В нем обязательно должен быть один элемент <enterprise-beans>, а также дополнительные элементы.
- <description> - словесное описание.
- <display-name> - этот элемент представляет собой идентификатор, который могут использовать некоторые программы, работающие с дескриптором развертывания (Eclipse, JBuilder, IntelliJ Idea).
- <enterprise-beans> - содержит объявления всех компонентов, описываемых этим дескриптором развертывания.
- <session> - в этом элементе описывается сеансовый компонент (Session Bean).
- <ejb-name> - имя компонента.
- <home> - полное имя класса домашнего интерфейса.
- <remote> - полное имя класса удаленного интерфейса.
- <ejb-class> - полное имя класса компонента. Этот класс реализует прикладные методы компонента.
- <session-type> - тип сеансового компонента. Stateless - без состояния, Stateful - с состоянием.
- <transaction-type> - определяет, управляет ли сеансовый компонент сам своими транзакциями, или за него это делает контейнер EJB. Значение Container - управляет контейнер, значение Bean - управляет компонент.
Клиентское приложение
Теперь создадим клиентское приложение (файл CorrectTimeClient.java ), которое будет подключаться к контейнеру EJB,получать ссылку на удаленный интерфейс компонента, вызывать метод получения серверного времени, а также печатать это время вместе со временем на клиентском компьютере.
public class CorrectTimeClient { public static void main(String[] args) { try { Context jndiContext = createJBossContext(); Object ref = jndiContext.lookup("CorrectTimeBean"); CorrectTimeHome home = (CorrectTimeHome) PortableRemoteObject.narrow(ref, CorrectTimeHome.class); CorrectTimeRemote remote = home.create(); Date localDate = new Date(); Date remoteDate = remote.obtainCorrectTime(); System.out.println("Remote time is " +remoteDate.toString()+ "\nCurrent time is " + localDate.toString()); } catch (RemoteException e) { e.printStackTrace(); } catch (NamingException e) { e.printStackTrace(); } catch (CreateException e) { e.printStackTrace(); } } public static Context createJBossContext() throws NamingException { Properties p = new Properties(); p.put("java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory"); p.put("java.naming.provider.url", "jnp://127.0.0.1:1099"); p.put("java.naming.factory.url.pkgs", "org.jboss.naming:org.jnp.interfaces"); Context jndiContext = new InitialContext(p); return jndiContext; } }
Клиент, собирающийся обратиться к компоненту, начинает с использования пакета JNDI (Java Naming and Directory Interface) для получения соединения с контейнером компонента. JNDI - это независимый от реализации API для систем имен и каталогов. Каждый производитель EJB должен предоставить JNDI -совместимую службу каталогов. При помощи JNDI клиент получает удаленные ссылки на компоненты, размешенные в контейнере EJB. Метод createJBossContext() содержит логику, основывающуюся на JNDI для получения сетевого соединения с сервером JBoss.Для разных контейнеров EJB этот код будет разным, например, для контейнера WebSphere код выглядит иначе:
public static Context createWebSphereContext() throws NamingException { Properties p = new Properties(); props.put(Context.INITIAL_CONTEXT_FACTORY, "com.ibm.websphere.naming.WsnInitialContextFactory"); props.put(Context.PROVIDER_URL,"iiop://localhost:900"); Context jndiContext = new InitialContext(p); return jndiContext; }
Подробнее JNDI будет рассмотрен далее в отдельной главе.
Параметр java.naming.provider.urI в теле метода createJBossContext () задает URL сервера приложений JBoss.В данном примере этот параметр равен jnp://127.0.0.1:1099/,что соответствует JNDI сервису от JBoss,запущенного на локальной машине. После установления JNDI -соединения и получения контекста из метода createJBossContext () этот контекст можно использовать для поиска внутреннего объекта CorrectTimeBean.
Object ref = jndiContext.lookup("CorrectTimeBean");
В нашем клиентском приложении используется метод
PortableRemoteObject.narrow(); CorrectTimeHome home = (CorrectTimeHome)
PortableRemoteObject.narrow(ref, CorrectTimeHome.class);
Имя, по которому выполняется поиск внутреннего объекта компонента CorrectTimeBean,задается во время развертывания. Оно может совпадать с именем компонента в XML- дескрипторе, либо быть чем-то совершенно другим.
Получив удаленную ссылку на внутренний объект, мы может использовать ее для создания нового объекта компонента CorrectTimeBean.
CorrectTimeRemote remote = home.create();
На самом деле, как уже говорилось раньше, сеансовый компонент без состояния не обязательно создается при вызове create() - у контейнера EJB есть пул уже готовых компонентов без состояния. На вызов create() он просто возвращает один из них. Теперь мы можем вызвать метод obtainCorrectTime (), в результате чего будет вызван через RMI соответствующий метод у компонента из контейнера EJB.
Date remoteDate = remote.obtainCorrectTime();