С точки зрения дизайна, было сделано то, что мы ввели сессионный компонент ShowMahager, являющийся простым façade (фасадом), который могут использовать клиенты для создания, удаления фильмов в системе, за которым располагаются наши entity bean'ы. Ясно, что сессионный компонент ShowManager предназначен для управления системой JavaTheater - предполагается, что обычные пользователи не будут изменять списочную информацию.
Теперь мы перейдем к созданию второго сессионного компонента, задачей которого является возвращение информации о фильме, но не предоставление метода для изменения списка фильмов. Этот компонент, к которому можно безопасно открыть доступ для всех пользователей, мы назовем ShowListing. Однако возвращаемая информация в распределенной системе может использовать слишком широкий канал, если она сделана неправильно. Определенно вам не нужно, например, создание клиентов, один из которых получает список названий фильмов, другой получает директоров картин и тому подобное. Вместо этого нам нужен клиент для получения всей необходимой информации о фильме в одном едином сетевом запросе.
Такой метод является тем, что мы называем групповой аксессор, метод, который получает доступ к групповой информации в едином обращении - информация может быть передана в компонент или возвращена из компонента. Этой массе информации необходима некоторая структуризация, но так как нет конструктора для структур в Java, групповой аксессор всегда работает в связке с объектами значения. Существуют простые, обычные интерфейсы Java, которые сериализуются и предоставляют публичные поля для чтения и записи значений - также называемые объектами значения или, в зависимости от автора, объектами данных или объект-посыльный.
Приведенный ниже код является примером объекта значения, предназначенного для передачи информации о фильме, который определен в своем собственном пакете, называемом javatheater.valueobject, и будет доступен и для клиентского кода и для кода компонент.
package javatheater.valueobject;
//:example09/src/ejb-tier/javatheater/valueobject/MovieItem.java
/**
* MovieItem является объектом данных, используемый для передачи
* структурированной информации о Movie между клиентом и ejb.
*/
public class MovieItem implements java.io.Serializable {
public Integer pk;
public String title;
public MovieItem(Integer pk, String title) {
this.pk = pk;
this.title = title;
}
}
// /:~
Код, как вы видите, очень прост: это просто публичный класс с парой публичных полей для информации о фильме. Сам по себе класс сериализуем и предоставляет соответствующий конструктор. Этот класс будет использоваться в реализации сессионного компонента ShowListing, код которого приведен ниже:
//:example10/src/ejb-tier/javatheater/ejb/implementation/MovieBean.java
package javatheater.ejb.implementation;
import javax.ejb.*;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
/**
* Этот код реализации сущностного компонента <code>Movie</code>.
*
* @see javatheater.ejb.MovieHome
* @ejb:bean name="Movie" local-jndi-name="javatheater/Movie" view-type="local"
* type="CMP" primkey-field="id" reentrant="False"
* @ejb:pk class="java.lang.Integer"
* @ejb:home local-class="javatheater.ejb.MovieHome"
* @ejb:interface local-class="javatheater.ejb.Movie"
* @ejb:finder signature="java.util.Collection findByTitle(java.lang.String
* title)" query="SELECT OBJECT(m) FROM Movie m WHERE m.title=?1"
* @ejb:finder signature="java.util.Collection findAll()" query="SELECT
* OBJECT(m) FROM Movie m"
* @ejb:env-entry name="CounterName" value="MoviePKCounter"
* type="java.lang.String"
*/
public abstract class MovieBean implements EntityBean {
/**
* Проверяем первичный ключ и инициализируем параметры компонента.
*
* @ejb:create-method
*/
public Integer ejbCreate(Integer id, String title) throws CreateException {
if (id == null)
throw new CreateException("Primary key cannot be null");
if (id.intValue() == 0)
throw new CreateException("Primary key cannot be zero");
setId(id);
setTitle(title);
return null;
}
/**
* Ничего не делаем.
*/
public void ejbPostCreate(Integer id, String title) {
}
/**
* Домашний метод возвращает имя счетчика (String) для генерации уникального
* значения первичного ключа фильма. Реальное имя определяется как
* переменная окружения компонента Movie.
*
* @ejb:home-method
*/
public String ejbHomeGetCounterName() {
try {
Context initial = new InitialContext();
return (String) initial.lookup("java:comp/env/CounterName");
}
catch (NamingException e) {
throw new EJBException(e);
}
}
/**
* Возвращает Movie id, который также является первичным ключем.
*
* @ejb:pk-field
* @ejb:persistent-field
* @ejb:interface-method
*/
abstract public Integer getId();
/**
* Этот метод не является частью удаленного интерфейса но ejbdoclet будет
* все равно генерировать его реализацию. Этот методы вызывается из
* ejbCreate().
*/
abstract public void setId(Integer id);
/**
* Возвращает заголовок фильма.
*
* @ejb:persistent-field
* @ejb:interface-method
*/
abstract public String getTitle();
/**
* Устанавливает заголовок фильма.
*
* @ejb:persistent-field
* @ejb:interface-method
*/
abstract public void setTitle(String title);
}
// /:~
Как вы видите из тэгов EJBDoclet'а, это просто обычный сессионный компонент без состояний с удаленным клиентским представлением. Метод ejbCreate( ) не делает ничего специального, за исключением получения домашнего интерфейса entity компонента Movie, который будет использоваться позже в методе getMovie(…). getMovie(…) реализуется для: получения экземпляра Movie по значению первичного ключа, который передан в качестве аргумента; создания объекта MovieItem, заполняющегося информацией о фильме; и возвращения объекта MovieItem.
Я умышленно упростил этот код, но, тем не менее, он демонстрирует использование группового аксессора и объекта значений для получения структурированной информации в одном цельном RMI вызове. Помните, что вы точно также можете использовать групповой аксессор для передачи информации в компонент. В следующем примере мы будем реализовывать дополнительный групповой аксессор, предназначенный для получения информации по различным критериям.
И, наконец, пожалуйста, обратите внимание, что в этом примере мы реализовали групповой аксессор в сессионном компоненте, но вы можете сделать то же самое и для entity компонента. В обоих случаях вы можете пожелать, чтобы ваш компонент имел и обычный доступ, так чтобы ваш удаленный клиент не получал огромный, структурированный объект по сети в том случае, если вы интересуетесь только одним полем.