1. Обзор
Enterprise Java Beans (EJB) — это основная часть спецификации Java EE , направленная на упрощение разработки распределенных приложений корпоративного уровня. Жизненный цикл EJB обрабатывается сервером приложений, таким как JBoss WildFly или Oracle GlassFish .
Компоненты EJB обеспечивают надежную модель программирования, которая облегчает внедрение программных модулей уровня предприятия, поскольку сервер приложений должен решать вопросы, не связанные с бизнес-логикой, такие как обработка транзакций, управление жизненным циклом компонентов или внедрение зависимостей.
Кроме того, мы уже опубликовали две статьи, посвященные основным концепциям EJB, поэтому не стесняйтесь ознакомиться с ними здесь и здесь .
В этом руководстве мы покажем, как реализовать базовый модуль EJB на WildFly и вызвать EJB с удаленного клиента через JNDI .
2. Реализация модуля EJB
Бизнес-логика реализуется одним или несколькими локальными/удаленными бизнес-интерфейсами (также известными как локальные/удаленные представления) или напрямую через классы, которые не реализуют интерфейс (интерфейсы без представлений).
Стоит отметить, что локальные бизнес-интерфейсы используются, когда доступ к компоненту будет осуществляться с клиентов, которые находятся в той же среде, т. е. с одним и тем же файлом EAR или WAR, тогда как удаленные бизнес-интерфейсы необходимы, когда доступ к компоненту будет осуществляться из другой среды. , то есть другой JVM или сервер приложений.
Давайте создадим базовый модуль EJB, который будет состоять всего из одного компонента. Бизнес-логика компонента будет простой и ограничивается преобразованием данной строки
в ее версию в верхнем регистре.
2.1. Определение удаленного бизнес-интерфейса
Давайте сначала определим один единственный удаленный бизнес-интерфейс, украшенный аннотацией @Remote
. Это обязательно в соответствии со спецификацией EJB 3.x , поскольку доступ к bean-компоненту будет осуществляться с удаленного клиента:
@Remote
public interface TextProcessorRemote {
String processText(String text);
}
2.2. Определение компонента без сохранения состояния
Далее реализуем бизнес-логику, реализовав вышеупомянутый удаленный интерфейс:
@Stateless
public class TextProcessorBean implements TextProcessorRemote {
public String processText(String text) {
return text.toUpperCase();
}
}
Класс TextProcessorBean
— это простой класс Java, украшенный аннотацией @Stateless
.
Bean-компоненты без сохранения состояния по определению не поддерживают никакого диалогового состояния со своими клиентами, даже если они могут поддерживать состояние экземпляра для разных запросов. Их аналог, bean-компоненты с отслеживанием состояния, сохраняют свое диалоговое состояние, и, например, их создание для сервера приложений обходится дороже.
Поскольку в этом случае вышеприведенный класс не имеет состояния экземпляра, его можно сделать без состояния. В случае, если у него есть состояние, его использование для разных клиентских запросов вообще не имеет смысла.
Поведение bean-компонента детерминировано, т. е. у него нет побочных эффектов, как и должно быть у хорошо спроектированного bean-компонента: он просто принимает входную строку
и возвращает ее версию в верхнем регистре.
2.3. Зависимости Maven
Затем нам нужно добавить в модуль артефакт javaee-api
Maven, который предоставляет все API-интерфейсы спецификации Java EE 7, в том числе необходимые для EJB:
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>7.0</version>
<scope>provided</scope>
</dependency>
На данный момент нам удалось создать базовый, но функциональный модуль EJB. Чтобы сделать его доступным для всех потенциальных клиентов, мы должны добавить артефакт в наш локальный репозиторий Maven в виде файла JAR.
2.4. Установка модуля EJB в локальный репозиторий
Есть несколько способов добиться этого. Самый простой из них состоит в выполнении этапов сборки жизненного цикла Maven clean-install :
mvn clean install
Эта команда устанавливает модуль EJB как ejbmodule-1.0.jar (
или любой произвольный идентификатор артефакта, указанный в файле pom.xml
) в наш локальный репозиторий. Для получения дополнительной информации о том, как установить локальный JAR с помощью Maven, ознакомьтесь с этой статьей .
Предполагая, что модуль EJB был правильно установлен в наш локальный репозиторий, следующим шагом будет разработка удаленного клиентского приложения, использующего наш TextProcessorBean
API.
3. Удаленный EJB-клиент
Мы сохраним бизнес-логику удаленного EJB-клиента предельно простой: во-первых, он выполняет поиск JNDI, чтобы получить прокси TextProcessorBean
. После этого он вызывает метод processText()
прокси .
3.1. Зависимости Maven
Нам нужно включить следующие артефакты Maven, чтобы клиент EJB работал должным образом:
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>7.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.wildfly</groupId>
<artifactId>wildfly-ejb-client-bom</artifactId>
<version>10.1.0.Final</version>
</dependency>
<dependency>
<groupId>com.beldung.ejbmodule</groupId>
<artifactId>ejbmodule</artifactId>
<version>1.0</version>
</dependency>
Хотя довольно очевидно, почему мы включаем артефакт javaee-api
, включение wildfly-ejb-client-bom
— нет. Артефакт необходим для выполнения удаленных вызовов EJB в WildFly.
И последнее, но не менее важное: нам нужно сделать предыдущий модуль EJB доступным для клиента, поэтому мы также добавили зависимость ejbmodule
.
3.2. Клиентский класс EJB
Учитывая, что EJB-клиент вызывает прокси TextProcessorBean
, мы будем очень прагматичны и назовем клиентский класс TextApplication
:
public class TextApplication {
public static void main(String[] args) throws NamingException {
TextProcessorRemote textProcessor = EJBFactory
.createTextProcessorBeanFromJNDI("ejb:");
System.out.print(textProcessor.processText("sample text"));
}
private static class EJBFactory {
private static TextProcessorRemote createTextProcessorBeanFromJNDI
(String namespace) throws NamingException {
return lookupTextProcessorBean(namespace);
}
private static TextProcessorRemote lookupTextProcessorBean
(String namespace) throws NamingException {
Context ctx = createInitialContext();
String appName = "";
String moduleName = "EJBModule";
String distinctName = "";
String beanName = TextProcessorBean.class.getSimpleName();
String viewClassName = TextProcessorRemote.class.getName();
return (TextProcessorRemote) ctx.lookup(namespace
+ appName + "/" + moduleName
+ "/" + distinctName + "/" + beanName + "!" + viewClassName);
}
private static Context createInitialContext() throws NamingException {
Properties jndiProperties = new Properties();
jndiProperties.put(Context.INITIAL_CONTEXT_FACTORY,
"org.jboss.naming.remote.client.InitialContextFactory");
jndiProperties.put(Context.URL_PKG_PREFIXES,
"org.jboss.ejb.client.naming");
jndiProperties.put(Context.PROVIDER_URL,
"http-remoting://localhost:8080");
jndiProperties.put("jboss.naming.client.ejb.context", true);
return new InitialContext(jndiProperties);
}
}
}
Проще говоря, все, что делает класс TextApplication , — это извлекает прокси-компонент bean-компонента и вызывает его метод
processText()
с образцом строки.
Фактический поиск выполняется вложенным классом EJBFactory
, который сначала создает экземпляр JNDI InitialContext
, затем передает необходимые параметры JNDI в конструктор и, наконец, использует его для поиска прокси-компонента. ``
Обратите внимание, что поиск выполняется с использованием проприетарного пространства имен WildFly «ejb:». Это оптимизирует процесс поиска, поскольку клиент откладывает подключение к серверу до тех пор, пока прокси-сервер не будет явно вызван.
Стоит также отметить, что можно искать прокси-сервер bean-компонента, вообще не прибегая к пространству имен «ejb». Однако мы упустим все дополнительные преимущества отложенных сетевых подключений, что сделает клиент намного менее производительным .
3.3. Настройка контекста EJB
Клиент должен знать, с каким хостом и портом установить соединение для выполнения поиска компонента. В этом случае клиенту требуется настроить проприетарный EJB-контекст WildFly, который определяется файлом jboss-ejb-client.properties
, размещенным в его пути к классам, обычно в папке src/main/resources
:
endpoint.name=client-endpoint
remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED=false
remote.connections=default
remote.connection.default.host=127.0.0.1
remote.connection.default.port=8080
remote.connection.default.connect.options.org.xnio.Options
.SASL_POLICY_NOANONYMOUS=false
remote.connection.default.username=myusername
remote.connection.default.password=mypassword
Этот файл говорит сам за себя, поскольку он предоставляет все параметры, необходимые для установления соединения с WildFly, включая количество удаленных подключений по умолчанию, хост и порт по умолчанию, а также учетные данные пользователя. В этом случае соединение не шифруется, но может быть при включенном SSL.
Последнее, что нужно учитывать, это то, что если соединение требует аутентификации, необходимо добавить пользователя в WildFly через утилиту add-user.sh/add-user.bat
.
4. Вывод
Выполнение поиска EJB в WildFly является простым, если мы строго придерживаемся описанного процесса.
Как обычно, все примеры, включенные в эту статью, доступны на GitHub здесь и здесь .