Таким образом, мы реализовали entity bean и session bean, который предоставляет клиенту удаленное представление. Это означает, что все вызовы методов этих компонентов будут происходить посредством RMI, через прокси и сериализацию аргументов метода, возвращаемых значений и всех исключений. Этот процесс очень затратный с точки зрения производительности (вызов RMI метода может быть на несколько порядков медленнее, чем обычный вызов метода внутри той же самой JVM), но вы получаете выгоду в виде полной прозрачности вашего компонента - клиентский код не знает в каком процессе расположен экземпляр компонента и не беспокоится об этом.
Важно запомнить, что когда клиентский код получает доступ к enterprise компоненту посредством удаленного представления компонента, вызовы всегда совершаются через RMI, даже если клиентский код и экземпляр компонента расположены в одной и той же JVM (на самом деле, контейнер обычно оптимизирует входящие в JVM RMI вызовы, используя некий специальный прокси, но какой-то прокси все равно существует и вызов все равно происходит медленнее, чем это могло быть, если бы клиентский код был способен получить прямой доступ к компоненту).
EJB 1.1 enterprise bean может предоставлять только удаленное представление. Однако, в попытках улучшить производительность, производители Контейнеров начали вводить в свои продукты некоторые "магические" флаги, которые могли бы использовать администраторы для отключения RMI для enterprise bean. Смысл этого, как я думаю, заключается в том, что клиентский код располагается в той же самой JVM, что и компоненты.
В конце концов, спецификация EJB 2.0 учла эту подсказку, поданную индустрией, и ввела концепцию локального представления enterprise bean. Когда enterprise bean предоставляет локальное представление, то RMI при этом не участвует вообще, а домашний и компонентный интерфейс компонента могут быть доступны из клиентского кода в той же самой JVM. Это более эффективно, но предотвращает использование компонентов в сильно распределенном приложении.
Компонент может поддерживать и локальное, и удаленное представления, в таком случае разработчик должен реализовывать домашний и компонентный интерфейсы в их локальной и удаленной версии. Так как локальный и удаленный интерфейсы объявляются разными тегами в дескрипторе развертывания, они легко могут быть идентифицированы Контейнером и пользователем компонента. Только session и entity bean могут иметь удаленное представление, в то время как message-driven bean'ы могут располагать только локальным представлением (другими словами, они никогда не могут быть доступны удаленно).
Хотя идея локального и удаленного представления сначала звучит хорошо, в обществе происходили некоторые дебаты относительно чистоты такого дизайна. Наибольшие нарекания вызывала неоднозначность при передаче аргументов. Когда интерфейс является удаленным (доступ через RMI) все аргументы при вызове метода передаются по значению: значения аргумента копируются в пространство процесса удаленного экземпляра, в то время как оригиналы остаются на клиентской стороне. С другой стороны, когда интерфейс локальный, все аргументы передаются по ссылке (за исключением примитивных типов данных и ссылок), а затем реализация метода оперирует с оригиналами. Проблема локального и удаленного представления EJB состоит в том, что нет ничего в интерфейсе Java, что могло бы проинформировать программиста, какой вид передачи аргументов используется. Ключом может быть только то, что интерфейсы были получены посредством JNDI, но как только вы получили ссылку на интерфейс, нет способа узнать происходит ли передача по значению или по ссылке. Как вы можете представить, в больших приложениях, в который разные части системы разрабатываются различными людьми, а ссылки на интерфейсы передаются по кругу постоянно, нет никакой информации времени компиляции или времени выполнения относительно модели передачи аргументов, что может привести к ошибкам.
Так как язык программирования и спецификация не помогают предотвратить эту проблему, вы должны прийти к своей собственной стратегии защиты. Это может быть, конечно, соглашение об именовании ваших интерфейсов компонент, например MovieLocal против MovieRemote; другой подход может заключаться в решении, что entity bean предназначен для предоставления только локального представления (что соответствует шаблону дизайна Façade).
Теперь пришло время для реализации нашего первого локального представления EJB. Вместо создания новой марки EJB, мы изменим реализацию Movie и AutoCounter entity bean'ов так, чтобы они предоставляли только локальное представление и не имели удаленного представления. Вот код для компонента Movie:
//: example08/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>
entity компонента.
*
*
@see
javatheater.ejb.MovieHome
* @ejb:bean name="Movie" local-jndi-name="javatheater/Movie" view-type="local"
* type="CMP" primkey-field="id" reentrant="False"
* @ejb:home local-class="javatheater.ejb.MovieHome"
* @ejb:interface local-class="javatheater.ejb.Movie"
* @ejb:pk class="java.lang.Integer"
* @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
) {
}
/**
* @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
)
;
}
}
/**
* @ejb:pk-field
* @ejb:persistent-field
* @ejb:interface-method
*/
abstract public
Integer getId
()
;
/**
* Этот метод не будет частью удаленного интерфейса.
*/
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'а в параметрах ejb:bean, ejb:home и ejb:interfase. В тэге ejb:bean мы использовали параметр local-jndi-name для указания JNDI имени локального домашнего интерфейса компонента Movie, а так же мы использовали view-type="local" для указания, что этот компонент, фактически, предоставляет только локальное представление. В тэгах ejb:home и ejb:interface мы использовали новый параметр local-class для указания имени домашнего и компонентного интерфейсов, который EJBDoclet сгенерирует за нас.
В самом Java коде нет отличий, но это происходит потому, что EJBDoclet берет на себя заботу о генерации кода, который подходит для локального представления. Я предполагаю, что прежде чем продолжить читать, вы запустите Ant скрипт для сборки компонента, и если сборка закончится успешно, вы взглянете на сгенерированный EJBDoclet'ом код в директории example08/gen. Начните с рассмотрения MovieHome.java, исходного файла для домашнего интерфейса Movie: вы должны заметить, что интерфейс больше не наследуется от javax.ejb.EJBHome, который является RMI интерфейсом, а от javax.ejb.EJBLocalHome, который не является RMI интерфейсом. Так что ваш домашний интерфейс больше не доступен удаленным клиентам и, соответственно, его методы больше не выбрасывают RemoteException. То же самое верно и для локального компонентного интерфейса Movie, определенного в Movie.java и расположенного в том же директории; отличия состоят в том, что интерфейс более не наследуется от javax.ejb.EJBObject, а вместо этого от javax.ejb.EJBLocalObject.
Теперь, когда удаленные клиенты не могут получить доступ к экземпляру компонента Movie, необходимо внести изменения в клиентский jar и в клиентское приложение. Ant скрипт был изменен и не поместил домашний и компонентный интерфейсы для Movie и AutoCounter в клиентский jar, так как они теперь бесполезны для клиентского приложения и могут, очевидно, ввести в заблуждение клиентского программиста. Клиентское приложение и тестовый случай будут также изменены, и каждая ссылка на Movie и AutoCounter будут удалены. Вы можете пожелать взглянуть на код в директории rmiclients этого примера, чтобы осведомится о деталях.
Однако, в данном случае важно не то, как поменялся код, а последствия изменений. Их можно разбить на две главные категории: производительность и дополнительное управление контейнера. Говоря о производительности, должно быть понятно, что теперь вызов методов локального интерфейса будет происходить гораздо быстрее, чем при вызове того же метода в удаленном интерфейсе, поскольку в этом случае не задействован RMI. Плюс к этому, так как компонент предоставляет только локальное представление, которое может быть доступно только для других локальных компонентов в этом же процессе, удаленные клиенты должны получать не прямой доступ к локальным компонентам, возможно, посредством использования удаленного session bean, работающего по принципу façade, который в последствии взаимодействует с локальными entity bean'ами. Session bean реализует бизнес логику и принимает вызовы методов, представляющие "макро-функции" вашего приложения; при реализации своей логики session bean может взаимодействовать с несколькими локальными компонентами. В этом случае, количество вызовов методов, которые реально происходят по сети, сильно сокращается.
Другой важный смысл использования локальных интерфейсов для компонента состоит в том, что контейнер может безопасно предполагать, что никакой другой процесс не способен воздействовать на этот экземпляр компонента, и поэтому, может делать с ним больше, чем он мог бы сделать с удаленным экземпляром. Наиболее примечательным в этой особенности EJB, применимой только для локального представления, является взаимосвязь между компонентами, одно из главных улучшений в EJB 2.0. Мы поговорим о взаимосвязях в следующем разделе, но сначала нам необходимо поговорить об языке запросов (EJB Query Language) EJB (EJB-QL), а перед этим вспомнить о группе аксессоров и объектах значения.
← | Реализация session bean | Группа аксессоров и объекты значения | → |