4. Веб-сервіс J2EE для зустрічей
Повернемося до архітектури додатка, який ми будемо створювати:
![]() |
У цьому розділі ми розглянемо створення веб-сервісу J2EE [1], що виконується на сервері Sun / Glassfish.
4.1. База даних
База даних, яку ми назвемо [dbrdvmedecins] , є базою даних MySQL5 із чотирма таблицями:

4.1.1. Таблиця [MEDECINS]
Вона містить інформацію про лікарів, які обслуговуються додатком [RdvMedecins].
![]() | ![]() |
- ID: номер, що ідентифікує лікаря — первинний ключ таблиці
- VERSION: номер, що ідентифікує версію рядка в таблиці. Це число збільшується на 1 щоразу, коли до рядка вносяться зміни.
- NOM: прізвище лікаря
- PRENOM: його ім’я
- TITRE: його/її титул (пані, пані, пан)
4.1.2. Таблиця [CLIENTS]
Пацієнти різних лікарів заносяться до таблиці [CLIENTS]:
![]() | ![]() |
- ID: номер, що ідентифікує пацієнта — первинний ключ таблиці
- VERSION: номер, що ідентифікує версію рядка в таблиці. Це число збільшується на 1 кожного разу, коли до рядка вносяться зміни.
- NOM: ім’я клієнта
- PRENOM: його ім’я
- TITRE: титул (пані, пані, пан)
4.1.3. Таблиця [CRENEAUX]
У ній перелічено часові проміжки, у яких можливі RV:
![]() |
![]() |
- ID: номер, що ідентифікує часовий проміжок — первинний ключ таблиці (рядок 8)
- VERSION: номер, що ідентифікує версію рядка в таблиці. Це число збільшується на 1 щоразу, коли до рядка вносяться зміни.
- ID_MEDECIN: номер, що ідентифікує лікаря, якому належить цей часовий проміжок — зовнішній ключ у стовпці MEDECINS (ID).
- HDEBUT: час початку часового проміжку
- MDEBUT: хвилини початку часового проміжку
- HFIN: година закінчення часового проміжку
- MFIN: хвилини закінчення інтервалу
Другий рядок таблиці [CRENEAUX] (див. [1] вище) вказує, наприклад, що слот № 2 починається о 8:20 і закінчується о 8:40 та належить лікарю № 1 (пані Марі PELISSIER).
4.1.4. Таблиця [RV]
У ній наведено перелік RV, призначених кожному лікарю:
![]() |
- ID: номер, що однозначно ідентифікує RV — первинний ключ
- JOUR: день RV
- ID_CRENEAU: часовий проміжок для RV — зовнішній ключ до поля [ID] у таблиці [CRENEAUX] — визначає як часовий проміжок, так і відповідного лікаря.
- ID_CLIENT: номер клієнта, для якого зроблено бронювання — зовнішній ключ до поля [ID] таблиці [CLIENTS]
Ця таблиця має обмеження унікальності ( ) для значень з’єднаних стовпців (JOUR, ID_CRENEAU):
Якщо рядок таблиці [RV] має значення (JOUR1, ID_CRENEAU1) для стовпців (JOUR, ID_CRENEAU), це значення не може зустрічатися більше ніде. Інакше це означало б, що одночасно було зареєстровано два записи RV для одного й того самого лікаря. З точки зору програмування на Java драйвер JDBC бази даних запускає SQLException, коли трапляється такий випадок.
Рядок id, що дорівнює 3 (див. [1] вище), означає, що 23.08.2006 було заброньовано RV для слоту № 20 та клієнта № 4. З таблиці [CRENEAUX] ми дізнаємося, що слот № 20 відповідає часовому проміжку 16:20–16:40 і належить лікарю № 1 (пані Марі PELISSIER). З таблиці [CLIENTS] випливає, що клієнт № 4 — це пані Бріжит BISTROU.
4.2. Створення бази даних
Створіть базу даних MySql [dbrdvmedecins] за допомогою інструменту на ваш вибір. Для створення таблиць та їх заповнення можна використати скрипт [createbd.sql], який вам буде надано. Його вміст такий:
4.3. Елементи архітектури на стороні сервера
Повернемося до архітектури додатка, який потрібно створити:
![]() |
На стороні сервера додаток складатиметься з:
- з JPA-шару, що дозволяє працювати з BD за допомогою об’єктів
- EJB, що відповідає за управління операціями з шаром JPA
- веб-сервісу, що відповідає за надання віддаленим клієнтам інтерфейсу EJB у вигляді веб-сервісу.
Елементи (b) та (c) реалізують шар [dao], зображений на попередній схемі. Відомо, що додаток може отримати доступ до віддаленого EJB через протоколи RMI та JNDI. На практиці це обмежує коло клієнтів лише клієнтами на Java. Веб-сервіс використовує стандартизований протокол зв’язку, який реалізують різні мови програмування: .NET, PHP, C++ тощо. Саме це ми й хочемо продемонструвати тут, використовуючи клієнт .NET.
Короткий вступ до веб-сервісів можна знайти в курсі [ref1], параграф 14, сторінка 109.
Веб-сервіс можна реалізувати двома способами:
- за допомогою класу з анотацією @WebService, який виконується у веб-контейнері
![]() |
- за допомогою EJB, анотованого @WebService, що виконується у контейнері EJB
![]() |
![]() |
Тут ми використаємо перший варіант:
У курсі [ref1], у параграфі 14 на сторінці 109, наведено приклад із використанням другого рішення.
4.4. Налаштування Hibernate для сервера Glassfish у
Залежно від версії, сервер Glassfish V2, що постачається разом із NetBeans, може не містити бібліотек Hibernate, необхідних для роботи шару JPA/Hibernate. Якщо під час проходження цього посібника ви виявите, що Glassfish не пропонує реалізацію JPA/Hibernate або під час розгортання служб з’являється виняток про те, що бібліотеки Hibernate не знайдено, вам слід додати ці бібліотеки до папки [<glassfish>/domains/domain1/lib/ext], а потім перезапустити сервер Glassfish:
![]() |
|
Бібліотеки Hibernate містяться в архіві, що додається до цього посібника.
4.5. Інструменти автоматичного генерування NetBeans
Повернемося до архітектури, яку нам потрібно побудувати:
![]() |
За допомогою NetBeans можна автоматично згенерувати шар [JPA] та шар [Ejb], який контролює доступ до згенерованих сутностей JPA. Цікаво ознайомитися з цими методами автоматичного генерування, оскільки згенерований код дає цінні підказки щодо того, як писати сутності JPA або EJB-код, який їх використовує.
Тепер ми опишемо деякі з цих інструментів автоматичного генерування. Щоб зрозуміти згенерований код, потрібно мати хороші знання про сутності JPA, [ref1], а також EJB і [ref2].
Створення з’єднання NetBeans із базою даних
- запустіть SGBD та MySQL 5, щоб BD став доступним
- Створити з’єднання NetBeans із базою даних [dbrdvmedecins]
![]() |
- на вкладці [Files], у гілці [Databases] [1], вибрати драйвер JDBC MySQL [2]
- потім виберіть опцію [3] «Connect Using», що дозволяє створити з’єднання з базою даних MySQL
- у [4] введіть необхідні дані
- потім підтвердіть у [5]
![]() |
- у [6] з’єднання створено. Тут відображаються чотири таблиці підключеної бази даних.
Створення проекту EJB
![]() |
- в [1], створіть нову програму, модуль EJB
- у [2], виберіть категорію [Java EE], а в [3] — тип [EJB Module]
![]() |
- у [4] виберіть папку для проекту, а в [5] надайте йому ім’я — потім завершіть роботу майстра
- у [6] — згенерований проєкт
Додавання ресурсу JDBC на сервер Glassfish
Ми додамо ресурс JDBC на сервер Glassfish.
![]() |
![]() |
- на вкладці [Services] запустіть сервер Glassfish [2, 3]
- у вкладці [Projects] клацніть правою кнопкою миші на проекті Ejb і в [5] виберіть опцію [New / Other], що дозволяє додати елемент до проекту.

