7. Версія 3: Портування додатка PAM на сервер додатків Glassfish
Ми пропонуємо розмістити EJB з рівнів [metier] та [DAO] архітектури OpenEJB / EclipseLink у контейнер сервера додатків Glassfish.
Поточна реалізація з OpenEJB / EclipseLink
![]() |
Як показано вище, рівень [ui] використовує віддалений інтерфейс рівня [metier].
Ми протестували два контексти виконання: local та distant. В останньому режимі шар [ui] був клієнтом шару [metier], який реалізовано за допомогою EJB. Щоб працювати в режимі «клієнт/сервер», в якому клієнт і сервер виконуються у двох різних JVM, ми розмістимо шари [metier, DAO, jpa] на сервері Java EE Glassfish. Цей сервер входить до складу NetBeans.
Реалізація, яку потрібно створити з використанням сервера Glassfish
![]() |
- шар [ui] працюватиме в середовищі Java SE (Standard Edition)
- шари [metier, DAO, JPA] будуть виконуватися в середовищі Java EE (Enterprise Edition) на сервері Glassfish v3
- клієнт буде взаємодіяти з сервером через мережу TCP/IP. Мережевий обмін даними є прозорим для розробника, однак він повинен усвідомлювати, що для взаємодії клієнт і сервер обмінюються серіалізованими об’єктами, а не посиланнями на об’єкти. Мережевий протокол, що використовується для цього обміну, називається RMI (Remote Method Invocation) — це протокол, який можна використовувати виключно між двома Java-додатками.
- Реалізація JPA, що використовується на сервері Glassfish, матиме назву EclipseLink.
7.1. Серверна частина клієнтсько-серверного додатка PAM
7.1.1. Архітектура додатка
Тут ми розглянемо серверну частину, яка розміщуватиметься у контейнері EJB3 сервера Glassfish:
![]() |
Мета полягає в тому, щоб перенести на сервер Glassfish те, що вже було зроблено та протестовано з контейнером OpenEJB. У цьому полягає перевага контейнера OpenEJB і, загалом, вбудованих контейнерів EJB: вони дають змогу тестувати додаток у спрощеному середовищі виконання. Після тестування додатка залишається лише перенести його на цільовий сервер, у даному випадку — сервер Glassfish.
7.1.1.1. Проєкт NetBeans
Почнемо зі створення нового проєкту NetBeans:
![]() |
- у [1], новий проєкт
- у [2], виберіть категорію Maven, а в [3] — тип EJB «Модуль». Справа в тому, що потрібно створити проект, який буде розміщений і виконуватися контейнером EJB — контейнером сервера Glassfish.
![]() |
- за допомогою кнопки [4a] виберіть батьківську папку для папки проекту або введіть її назву безпосередньо в полі [4b].
- у [5] надайте назву проекту
- у [6] виберіть сервер додатків, на якому він буде виконуватися. Тут вибрано один із тих, що відображаються на вкладці [Runtime / Servers], а саме Glassfish v3.
- у [7] виберіть версію Java EE.
![]() |
- у [1] — новий проєкт. Він відрізняється від класичного Java-проєкту кількома моментами:
- автоматично створюється гілка [Other Sources] [2]. Вона, зокрема, міститиме файл [persistence.xml], який налаштовує шар JPA,
- якщо виконати збірку (Build) проекту, з’явиться залежність [javaee-api-6.0] від [3]. Вона має тип provided, оскільки під час виконання її надає контейнер EJB від Glassfish.
7.1.1.2. Налаштування рівня персистентності
Під налаштуванням шару персистентності ми маємо на увазі створення файлу [persistence.xml], який визначає:
- реалізацію JPA, яку слід використовувати
- визначення джерела даних, яке використовується шаром JPA. Цим джерелом буде JDBC, яке керується сервером Glassfish.
![]() |
Можна діяти наступним чином. Спочатку на вкладці [Runtime / Databases] слід створити [1] з’єднання з базою даних MySQL5 / dbpam_eclipselink:
![]() |
Після цього можна перейти до створення ресурсу JDBC, який використовується модулем EJB:
![]() |
- у [1] створіть новий файл — перед виконанням цієї операції переконайтеся, що вибрано проект EJB
- у [2] — проект EJB
- у [3] виберіть категорію [Glassfish]
- у [4] потрібно створити ресурс JDBC
![]() |
- у [5] вкажіть, що ресурс JDBC використовуватиме новий пул з'єднань. Нагадуємо, що пул з'єднань — це набір відкритих з'єднань, який слугує для прискорення обміну даними між додатком та базою даних.
- у [6] присвоїти ім’я JNDI створеному ресурсу JDBC. Це ім’я може бути будь-яким, але найчастіше воно має вигляд jdbc/nom. Ця назва JNDI буде використовуватися у файлі [persistence.xml] для позначення джерела даних, яке має використовувати реалізація JPA.
- У [7] надайте будь-яке ім’я пулу з’єднань, який буде створено
- у випадаючому списку [8] виберіть з’єднання JDBC, створене раніше на основі MySQL / dbpam_eclipselink.
- У вікні [9] відображається підсумок властивостей пулу з’єднань — нічого не змінюйте
![]() |
- у [10] можна вказати кілька властивостей пулу з'єднань — залишаємо значення за замовчуванням
- у [11], після завершення роботи майстра створення ресурсу JDBC для модуля EJB, у гілці [Other Sources] було створено файл [glassfish-resources.xml]. Вміст цього файлу такий:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE resources PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Resource Definitions//EN" "http://glassfish.org/dtds/glassfish-resources_1_5.dtd">
<resources>
<jdbc-resource enabled="true" jndi-name="jdbc/dbpam_eclipselink" object-type="user" pool-name="dbpamEclipselinkConnectionPool">
<description/>
</jdbc-resource>
<jdbc-connection-pool allow-non-component-callers="false" associate-with-thread="false" connection-creation-retry-attempts="0" connection-creation-retry-interval-in-seconds="10" connection-leak-reclaim="false" connection-leak-timeout-in-seconds="0" connection-validation-method="auto-commit" datasource-classname="com.mysql.jdbc.jdbc2.optional.MysqlDataSource" fail-all-connections="false" idle-timeout-in-seconds="300" is-connection-validation-required="false" is-isolation-level-guaranteed="true" lazy-connection-association="false" lazy-connection-enlistment="false" match-connections="false" max-connection-usage-count="0" max-pool-size="32" max-wait-time-in-millis="60000" name="dbpamEclipselinkConnectionPool" non-transactional-connections="false" pool-resize-quantity="2" res-type="javax.sql.DataSource" statement-timeout-in-seconds="-1" steady-pool-size="8" validate-atmost-once-period-in-seconds="0" wrap-jdbc-objects="false">
<property name="URL" value="jdbc:mysql://localhost:3306/dbpam_eclipselink"/>
<property name="User" value="root"/>
<property name="Password" value=""/>
</jdbc-connection-pool>
</resources>
Файл [glassfish-resources.xml] — це файл XML, який містить усі дані, зібрані майстром. Він буде використовуватися NetBeans для того, щоб під час розгортання модуля EJB на сервері GlassFish ініціювати створення ресурсу JDBC, необхідного цьому модулю.
Тепер можна створити файл [persistence.xml], який налаштує рівень JPA модуля EJB:
![]() |
- у [1] створіть новий файл — перед виконанням цієї операції переконайтеся, що вибрано проект EJB
- у [2], проект EJB
- в [3] вибираємо категорію [Persistence]
- у [4] потрібно створити одиницю збереження
![]() |
- в [5], присвоїти ім’я одиниці збереження
- у [6] пропонується кілька реалізацій JPA. Тут виберемо [EclipseLink]. Інші реалізації можна використовувати за умови, що бібліотеки, які їх реалізують, будуть розміщені разом із бібліотеками сервера Glassfish.
- У випадаючому списку [7] виберіть щойно створене джерело даних JDBC [jdbc/dbpam_eclipselink].
- У [8] вкажіть, що транзакції обробляються контейнером EJB
- у [9] вкажіть, що під час розгортання модуля EJB на сервері жодних операцій з джерелом даних виконувати не слід. Дійсно, модуль EJB використовуватиме вже створену базу даних [dbpam_eclipselink].
- Наприкінці роботи майстра було створено файл [persistence.xml] [10]. Його вміст такий:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="mv-pam-ejb-metier-dao-eclipselinkPU" transaction-type="JTA">
<jta-data-source>jdbc/dbpam_eclipselink</jta-data-source>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties/>
</persistence-unit>
</persistence>
- рядок 3: ім’я модуля збереження даних [mv-pam-ejb-metier-dao-eclipselinkPU] та тип транзакцій (JTA для контейнера EJB)
- рядок 5: ім’я JNDI джерела даних, що використовується рівнем персистентності: jdbc/dbpam_eclipselink
- рядок 6: сутності JPA не вказані. Їх буде шукано в Classpath модуля EJB.
- назва використовуваної реалізації JPA (Hibernate, EclipseLink, ...) не вказана. У цьому випадку Glassfish v3 за замовчуванням використовує EclipseLink.
7.1.1.3. Вставлення шарів [jpa, DAO, metier]
Тепер, коли файл [persistence.xml] визначено, ми можемо перейти до вставлення в проект шарів [metier, dao, jpa] корпоративного додатка [pam]:
![]() |
Ці три шари ідентичні тим, що були у OpenEJB. Можна просто скопіювати та вставити їх між двома проектами. Саме це ми й робимо зараз:
![]() |
- у [1] — результат копіювання пакетів [jpa, dao, metier, exception] з проєкту [mv-pam-openejb-eclipselink] у модуль EJB [mv-pam-ejb-metier-dao-jpa-eclipselink]
7.1.1.4. Налаштування сервера Glassfish
Нам залишається налаштувати сервер Glassfish у двох аспектах:
- шар JPA реалізовано за допомогою EclipseLink. Потрібно переконатися, що сервер Glassfish має бібліотеки цієї реалізації JPA.
- джерелом даних є база даних MySQL. Необхідно переконатися, що сервер Glassfish має драйвер JDBC для цієї бази даних SGBD.
Відсутність цих бібліотек можна виявити під час розгортання модуля EJB. Ось один із способів додавання відсутніх бібліотек на сервер Glassfish:
![]() |
- у [1] перегляньте властивості сервера Glassfish
- у [2], занотуйте папку доменів сервера. Далі позначимо її як <domains>
- у папці <domains>\domain1\lib розмістіть відсутні бібліотеки. У цьому прикладі було додано бібліотеки Hibernate (lib / hibernate-tools) та драйвер JDBC з MySQL (lib / divers). За замовчуванням сервер Glassfish має бібліотеки EclipseLink. Тому ми додамо лише драйвер JDBC з MySQL.
![]() |
- у [1], на вкладці [Services] запускаємо сервер Glassfish v3
- у [2], він активний
7.1.1.5. Розгортання модуля EJB
Тепер ми розгортаємо модуль EJB на сервері Glassfish:
![]() |
- у [1], модуль EJB розгорнуто
- на [2], дерево сервера Glassfish оновлюється
- у [3], після розгортання модуль EJB з’являється у гілці [Applications] сервера Glassfish
- у [4] на сервері Glassfish було створено ресурс JDBC [jdbc / dbpam_eclipselink]. Нагадаємо, що ми його визначили в розділі 7.1.1.2.
Під час розгортання сервер Glassfish записує в консоль цікаву інформацію:
Зверніть увагу на рядки
- 3, 6, 8 та 11 назви переносних JNDI розгорнутих EJB. У Java EE 6 було введено поняття переносного імені JNDI. Це означає, що ім’я JNDI розпізнається всіма серверами Java EE 6. У Java EE 5 імена JNDI є специфічними для використовуваного сервера.
- 4, 7, 9, 12: імена JNDI модулів EJB, розгорнутих у формі, специфічній для Glassfish v3.
Ці імена знадобляться консольному додатку, який ми будемо писати для роботи з розгорнутим модулем EJB.
7.2. Консольний клієнт — версія 1
Тепер, коли ми розгорнули серверну частину нашого клієнт-серверного додатка, переходимо до вивчення клієнтської частини [1]:
![]() |
7.2.1. Проєкт клієнта
Ми створюємо новий проект Maven типу [Java Application] під назвою [mv-pam-client-ejb-metier-dao-eclipselink]:
![]() |
- у [1], проект клієнта
У файлі [pom.xml] додаємо такі залежності:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>istia.st</groupId>
<artifactId>mv-pam-client-ejb-metier-dao-eclipselink</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>mv-pam-client-ejb-metier-dao-eclipselink</name>
<url>http://maven.apache.org</url>
<repositories>
<repository>
<url>http://download.eclipse.org/rt/eclipselink/maven.repo/</url>
<id>eclipselink</id>
<layout>default</layout>
<name>Repository for library Library[eclipselink]</name>
</repository>
<repository>
<url>http://repo1.maven.org/maven2/</url>
<id>swing-layout</id>
<layout>default</layout>
<name>Repository for library Library[swing-layout]</name>
</repository>
</repositories>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.glassfish.appclient</groupId>
<artifactId>gf-client</artifactId>
<version>3.1.1</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>mv-pam-ejb-metier-dao-eclipselink</artifactId>
<version>${project.version}</version>
<type>ejb</type>
</dependency>
<dependency>
<groupId>org.swinglabs</groupId>
<artifactId>swing-layout</artifactId>
<version>1.0.3</version>
</dependency>
</dependencies>
</project>
- рядки 31–35: залежність від бібліотеки [gf-client], яка дозволяє клієнту Classfish взаємодіяти з віддаленим сервером,
- рядки 36–41: залежність від проекту Maven модуля EJB. Тут ми хочемо отримати визначення сутностей JPA та різних інтерфейсів, а також визначення класу винятків [PamException],
З проєкту [mv-pam-openejb-eclipselink] ми копіюємо клас [MainRemote]:
![]() |
Клас [MainRemote] повинен отримати посилання на EJB з рівня [metier]. Код класу [MainRemote] змінюється наступним чином:
// все гаразд — можна запитувати платіжну відомість
FeuilleSalaire feuilleSalaire = null;
IMetierRemote metier = null;
try {
// контекст JNDI сервера Glassfish
InitialContext initialContext = new InitialContext();
// інстанціювання бізнес-шару
metier = (IMetierRemote) initialContext.lookup("java:global/istia.st_mv-pam-ejb-metier-dao-eclipselink_ejb_1.0-SNAPSHOT/Metier!metier.IMetierRemote");
// розрахунок відомості про заробітну плату
feuilleSalaire = metier.calculerFeuilleSalaire(args[0], nbHeuresTravaillées, nbJoursTravaillés);
} catch (PamException ex) {
System.err.println("L'erreur suivante s'est produite : "
+ ex.getMessage());
return;
} catch (Exception ex) {
System.err.println("L'erreur suivante s'est produite : "
+ ex.toString());
return;
}
- рядок 6: ініціалізація контексту JNDI сервера Glassfish.
- рядок 8: у цього контексту JNDI запитується посилання на віддалений інтерфейс шару [metier]. Згідно з логами Glassfish, відомо, що віддалений інтерфейс шару [metier] може мати два можливі імена:
Рядок 1: ім’я JNDI, яке можна використовувати з будь-яким сервером додатків: JAVA, EE, 6. Рядок 2: ім’я JNDI, специфічне для Glassfish. У коді, у рядку 9, ми використовуємо переносиме ім’я JNDI.
- Решта коду залишається без змін
![]() |
У [1] ми налаштовуємо проєкт так, щоб він запускав клас [MainRemote] з аргументами. Якщо все гаразд, виконання проєкту дає такий результат:
Якщо у властивостях вказати неправильний номер соціального страхування, отримаємо такий результат:
7.3. Консольний клієнт — версія 2
У попередніх версіях середовище JNDI сервера Glassfish налаштовувалося на основі файлу [jndi.properties], який містився десь в архівах проєкту. Його вміст за замовчуванням такий:
# доступ JNDI до Sun Application Server
java.naming.factory.initial=com.sun.enterprise.naming.SerialInitContextFactory
java.naming.factory.url.pkgs=com.sun.enterprise.naming
# Необхідно додати javax.naming.spi.StateFactory для CosNaming, який
# підтримує динамічний RMI-IIOP.
java.naming.factory.state=com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl
org.omg.CORBA.ORBInitialHost=localhost
org.omg.CORBA.ORBInitialPort=3700
Рядки 7 і 8 вказують на машину служби JNDI та її порт прослуховування. Цей файл не дозволяє звертатися до сервера JNDI, відмінного від localhost, або до сервера, що працює на порту, відмінному від 3700. Якщо потрібно змінити ці два параметри, можна створити власний файл [jndi.properties] або використати конфігурацію Spring. Ми продемонструємо другий спосіб.
Почнемо зі створення нового проєкту на основі початкового проєкту [pam-client-metier-dao-jpa-eclipselink].
![]() |
- у [1], новий проєкт
- у [2] — файл конфігурації Spring [spring-config-client.xml]. Його вміст такий:
Файл конфігурації Spring має такий вигляд:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee-2.0.xsd">
<!-- професія -->
<jee:jndi-lookup id="metier" jndi-name="java:global/istia.st_mv-pam-ejb-metier-dao-eclipselink_ejb_1.0-SNAPSHOT/Metier!metier.IMetierRemote">
<jee:environment>
java.naming.factory.initial=com.sun.enterprise.naming.SerialInitContextFactory
java.naming.factory.url.pkgs=com.sun.enterprise.naming
java.naming.factory.state=com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl
org.omg.CORBA.ORBInitialHost=localhost
org.omg.CORBA.ORBInitialPort=3700
</jee:environment>
</jee:jndi-lookup>
</beans>
Тут ми використовуємо тег <jee> (рядок 14), який з’явився у Spring 2.0. Використання цього тегу вимагає визначення схеми, до якої він належить (рядки 4, 10 та 11).
- рядок 14: тег <jee:jndi-lookup> дозволяє отримати посилання на об’єкт із сервісу JNDI. Тут бін із назвою «metier» пов’язується з ресурсом JNDI, пов’язаним із EJB та [Metier]. Назва JNDI, що використовується тут, є переносимою назвою (Java EE 6) для EJB.
- Вміст файлу [jndi.properties] стає вмістом тегу <jee:environment> (рядок 15), який використовується для визначення параметрів підключення до служби JNDI.
Головний клас [MainRemote] змінюється наступним чином:
У рядках 7–8 до Spring надсилається запит на посилання типу [IMetierRemote] на рівні [metier]. Таке рішення забезпечує гнучкість нашої архітектури. Адже якщо EJB з шару [metier] стане локальним, c.a.d виконувався в тому самому JVM, що й наш клієнт [MainRemote], код останнього не змінився б. Зміниться лише вміст файлу [spring-config-client.xml]. У результаті ми отримаємо конфігурацію, аналогічну архітектурі Spring / JPA, розглянутій у розділі 5.11.
Пропонуємо читачеві випробувати цю нову версію.
7.4. Клієнт Swing
Тепер ми створюємо клієнт swing для нашого клієнт-серверного додатка EJB.
![]() |
Файл [pom.xml] повинен мати необхідні залежності від додатків Swing:
<dependency>
<groupId>org.swinglabs</groupId>
<artifactId>swing-layout</artifactId>
<version>1.0.3</version>
</dependency>
Вищезазначений клас [PamJFrame] спочатку був написаний для виконання в середовищі Spring / JPA:
![]() |
Тепер цей клас має стати віддаленим клієнтом класу EJB, розгорнутого на сервері Glassfish.
![]() |
Практичне завдання: слідуючи прикладу консольного клієнта [ui.console.MainRemote] з проекту, змініть спосіб, яким метод [doMyInit] (див. параграф 5.12.4) класу [PamJFrame] для отримання посилання на шар [metier], який тепер є віддаленим.

























