Клиентское EJB приложение


Код в директории c18/example03/rmiclients содержит пример простейшего удаленного клиентского Java приложения, которое использует наш компонент Movie для создания, просмотра и удаления записей о фильмах в системе на стороне сервера.

Как и для любого другого удаленного RMI приложения, есть четыре вещи, которые вам необходимо сделать, чтобы запустить приложение: 1) убедитесь, что клиентское приложение может найти пакеты, требуемые для EJB API и для вашего Контейнера; 2) поместите клиентский jar, содержащий определения домашнего и компонентного интерфейса в каталог, который виден клиентскому приложению в переменной CLASSPATH; 3) сконфигурируйте параметр JNDI так, чтобы клиентское приложение могло соединиться к службе JNDI, поставляемой EJB Контейнером; и 4) используйте JNDI API для того, чтобы найти EJB дом.

Клиентские пакеты для EJB и JBoss есть в пакете поставки JBoss, а клиентский jar для компонента Movie может быть легко создан с помощью Ant скрипта.

Мы выполним настройку параметров JNDI с помощью внешнего файла jndi.properties, который должен также располагаться в директории, зарегистрированном в CLASSPATH, что я уже объяснял в приведенном выше разделе, посвященном JNDI.

Нашим поставщиком службы JNDI будет JBoss (вернее, одна из его служб), так что нам необходимо предоставить следующую специфическую конфигурационную информацию JNDI в файле jndi.properties:

java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
java.naming.provider.url=localhost:
1099
java.naming.factory.url.pkgs=org.jboss.naming

Пожалуйста, обратите внимание, что если ваше клиентское приложение и ваш JBoss работают на разных машинах, вам необходимо заменить localhost в приведенном выше файле на IP адрес машины, на которой запущен JBoss.

Ниже приведен код для нашего клиентского приложения MovieClient:

//: c18:example03:src:rmiclients:javatheater:client:MovieClient.java
package javatheater.client;

import javax.naming.*;

import javatheater.ejb.*;

public class MovieClient {
  
public static void main ( String [] args ) throws Exception {
     
javax.naming.Context initial = new javax.naming.InitialContext () ;
      Object objRef = initial.lookup
( "javatheater/Movie" ) ;
      MovieHome movieHome =
( MovieHome ) javax.rmi.PortableRemoteObject
            .narrow
( objRef, MovieHome. class ) ;
     
// Генерируем значение первичного ключа
     
int pkValue = ( int ) System.currentTimeMillis () % Integer.MAX_VALUE;
     
// Создаем новый Movie entity
     
Movie movie = movieHome.create ( new Integer ( pkValue ) , "A Bug's Life" ) ;
     
// В качестве проверки, находим только что созданный entity
     
movie = movieHome.findByPrimaryKey ( new Integer ( pkValue )) ;
     
// Доступ к параметрам компонента
     
System.out.println ( movie.getId ()) ;
      System.out.println
( movie.getTitle ()) ;
     
// Удаляем компонент
     
movie.remove () ;
  
}
}
// /:~

Если вы помните раздел относящийся к JNDI, первая пара инструкций будет вам понятна: мы создаем InitialContext, который, в соответствии нашим установкам JNDI, будет указывать на корень имен в JBoss; затем, мы используем метод lookup( ) для нахождения интерфейса Movie, используя компонентное имя JNDI, которое мы указали в файле jboss.xml.

Использование PortableRemoteObject должно быть объяснено. Мы пытаемся здесь выполнить типобезопасное удаленное приведение к базовому типу. Вы помните, что метод lookup( ) возвращает ссылку на общий Object; а так как мы знаем, что мы ищем интерфейс MovieHome, нам необходимо привести его к соответствующему типу времени выполнения. Также мы хотим быть информированы, если мы попытаемся привести к неправильному типу. Но, если вы подумаете над этим, то поймете, что проверка должна быть выполнена над объектом на сервере, а не в локальном процессе. Локально у нас есть только RMI прокси, а не реальный объект. Метод narrow( ) реализует такую форму типобезопасного удаленного приведения типа. Он совершает передачу по сети и проверяет, что тип времени выполнения объекта, переданного в качестве первого аргумента, совместим с типом, представленным вторым аргументом. Если типы не совместимы, вы получите исключение. Если удаленное приведение типов закончится успешно, вам просто нужно выполнить приведение типа для локальной ссылки к нужному вам типу, вот какой смысл для такого приведения типа.

Пожалуйста, обратите внимание, что для определенных целей может быть допустимо использование простого приведения типов Java, но использование метода narrow( ) гарантирует, что удаленное приведение будет работать на всех платформах и в любом окружении.

Как только мы получим ссылку на домашний интерфейс Movie, мы можем выполнить создание, поиск и удаление экземпляра Movie. Так как Movie является entity bean'ом, нам необходимо передавать новый первичный ключ при каждом создании нового экземпляра. Для снижения риска использования существующего первичного ключа, что может вызвать ошибку вашего приложения при получении DuplicateException, я использую простое вычисление, основываясь на System.currentTimeMillis( ) для генерации приемлемо уникального значения. Пожалуйста, обратите внимание, что не следует использовать эту технику в коде вашего продукта, так как она не гарантирует уникальности полученного значения (проблема генерации уникальных первичных ключей не является тривиальной и не может быть освещена здесь).

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

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

Как только вы получите работающий пример, у вас может возникнуть желания попытаться закомментировать строку, которая удаляет экземпляр, запустить пример еще раз или два, оставить JBoss запущенным и, используя Hypersonic Database Manager проверить содержимое базы данных JBoss по умолчанию. Просто запустите Database Manager как описано в руководстве по установке в этой главе, затем выберите "HSQL Database Engine Server" в качестве типа, и используйте порт 1476 в URL'е.

В каталоге c18/example04/rmiclients вы найдете различные типы клиентов: JUnit тестовый случай для нашего компонента Movie (он определен в пакете javatheater.test). Тестовый случай для запуска EJB с помощью Java приложения (пускатель JUnit теста или вашего собственного кода), так что все, что я объяснял относительно удаленного клиентского приложения применимо для запуска любых тестовых случаев.

Есть еще важная информация относительно JUnit. Если вы используете один или два графических пускателя, полученных вместе в JUnit, вы должны выключить опцию "перезагрузка классов при каждом запуске (reload classes on every run)" (есть такой элемент выбора в главном окне). Если вы оставите эту опцию включенной, JUnit загрузит ваши классы, используя свой собственный загрузчик классов. К сожалению, этот загрузчик классов не предназначен для работы через сеть, что обязательно необходимо для загрузки динамического RMI прокси, который JBoss посылает клиенту. Если вы не выключите эту опцию, ваш тестовый инструмент JUnit не будет способен присоединится к удаленному enterprise bean и не сработает.

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