- у [6], виберіть категорію [Glassfish], а в [7] вкажіть, що ви хочете створити ресурс JDBC, вибравши тип [JDBC Resource]
- у полі [8] вкажіть, що цей ресурс JDBC використовуватиме власний пул з’єднань
- у [9], надайте ім’я ресурсу JDBC
- у [10] перейти до наступного кроку
![]() |
- в [11] визначаються характеристики пулу з'єднань ресурсу JDBC
- у [12], надайте ім’я пулу з’єднань
- у [13], виберіть раніше створене з'єднання NetBeans [dbrdvmedecins]
- у [14] перейдіть до наступного кроку
- у [15], зазвичай на цій сторінці нічого змінювати не потрібно. Властивості з’єднання з базою даних MySQL [dbrdvmedecins] були скопійовані з властивостей раніше створеного з’єднання NetBeans [dbrdvmedecins]
- у [16], перейдіть до наступного кроку
![]() |
- у [17], залиште запропоновані значення за замовчуванням
- у [18], завершіть роботу майстра. Він створить файл [sun-resources.xml] [19], вміст якого є таким:
Вищезазначений файл містить всю інформацію, введену в майстрі, у форматі XML. Він буде використаний NetBeans для запиту до сервера GlassFish щодо створення ресурсу «jdbc/dbrdvmedecins», визначеного у рядку 4.
Створення модуля збереження даних
Модуль збереження даних [persistence.xml] налаштовує рівень JPA: він вказує використовувану реалізацію JPA (Toplink, Hibernate тощо) та налаштовує її.
![]() |
![]() |
- у [1] клацніть правою кнопкою миші на проекті Ejb і виберіть [New / Other] у [2]
- в [3], виберіть категорію [Persistence], а потім в [4] вкажіть, що ви хочете створити одиницю збереження JPA
![]() |
- у [5] надайте ім’я створеному модулю збереження
- в [6], виберіть [Hibernate] як реалізацію JPA
- у [7], вибрати щойно створений ресурс Glassfish «jdbc/dbrdvmedecins»
- у [8] вкажіть, що під час інстанціювання шару JPA з базою даних не слід виконувати жодних дій
- завершіть роботу майстра
- у [9] — файл [persistence.xml], створений майстром
Його вміст такий:
Знову ж таки, у форматі XML він повторює інформацію, вказану в майстрі. Цього файлу недостатньо для роботи з базою даних MySQL5 «dbrdvmedecins». Нам потрібно вказати Hibernate, який тип SGBD слід обробляти. Це буде зроблено пізніше.
Створення сутностей JPA
![]() |
![]() |
![]() |
- у [1] клацніть правою кнопкою миші на проекті, а в [2] виберіть опцію [New / Other]
- у [3] виберіть категорію [Persistence], а потім у [4] вкажіть, що ви хочете створити об’єкти JPA на основі існуючої бази даних.
![]() |
- у [5] виберіть джерело JDBC «jdbc/dbrdvmedecins», яке ми створили
- у [6] чотири таблиці пов’язаної бази даних
- у [7,8] включіть їх усі у процес генерації сутностей JPA
- у [9], продовжити роботу з майстром
![]() |
- у [10], сутності JPA, які будуть згенеровані
- в [11], надати ім’я пакету для об’єктів JPA
- в [12], вибрати тип Java, який буде інкапсулювати списки об’єктів, що повертаються шаром JPA
- завершіть роботу майстра
- у [13] — чотири згенеровані сутності JPA, по одній для кожної таблиці бази даних.
Ось, наприклад, код сутності [Rv], яка представляє рядок таблиці [rv] у базі [dbrdvmedecins].
Створення EJB-шару для доступу до сутностей JPA
![]() |
![]() |
- у [1] клацніть правою кнопкою миші на проекті, а в [2] виберіть опцію [New / Other]
- у [3], вибрати категорію [Persistence], а потім у [4] — тип [Session Beans for Entity Classes]
![]() |
- в [5] раніше створені об’єкти JPA відображаються
- у [6], виділіть їх усі
- у [7] — вони були виділені
- у [8], продовжити роботу з майстром
![]() |
- у [9], надайте ім’я пакету EJB, які будуть згенеровані
- в [10], вказати, що EJB мають реалізовувати як локальний, так і віддалений інтерфейс
- завершити роботу майстра
- у [11] — згенеровані EJB
Ось, наприклад, код EJB, який керує доступом до сутності [Rv], а отже, до таблиці [rv] у базі даних [dbrdvmedecins]:
Як уже зазначалося, автоматичне генерування коду може бути дуже корисним для запуску проєкту та ознайомлення з сутностями JPA та EJB. Далі ми перепишемо шари JPA та EJB, використовуючи власний код, але читач знайде в них інформацію, яку ми щойно розглянули під час автоматичного генерування шарів.
4.6. Проєкт NetBeans для модуля EJB
Ми створюємо новий порожній модуль EJB (див. розділ 4.5):
![]() |
- пакет [rdvmedecins.entites] об’єднує сутності шару JPA
- пакет [rdvmedecins.dao] реалізує EJB шару [dao]
- пакет [rdvmedecins.exceptions] реалізує клас винятків, специфічний для даного додатка
Далі ми припускаємо, що читач виконав усі кроки, описані в розділі 4.5. Деякі з них йому доведеться повторити.
4.6.1. Налаштування шару JPA
Нагадаємо архітектуру нашого клієнт-серверного додатка:
![]() |
Проєкт NetBeans:
![]() |
Рівень [JPA] налаштовується за допомогою файлів [persistence.xml] та [sun-resources.xml], наведених вище. Ці два файли генеруються майстрами, з якими ми вже знайомилися:
- створення файлу [sun-resources.xml] було описано в розділі 4.5.
- створення файлу [persistence.xml] описано в розділі 4.5.
Створений файл [persistence.xml] потрібно змінити наступним чином:
- рядок 3: тип транзакцій — JTA: транзакції будуть оброблятися контейнером Ejb3 сервера Glassfish
- рядок 4: використовується реалізація JPA / Hibernate. Для цього бібліотека Hibernate була додана до сервера Glassfish (див. параграф 4.4).
- рядок 5: джерело даних JTA, яке використовується рівнем JPA, має ім’я JNDI «jdbc/dbrdvmedecins».
- рядок 8: цей рядок не генерується автоматично. Його потрібно додати вручну. Він вказує Hibernate, що використовується SGBD — це MySQL5.
Джерело даних «jdbc/dbrdvmedecins» налаштовується у такому файлі [sun-resources.xml]:
- рядки 8–10: параметри JDBC джерела даних (URL бази даних, ім’я користувача та пароль). База даних MySQL dbrdvmedecins — це та, що описана в розділі 4.1.
- рядок 7: параметри пулу з’єднань, пов’язаного з цим джерелом даних
4.6.2. Елементи шару JPA
Нагадаємо архітектуру нашого клієнт-серверного додатка:
![]() |
Проєкт NetBeans:
![]() |
Пакет [rdvmedecins.entites] реалізує рівень [Jpa].
У розділі 4.5 ми розглянули, як автоматично генерувати сутності JPA для додатка. Тут ми не будемо використовувати цю техніку, а визначимо сутності самостійно. Однак вони будуть містити значну частину коду, згенерованого в розділі 4.5. У цьому випадку ми хочемо, щоб сутності [Medecin] та [Client] були дочірніми класами класу [Personne].
Клас Personne використовується для представлення лікарів та клієнтів:
- рядок 3: зверніть увагу, що клас [Personne] сам по собі не є сутністю (@Entity). Він буде батьківським класом для сутностей. Анотація @MappedSuperClass позначає цю ситуацію.
Ентітет [Client] інкапсулює рядки таблиці [clients]. Він походить від попереднього класу [Personne]:
- рядок 3: клас [Client] є сутністю JPA
- рядок 4: вона пов’язана з таблицею [clients]
- рядок 5: вона походить від класу [Personne]
Ентітет [Medecin], який інкапсулює рядки таблиці [medecins], побудований за тим самим зразком:
Об’єкт [Creneau] інкапсулює рядки таблиці [creneaux]:
- рядки 15–17 моделюють відношення «один до багатьох», яке існує між таблицею [creneaux] та таблицею [medecins] у базі даних.
Суть [Rv] інкапсулює рядки таблиці [rv]:
- рядки 15–17 моделюють відношення «один до багатьох», що існує між таблицею [rv] та таблицею [clients] у базі даних, а рядки 18–20 — відношення «один до багатьох», що існує між таблицею [rv] та таблицею [creneaux]
4.6.3. Клас винятків
![]() |
Клас винятків [RdvMedecinsException] додатка має такий вигляд:
- рядок 6: клас походить від класу [RuntimeException]. Тому компілятор не вимагає обробляти його за допомогою блоків try / catch.
- рядок 5: анотація @ApplicationException гарантує, що виняток не буде «поглинутий» винятком типу [EjbException].
Щоб зрозуміти анотацію @ApplicationException, повернемося до архітектури, що використовується на стороні сервера:
![]() |
Виняток типу [RdvMedecinsException] буде згенеровано методами EJB шару [dao] всередині контейнера EJB3 і перехоплено ним. Без анотації @ApplicationException контейнер EJB3 інкапсулює виникле виключення у виключення типу [EjbException] і перекидає його. Можливо, ви не бажаєте такого інкапсулювання і хочете, щоб з контейнера Ejb3 виходило виключення типу [RdvMedecinsException]. Саме це дозволяє зробити анотація @ApplicationException. Крім того, атрибут (rollback=true) цієї анотації вказує контейнеру EJB3, що якщо виняток типу [RdvMedecinsException] виникає всередині методу, що виконується в рамках транзакції з SGBD, то цю транзакцію слід скасувати. З технічної точки зору це називається «rollback» транзакції.
4.6.4. EJB шару [dao]
![]() |
![]() |
Java-інтерфейс [IDao] шару [dao] має такий вигляд:
Локальний інтерфейс [IDaoLocal] EJB просто успадковує попередній інтерфейс [IDao]:
Те саме стосується віддаленого інтерфейсу [IDaoRemote]:
EJB [DaoJpa] реалізує обидва інтерфейси — локальний і віддалений:
- у рядку 3 вказано, що віддалений EJB має назву «rdvmedecins.dao»
- у рядку 4 вказано, що всі методи EJB виконуються в рамках транзакції, яку керує контейнер EJB3.
- у рядку 5 показано, що EJB реалізує локальний та віддалений інтерфейси.
Повний код EJB виглядає так:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | |
- рядок 8: об’єкт EntityManager, який керує доступом до контексту персистентності. Під час інстанціювання класу це поле буде ініціалізовано контейнером EJB завдяки анотації @PersistenceContext у рядку 7.
- рядок 15: запит JPQL, який повертає всі рядки таблиці [clients] у вигляді списку об’єктів [Client].
- рядок 22: аналогічний запит для лікарів
- рядок 32: запит JPQL, що виконує з'єднання таблиць [creneaux] та [medecins]. Він параметризується за ідентифікатором лікаря.
- рядок 43: запит JPQL, що виконує з'єднання між таблицями [rv], [creneaux] та [medecins] і має два параметри: ідентифікатор лікаря та день прийому.
- рядки 55–57: створення запису про прийом та його збереження в базі даних.
- рядок 67: видалення запису про прийом із бази даних.
- рядок 76: виконує запит SELECT до бази даних для пошуку певного клієнта
- рядок 85: те саме для лікаря
- рядок 94: те саме для запису на прийом
- рядок 103: те саме для часового проміжку
- усі операції з контекстом збереження даних (em) у рядку 9 можуть спричинити проблему з базою даних. Тому всі вони обгорнуті блоком try/catch. Можливе виключення інкапсульовано у «внутрішнє» виключення RdvMedecinsException.
Після компіляції модуль EJB створює файл .jar з іменем :
![]() |
4.7. Розгортання EJB шару [dao] за допомогою NetBeans
NetBeans дозволяє легко розгорнути раніше створений EJB на сервері GlassFish.
![]() |
- У властивостях проекту EJB перевірте параметри виконання [1].
- У [2] вкажіть ім’я сервера, на якому буде розгорнуто EJB
- на вкладці [Services] [3] запускаємо його [4].
![]() |
- у [5] — сервер Glassfish після запуску. На ньому ще немає модуля EJB.
- Запустіть сервер MySQL і переконайтеся, що база даних [dbrdvmedecins] працює. Для цього можна скористатися підключенням NetBeans, створеним у розділі 4.5.
- На вкладці [Projects] [6] розгортаємо модуль EJB [7]: необхідно, щоб SGBD MySQL5 був запущений, щоб ресурс JDBC «jdbc/dbrdvmedecins», який використовується EJB, був доступний.
- У [8] розгорнутий EJB відображається в дереві сервера GlassFish
![]() |
- У [9] розгорнутий EJB видаляється
- у [10] EJB більше не відображається в дереві сервера GlassFish.
4.8. Розгортання EJB з рівня [dao] за допомогою GlassFish
Тут ми покажемо, як розгорнути EJB на сервері GlassFish із його архіву .jar.
- Запустіть сервер MySQL і переконайтеся, що база даних [dbrdvmedecins] працює. Для цього можна скористатися підключенням NetBeans, створеним у розділі 4.5.
Нагадаємо конфігурацію JPA модуля EJB, який буде розгорнуто. Ця конфігурація налаштовується у файлі [persistence.xml]:
Рядок 5 вказує, що шар Jpa використовує джерело даних JTA, c.a.d, яке управляється контейнером Ejb3 і має назву «jdbc/dbrdvmedecins».
У розділі 4.5 ми розглянули, як створити цей ресурс JDBC за допомогою NetBeans. Тут ми покажемо, як це зробити безпосередньо в GlassFish. Ми дотримуємося процедури, описаної в розділі 13.1.2, на сторінці 79 [ref1].
Спочатку видалимо ресурс, щоб мати змогу створити його заново. Зробимо це за допомогою NetBeans:
![]() |
- у [1], ресурси JDBC сервера Glassfish
- на [2], ресурс «jdbc/dbrdvmedecins» нашого EJB
- у [3], пул з’єднань цього ресурсу JDBC
![]() |
- в [4] видаляється пул з'єднань. Це призведе до видалення всіх ресурсів JDBC, які його використовують, а отже, і ресурсу «jdbc/dbrdvmedecins».
- У [5] та [6] ресурс JDBC та пул з’єднань були знищені.
Тепер ми використовуємо консоль адміністрування сервера Glassfish для створення ресурсу JDBC та розгортання EJB.
![]() |
- на вкладці [services] [1] у NetBeans, запустіть сервер GlassFish [2], а потім перейдіть [3] до його консолі адміністрування
- за адресою [4], увійдіть як адміністратор (пароль: adminadmin, якщо ви не змінювали його під час встановлення або пізніше).
![]() |
- в [5] виберіть гілку [Connection Pools] ресурсів Glassfish
- у [6] створіть новий пул з'єднань. Нагадуємо, що пул з'єднань — це технічний засіб для обмеження кількості відкриттів/закриттів з'єднань із SGBD. Під час запуску сервера N (число, визначене в конфігурації) з’єднань відкриваються з SGBD. Ці відкриті з’єднання потім надаються EJB, які їх запитують для виконання операції з SGBD. Як тільки операція завершується, EJB повертає з’єднання до пулу. З’єднання ніколи не закривається. Воно спільно використовується різними потоками, які звертаються до SGBD
- у [7], надайте ім’я пулу
- в [8], клас, що моделює джерело даних, — це клас [javax.sql.DataSource]
- в [9], SGBD, що містить джерело даних, тут — MySQl.
- у [10] перейдіть до наступного кроку
![]() |
- у [11] атрибут «Connection Validation Required» забезпечує те, що перед наданням з’єднання пул перевіряє, чи воно працює. Якщо це не так, він створює нове. Це дозволяє додатку продовжувати працювати після тимчасового розриву з’єднання з SGBD. Під час перерви жодне з'єднання не є доступним, і клієнту надсилаються винятки. Коли збій закінчується, клієнти, які продовжують запитувати з’єднання, знову їх отримують: завдяки атрибуту «Connection Validation Required» усі з’єднання пулу будуть відтворені. Без цього атрибуту пул констатував би, що початкові з’єднання було розірвано, але не намагався б створити нові.
- У [12] задається рівень ізоляції «Read Committed» для транзакцій. Цей рівень гарантує, що транзакція T2 не зможе зчитувати дані, змінені транзакцією T1, доки остання не буде повністю завершена.
- У транзакції [13] вимагається, щоб усі транзакції використовували рівень ізоляції, зазначений у транзакції [12]
![]() |
- у [14] та [15] вкажіть URL-адресу BD, з’єднання з яким обслуговує пул
- у [16], користувачем буде root
- у [17] додайте властивість
- у [18] додайте властивість «Password» із значенням () у [19]. Хоча на скріншоті [19] цього не показано, для позначення порожнього пароля не слід вказувати порожній рядок, а саме () (відкриваюча дужка, закриваюча дужка). Якщо користувач root у вашому SGBD MySQL має непорожній пароль, вкажіть цей пароль.
- у [20], завершіть роботу майстра створення пулу з’єднань для бази даних MySQL [dbrdvmedecins].
![]() |
- у [21] пул створено. Клацніть на його посилання.
- у [22] кнопка [Ping] дозволяє створити з’єднання з базою даних [dbrdvmedecins]
- у [23], якщо все гаразд, з’явиться повідомлення про те, що з’єднання встановлено
Після створення пулу з’єднань можна створити ресурс JDBC:
![]() |
- у [1], вибираємо гілку [JDBC Resources] у дереві об’єктів сервера
- у [2] створюємо новий ресурс JDBC
- у [3] надаємо ім’я ресурсу JDBC. Воно має відповідати імені, використаному у файлі [persistence.xml]:
- у [4] вказується пул з'єднань, який має використовувати новий ресурс JDBC: той, що щойно було створено
- у файлі [5] завершуємо роботу майстра створення
![]() |
- у [6] новий ресурс JDBC
Тепер, коли ресурс JDBC створено, можна розгорнути архів jar EJB:
![]() |
- у [1], виберіть гілку [Enterprise Applications]
- у [2], за допомогою кнопки [Deploy] вкажіть, що ви хочете розгорнути нову програму
- у [3] вкажіть, що додаток є модулем EJB
- у [4] виберіть JAR-файл EJB [serveur-ejb-dao-jpa-hibernate.jar], який вам надали для TP.
- у [5] ви можете змінити назву модуля EJB, якщо бажаєте
- у [6] завершіть роботу майстра розгортання модуля EJB
![]() |
- У [7] було розгорнуто модуль EJB. Тепер ним можна користуватися.
4.9. Тестування EJB шару [dao]
Тепер, коли EJB шару [dao] нашого додатка розгорнуто, ми можемо його протестувати. Ми зробимо це за допомогою такого Java-клієнта:
![]() |
Клас [MainTestsDaoRemote] [1] є тестовим класом JUnit 4. Бібліотеки в [2] складаються, з одного боку, з:
- jar-файлу EJB шару [dao] [3] (див. розділ 4.6.4).
- бібліотек GlassFish [4], необхідних для віддалених клієнтів EJB.
Тестовий клас має такий вигляд:
- рядок 13: зверніть увагу на створення екземпляра проксі віддаленого EJB. Використовується його ім’я JNDI «rdvmedecins.dao».
- Тестові методи використовують методи, надані EJB (див. розділ 4.6.4).
Якщо все гаразд, тести мають пройти успішно:
![]() |
Тепер, коли EJB шару [dao] працює, можна перейти до його публічного оприлюднення через веб-сервіс.
4.10. Веб-сервіс шару [dao]
Для короткого вступу до поняття веб-сервісу див. параграф 14, сторінка 111 у [ref1].
Повернемося до архітектури сервера нашого клієнт-серверного додатка:
![]() |
Вище ми розглядаємо веб-сервіс рівня [dao]. Єдина роль цього сервісу полягає в тому, щоб зробити інтерфейс EJB рівня [dao] доступним для багатоплатформних клієнтів, здатних взаємодіяти з веб-сервісом.
Нагадаємо, що існує два способи реалізації веб-сервісу:
- за допомогою класу з анотацією @WebService, який виконується у веб-контейнері
![]() |
- за допомогою EJB, анотованого @WebService, що виконується у контейнері EJB
![]() |
Тут ми використовуємо перший варіант. У NetBeans IDE нам потрібно створити корпоративний проєкт із двома модулями:
- модуль EJB, який виконуватиметься в контейнері EJB: EJB рівня [dao].
- веб-модуль, який виконуватиметься у веб-контейнері: веб-сервіс, який ми зараз створюємо.
Ми будемо створювати цей корпоративний проєкт двома способами.
4.10.1. Проєкт NetBeans — версія 1
Спочатку створюємо проект NetBeans типу «Web Application»:
![]() |
- у [1] створюємо новий проєкт у категорії «Java Web» [2] типу «Веб-додаток» [3].
![]() |
- у [4] надаємо проекту ім’я, а у [5] вказуємо папку, в якій його слід згенерувати
- у [6] вказується сервер додатків, який буде виконувати веб-додаток
- у [7] задається контекст додатка
- у [8] підтверджуємо конфігурацію проєкту.
![]() |
- у [9] — згенерований проект. Веб-сервіс, який ми створюємо, використовуватиме EJB з попереднього проєкту [10]. Тому йому потрібно вказати посилання на .jar-файл модуля EJB [10].
- У [11] додаємо проект NetBeans до бібліотек веб-проекту [12]
![]() |
- у [13] вибираємо папку модуля EJB у файловій системі та підтверджуємо вибір.
![]() |
- у [14] модуль EJB було додано до бібліотек веб-проєкту.
У [15] ми реалізуємо веб-сервіс за допомогою наступного класу [WsDaoJpa]:
- у рядку 4 клас [WsdaoJpa] реалізує інтерфейс [IDao]. Нагадаємо, що цей інтерфейс визначено в архіві EJB шару [dao] у такому вигляді:
- рядок 3: анотація @WebService перетворює клас [WsDaoJpa] на веб-сервіс.
- рядки 6–7: посилання на EJB шару [dao] буде вставлено сервером додатків у поле рядка 7. Нагадаємо, що таким чином завжди вводиться локальна реалізація (у даному випадку — IDaoLocal). Це введення можливе, оскільки веб-сервіс виконується в тій самій JVM, що й EJB.
- Усі методи веб-сервісу позначені анотацією @WebMethod, щоб зробити їх видимими для віддалених клієнтів. Метод, не позначений анотацією @WebMethod, був би внутрішнім для веб-сервісу і невидимим для віддалених клієнтів. Кожен метод M веб-сервісу просто викликає відповідний метод M EJB, введений у рядку 7.
Створення цього веб-сервісу відображається новою гілкою в проєкті NetBeans:
![]() |
У [1] ми бачимо веб-сервіс WsDaoJpa, а в [2] — методи, які він надає віддаленим клієнтам.
Нагадаємо архітектуру веб-сервісу, що розробляється:
![]() |
Компоненти веб-сервісу, який ми збираємося розгорнути, такі:
- [1]: веб-модуль, який ми щойно створили
- [2]: модуль EJB, який ми створили на попередньому етапі і від якого залежить веб-сервіс
Щоб розгорнути їх разом, потрібно об’єднати обидва модулі в так званий «корпоративний» проєкт NetBeans:
![]() |
У [1] створюємо новий корпоративний проєкт [2, 3].
![]() |
- У [4,5] надаємо проекту ім’я та вказуємо папку для його створення
- у [6] вибирають сервер додатків, на якому буде розгорнуто корпоративний додаток
- у [7] зазначено, що корпоративний проєкт може мати три компоненти: веб-додаток, модуль EJB, клієнтський додаток. У цьому випадку проєкт створюється без жодних компонентів. Їх буде додано пізніше.
![]() |
- у [8] — щойно створений корпоративний додаток.
![]() |
- у [9] клацніть правою кнопкою миші на [Java EE Modules] і додайте новий модуль
- у [10] відображаються лише ті модулі NetBeans, які наразі відкриті в IDE. Тут ми вибираємо веб-модуль [serveur-webservice-1-ejb-dao-jpa-hibernate] та модуль EJB [serveur-ejb-dao-jpa-hibernate], які ми створили.
- У [11] — обидва модулі, додані до корпоративного проєкту.
Залишилося розгорнути цей корпоративний додаток на сервері Glassfish. Далі потрібно запустити SGBD та MySQL, щоб забезпечити доступ до джерела даних JDBC «jdbc/dbrdvmedecins», яке використовує модуль EJB.
![]() |
- у [1] запускається сервер Glassfish
- якщо модуль EJB [serveur-ejb-dao-jpa-hibernate] розгорнуто, його вивантажують [2]
- у [3] розгортається корпоративний додаток
![]() |
- у [4] — вона розгорнута. Бачимо, що вона містить обидва модулі: Web та EJB.
4.10.2. Проєкт NetBeans — версія 2
Тепер ми покажемо, як розгорнути веб-сервіс, коли у нас немає вихідного коду модуля EJB, а є лише його архів .jar.
Новий проект NetBeans для веб-сервісу матиме такий вигляд:
![]() |
Серед важливих елементів проєкту слід виділити такі:
- [1]: веб-сервіс реалізовано за допомогою проекту NetBeans типу [Web Application].
- [2]: веб-сервіс реалізовано за допомогою класу [WsDaoJpa], який ми вже розглядали
- [3]: архів EJB шару [dao], який дозволяє класу [WsDaoJpa] отримати доступ до визначень різних класів, інтерфейсів та сутностей шарів [dao] та [jpa].
Далі ми створюємо корпоративний проєкт, необхідний для розгортання веб-сервісу:
![]() |
- [1], створюємо корпоративний додаток [ea-rdvmedecins], спочатку без жодного модуля.
- у [2] додаємо попередній веб-модуль [serveur-webservice-ejb-dao-jpa-hibernate]
- у [3] — результат.
У такому вигляді корпоративний додаток [ea-rdvmedecins] не можна розгорнути на сервері Glassfish із NetBeans. Виникає помилка. Тому потрібно вручну розгорнути архів EAR додатка [ea-rdvmedecins]:
![]() |
- архів [ea-rdvmedecins.ear] знаходиться в папці [dist] [2] на вкладці [Files] у NetBeans.
- У цьому архіві [3] містяться два елементи корпоративного додатка:
- архів EJB [serveur-ejb-dao-jpa-hibernate]. Цей архів присутній, оскільки він входив до складу бібліотек, на які посилається веб-сервіс.
- архів веб-сервісу [serveur-webservice- ejb-dao-jpa-hibernate].
- архів [ea-rdvmedecins.ear] складається з простого Build [4] корпоративного додатка.
- У [5] операція розгортання завершується з помилкою.
Щоб розгорнути архів [ea-rdvmedecins.ear] корпоративного додатка, ми діємо так, як було показано під час розгортання архіву EJB [serveur-ejb-dao-jpa-hibernate.jar] у розділі 4.2. Ми знову використовуємо веб-клієнт адміністрування сервера GlassFish. Ми не повторюємо кроки, що вже були описані.
Спочатку «вивантажимо» корпоративний додаток, розгорнутий у розділі 4.10.1:
![]() |
- [1]: виберіть гілку [Enterprise Applications] сервера Glassfish
- у [2] виберіть корпоративний додаток, який потрібно вивантажити, а потім у [3] вивантажте його
- у [4] корпоративний додаток було вивантажено
![]() |
- у [1] виберіть гілку [Enterprise Applications] сервера Glassfish
- у [2], розгорніть новий корпоративний додаток
- у [3] виберіть тип [Enterprise Application]
- у [4] вкажіть файл .ear проекту NetBeans [ea-rdvmedecins]
- у [5] розгорніть цей архів
![]() |
- в [6], додаток було розгорнуто
- у [7], веб-сервіс [WsDaoJpa] з’являється у гілці [Web Services] сервера Glassfish. Його потрібно вибрати.
- У [8] міститься різноманітна інформація про веб-сервіс. Найцікавішою для клієнта є інформація [9]: URI веб-сервісу.
- У [10] можна протестувати веб-сервіс
![]() |
- У [11] наведено URI веб-сервісу , до якого додано параметр ?tester. Цей URI відкриває тестову сторінку. Усі методи (@WebMethod), що надаються веб-сервісом, відображаються та можуть бути протестовані. Тут ми тестуємо метод [13], який запитує список клієнтів.
![]() |
- У [14] ми показуємо лише частину сторінки відповіді. Але можна побачити, що метод getAllClients дійсно повернув список клієнтів. Знімок екрана показує, що відповідь надсилається у форматі XML.
Веб-сервіс повністю описується файлом XML, який називається файлом WSDL:
![]() |
- у [1] у веб-інструменті адміністрування сервера Glassfish виберіть веб-сервіс [WsDaoJpa]
- на [2], перейдіть за посиланням [View WSDL]
![]() |
- на [3]: URI файлу WSDL. Цю інформацію важливо знати. Вона необхідна для налаштування клієнтів цього веб-сервісу.
- [4] — опис веб-сервісу XML. Ми не будемо коментувати цей складний вміст.
4.10.3. Тестування веб-сервісу JUnit
Ми створюємо проект NetBeans, щоб «відтворити» тести, які вже виконувалися з клієнтом EJB, але цього разу з клієнтом для нещодавно розгорнутого веб-сервісу. Тут ми дотримуємося підходу, аналогічного тому, що описано в параграфі 14.2.1, на сторінці 115 документа [ref1].
![]() |
- у [1] — класичний Java-проект
- у [2] — тестовий клас
- у [3] клієнт використовує архів EJB для доступу до визначень інтерфейсу шару [dao] та сутностей JPA. Нагадаємо, що цей архів знаходиться у підпапці [dist] папки модуля EJB.
Для доступу до віддаленого веб-сервісу необхідно згенерувати проксі-класи:
![]() |
На наведеному вище схематичному зображенні шар [2] [C=Client] взаємодіє з шаром [1] [S=Serveur]. Щоб взаємодіяти з рівнем [S], клієнт [C] повинен встановити мережеве з’єднання з рівнем [S] і взаємодіяти з ним за певним протоколом. Мережеві з’єднання мають ідентифікатори TCP, а транспортний протокол — HTTP. Рівень [S], що представляє веб-сервіс, реалізовано за допомогою Java-сервлета, який виконується на сервері Glassfish. Ми не писали цей сервлет. Його генерація автоматизована Glassfish на основі анотацій @Webservice та @WebMethod класу [WsDaoJpa], який ми написали. Так само ми автоматизуємо генерацію клієнтського рівня [C]. Іноді шар [C] називають проксі-шаром віддаленого веб-сервісу, оскільки термін proxy позначає проміжний елемент у програмному ланцюжку. У даному випадку проксі C є посередником між клієнтом, який ми збираємося написати, та веб-сервісом, який ми розгорнули.
У NetBeans 6.5 проксі C можна згенерувати таким чином (для подальших дій веб-сервіс має бути активним на сервері GlassFish):
![]() |
- у [1] додати новий елемент до проекту Java
- у [2] виберіть гілку [Web services]
- у [3] виберіть [Web Service Client]
![]() |
- у [4] вкажіть URI файлу WSDL веб-сервісу. Цей URI наведено в розділі 4.10.2.
- у полі [5] залиште значення за замовчуванням [JAX-WS]. Іншим можливим значенням є [JAX-RPC]
- після підтвердження роботи майстра створення проксі веб-сервісу проект NetBeans було доповнено гілкою [Web Service References] [6]. Ця гілка відображає методи, що надаються віддаленим веб-сервісом.
![]() |
- на вкладці [Files] [7] було додано вихідний код Java [8]. Він відповідає згенерованому проксі на мові C.
- У [9] наведено код одного з класів. З нього видно, що [10] ці класи було розміщено в пакеті [rdvmedecins.ws]. Ми не будемо коментувати код цих класів, який знову є досить складним.
Для Java-клієнта, який ми зараз створюємо, згенерований проксі-код на C слугує посередником. Щоб отримати доступ до методу M віддаленого веб-сервісу, Java-клієнт викликає метод M проксі-коду на C. Таким чином, Java-клієнт викликає локальні методи (які виконуються в тій самій JVM), і ці локальні виклики прозоро для нього перетворюються на віддалені виклики.
Залишається з’ясувати, як викликати методи M проксі-класу на мові C. Повернімося до нашого тестового класу JUnit:
![]() |
У [1] тестовий клас [MainTestsDaoRemote] — це той самий клас, що вже використовувався під час тестування EJB шару [dao]:
- у рядку [13] тест test1 збережено без змін.
- у рядку [9] вміст методу [init] було видалено.
На даному етапі у проєкті є помилки, оскільки тестовий метод [test1] використовує сутності [Client], [Medecin], [Creneau], [Rv], які більше не знаходяться в тих самих пакетах, що й раніше. Вони знаходяться в пакеті згенерованого проксі-коду на мові C. Видаляємо відповідні інструкції import і перегенеруємо їх за допомогою операції «Fix Imports».
![]() |
Повернемося до коду тестового класу [MainTestsDaoRemote]:
Метод [init] у рядку 10 повинен ініціалізувати посилання на шар [dao] у рядку 7. Нам потрібно знати, як використовувати згенерований проксі-код C у нашому коді. NetBeans допомагає нам у цьому.
![]() |
- у [1] виберіть метод [getAllClients] веб-сервісу та, за допомогою миші, перетягніть цей метод у метод [init] тестового класу.
Отримуємо результат [2]. Цей шаблон коду показує, як використовувати згенерований проксі-код на C:
- рядок [5] показує, що метод [getAllClients] є методом об’єкта типу [WsDaoJpa], визначеного у рядку 3. Тип [WsDaoJpa] — це інтерфейс, що містить ті самі методи, що й ті, які надає віддалений веб-сервіс.
- У рядку [3] об’єкт [WsDaoJpa port] отримується з іншого об’єкта типу [WsDaoJpaService], визначеного в рядку 2. Тип [WsDaoJpaService] представляє проксі на мові C, згенерований локально.
- Доступ до віддаленої веб-служби може завершитися невдачею, тому весь код обгорнуто блоком try/catch.
- Об’єкти проксі-сервера на мові C містяться в пакеті [rdvmedecins.ws]
Зрозумівши цей код, можна побачити, що локальний ідентифікатор віддаленого веб-сервісу можна отримати за допомогою такого коду:
Тоді код тестового класу JUnit виглядає так:
Тепер ми готові до тестування:
![]() |
У [1] виконується тест JUnit. У [2] він проходить успішно. Якщо подивитися на вивід у консолі NetBeans, можна побачити такі рядки:
Liste des clients :
rdvmedecins.ws.Client@1982fc1
rdvmedecins.ws.Client@676437
rdvmedecins.ws.Client@1e4853f
rdvmedecins.ws.Client@1e808ca
На стороні сервера суть [Client] має метод toString, який відображає різні поля об’єкта типу [Client]. Під час автоматичного генерування проксі-коду на C об’єкти створюються в проксі-коді на C, але лише з приватними полями та відповідними методами get / set. Таким чином, метод toString не був згенерований в об’єкті [Client] проксі-коду на C. Це пояснює попереднє відображення. Це ніяк не впливає на результат тесту JUnit: він пройшов успішно. Відтепер вважатимемо, що веб-сервіс працює.






























































































