Эта проблема была решена, начиная с EJB 3.1, созданием встраиваемого EJB-контейнера. Версия EJB 3.1 привнесла стандартный API-интерфейс для выполнения EJB-компонентов в среде Java SE. Embeddable API (пакет javax.ejb.embeddable) позволяет клиенту создать экземпляр EJB-контейнера, который будет функционировать в рамках своей виртуальной машины Java. Встраиваемый контейнер обеспечивает управляемую среду с поддержкой тех же базовых служб, которые имеют место в контейнере Java EE: внедрение, управление транзакциями, управление жизненным циклом и т. д. Встраиваемые EJB-контейнеры работают только с API-интерфейсом подмножества EJB Lite (никаких EJB-компонентов, управляемых сообщениями, удаленных вызовов и т. д.), то есть они обладают теми же возможностями, что и контейнер EJB Lite (но не контейнер, отвечающий полной версии EJB).
В листинге 7.22 приведен класс Main, который задействует API-интерфейс для начальной загрузки, чтобы запустить контейнер (абстрактный класс javax.ejb.embeddable.EJBContainer), осуществляет поиск EJB-компонента и вызывает методы в нем.
public class Main {
··public static void main(String[] args) throws NamingException {
····// Задает путь к классам контейнера
····Map
····properties.put(EJBContainer.MODULES, new File("target/classes"));
····// Создает встроенный контейнер и получает JNDI-контекст
····try (EJBContainer ec = EJBContainer.createEJBContainer(properties)) {
······Context ctx = ec.getContext();
······// Создает экземпляр Book
······Book book = new Book();
······book.setTitle("Автостопом по Галактике");
······book.setPrice(12.5F);
······book.setDescription("Научно-фантастическая комедийная книга");
······book.setIsbn("1-84173-742-2");
······book.setNbOfPage(354);
······book.setIllustrations(false);
······// Осуществляет поиск EJB-компонента с представлением без интерфейса
······ItemEJB itemEJB = (ItemEJB) ctx.lookup("java: global/classes/ItemEJB");
······// Обеспечивает постоянство Book в базе данных
······itemEJB.createBook(book);
······// Извлекает информацию обо всех соответствующих книгах из базы данных
······for (Book aBook: itemEJB.findBooks()) {
········System.out.println(aBook);
······}
····}
··}
}
Как вы можете видеть в листинге 7.22, EJBContainer содержит фабричный метод (createEJBContainer()) для создания экземпляра контейнера. По умолчанию встраиваемый контейнер осуществляет поиск, используя путь к классам клиента, чтобы отыскать набор EJB-компонентов для инициализации (либо вы можете задать путь к классам в свойствах). После инициализации контейнера приложение получает JNDI-контекст контейнера (EJBContainer.getContext(), который возвращает javax.naming.Context) для поиска ItemEJB (с использованием синтаксиса переносимых глобальных JNDI-имен).
Следует отметить, что ItemEJB (приведенный ранее в листинге 7.1) является сессионным EJB-компонентом без сохранения состояния, обеспечивающим бизнес-методы с помощью представления без интерфейса. Он использует внедрение, транзакции, управляемые контейнером, и сущность JPA с именем Book. Встраиваемый контейнер заботится о внедрении менеджера сущностей и фиксации или откате любой транзакции. EJBContainer реализует java.lang.AutoCloseable, поэтому блок try-with-resources автоматически вызовет метод EJBContainer.close() для закрытия экземпляра встраиваемого контейнера.
В листинге 7.22 я использовал класс Main, чтобы показать вам, как применять встраиваемый EJB-контейнер. Однако имейте в виду, что в настоящее время EJB-компоненты можно использовать в любой среде Java SE: от тестовых классов до Swing-приложений, и даже при пакетной обработке.
Вызов корпоративных EJB-компонентов