4. Usługa internetowa J2EE dotycząca spotkań
Wróćmy do architektury tworzonej aplikacji:
![]() |
W tej części skupimy się na tworzeniu serwisu internetowego J2EE [1] działającego na serwerze Sun / Glassfish.
4.1. Baza danych
Baza danych, którą nazwiemy [dbrdvmedecins] , jest bazą danych MySQL5 zawierającą cztery tabele:

4.1.1. Tabela [MEDECINS]
Zawiera informacje o lekarzach obsługiwanych przez aplikację [RdvMedecins].
![]() | ![]() |
- ID: numer identyfikacyjny lekarza – klucz główny tabeli
- VERSION: numer identyfikujący wersję wiersza w tabeli. Liczba ta jest zwiększana o 1 za każdym razem, gdy wprowadzana jest zmiana w wierszu.
- NOM: nazwisko lekarza
- PRENOM: jego imię
- TITRE: tytuł (panna, pani, pan)
4.1.2. Tabela [CLIENTS]
Pacjenci poszczególnych lekarzy są zapisani w tabeli [CLIENTS]:
![]() | ![]() |
- ID: numer identyfikacyjny pacjenta – klucz główny tabeli
- VERSION: numer identyfikujący wersję wiersza w tabeli. Liczba ta jest zwiększana o 1 za każdym razem, gdy wprowadzana jest zmiana w wierszu.
- NOM: nazwisko klienta
- PRENOM: imię klienta
- TITRE: tytuł (panna, pani, pan)
4.1.3. Tabela [CRENEAUX]
Zawiera listę przedziałów czasowych, w których możliwe są operacje RV:
![]() |
![]() |
- ID: numer identyfikujący przedział czasowy – klucz główny tabeli (wiersz 8)
- VERSION: numer identyfikujący wersję wiersza w tabeli. Liczba ta jest zwiększana o 1 za każdym razem, gdy wprowadzana jest zmiana w wierszu.
- ID_MEDECIN: numer identyfikujący lekarza, do którego należy ten przedział czasowy – klucz obcy w kolumnie MEDECINS (ID).
- HDEBUT: godzina rozpoczęcia terminu
- MDEBUT: minuty początku przedziału czasowego
- HFIN: godzina zakończenia przedziału czasowego
- MFIN: minuty zakończenia przedziału czasowego
Drugi wiersz tabeli [CRENEAUX] (por. [1] powyżej) wskazuje na przykład, że przedział nr 2 rozpoczyna się o godz. 8:20 i kończy o godz. 8:40 oraz należy do lekarza nr 1 (pani Marie PELISSIER).
4.1.4. Tabela [RV]
Zawiera listę RV przypisanych każdemu lekarzowi:
![]() |
- ID: numer jednoznacznie identyfikujący RV – klucz główny
- JOUR: dzień RV
- ID_CRENEAU: przedział czasowy dla RV – klucz obcy w polu [ID] tabeli [CRENEAUX] – określa zarówno przedział czasowy, jak i danego lekarza.
- ID_CLIENT: numer klienta, dla którego dokonano rezerwacji – klucz obcy w polu [ID] tabeli [CLIENTS]
Ta tabela posiada , która wymusza unikalność wartości połączonych kolumn (JOUR, ID_CRENEAU):
Jeśli wiersz w tabeli [RV] ma wartość (JOUR1, ID_CRENEAU1) w kolumnach (JOUR, ID_CRENEAU), to ta wartość nie może występować nigdzie indziej. W przeciwnym razie oznaczałoby to, że dwa rekordy o wartości RV zostały zarejestrowane w tym samym czasie dla tego samego lekarza. Z punktu widzenia programowania w Javie sterownik bazy danych o wartości JDBC uruchamia sterownik o wartości SQLException, gdy wystąpi taka sytuacja.
Wiersz o wartości 3 dla id (por. [1] powyżej) oznacza, że rezerwacja o numerze RV została dokonana na przedział czasowy nr 20 dla klienta nr 4 w dniu 23.08.2006 r. Z tabeli [CRENEAUX] wynika, że przedział nr 20 odpowiada przedziałowi czasowemu 16:20–16:40 i należy do lekarza nr 1 (pani Marie PELISSIER). Z tabeli [CLIENTS] wynika, że klient nr 4 to panna Brigitte BISTROU.
4.2. Tworzenie bazy danych
Utwórz bazę danych o nazwie MySql [dbrdvmedecins] za pomocą wybranego narzędzia. Do utworzenia tabel i wypełnienia ich danymi można wykorzystać skrypt [createbd.sql], który zostanie Państwu dostarczony. Jego treść jest następująca:
4.3. Elementy architektury po stronie serwera
Wróćmy do architektury tworzonej aplikacji:
![]() |
Po stronie serwera aplikacja będzie składać się z:
- warstwy JPA umożliwiającej pracę z BD za pomocą obiektów
- z EJB odpowiedzialnego za zarządzanie operacjami z warstwą JPA
- z serwisu internetowego odpowiedzialnego za udostępnianie zdalnym klientom interfejsu EJB w postaci serwisu internetowego.
Elementy (b) i (c) implementują warstwę [dao] przedstawioną na poprzednim schemacie. Wiadomo, że aplikacja może uzyskać dostęp do zdalnego EJB za pośrednictwem protokołów RMI i JNDI. W praktyce ogranicza to klientów do klientów Java. Usługa internetowa wykorzystuje znormalizowany protokół komunikacyjny, który jest implementowany przez różne języki programowania: .NET, PHP, C++, ... Właśnie to chcemy tutaj pokazać, korzystając z klienta .NET.
Krótkie wprowadzenie do usług internetowych można znaleźć w kursie [ref1], akapit 14, strona 109.
Usługę internetową można zaimplementować na dwa sposoby:
- za pomocą klasy opatrzonej adnotacją @WebService, która działa w kontenerze internetowym
![]() |
- poprzez komponent EJB opatrzony adnotacją @WebService, który działa w kontenerze EJB
![]() |
![]() |
W tym przypadku zastosujemy pierwsze rozwiązanie:
W kursie [ref1], w akapicie 14 na stronie 109, znajduje się przykład wykorzystujący drugie rozwiązanie.
4.4. Konfiguracja serwera Glassfish z em Hibernate
W zależności od wersji serwer Glassfish V2 dostarczany wraz z NetBeans może nie zawierać bibliotek Hibernate, których wymaga warstwa JPA / Hibernate. Jeśli w dalszej części samouczka okaże się, że GlassFish nie oferuje implementacji JPA/Hibernate lub że podczas wdrażania usług pojawi się wyjątek wskazujący, że nie znaleziono bibliotek Hibernate, należy dodać te biblioteki do folderu [<glassfish>/domains/domain1/lib/ext], a następnie ponownie uruchomić serwer GlassFish:
![]() |
|
Biblioteki Hibernate znajdują się w pliku ZIP dołączonym do samouczka.
4.5. Narzędzia do automatycznego generowania w NetBeans
Wróćmy do architektury, którą musimy zbudować:
![]() |
Za pomocą NetBeans można automatycznie wygenerować warstwę [JPA] oraz warstwę [Ejb], która kontroluje dostęp do wygenerowanych encji JPA. Warto zapoznać się z tymi metodami automatycznego generowania, ponieważ wygenerowany kod dostarcza cennych wskazówek dotyczących sposobu pisania encji JPA lub kodu EJB, który z nich korzysta.
Poniżej opisujemy niektóre z tych narzędzi do automatycznego generowania. Aby zrozumieć wygenerowany kod, należy posiadać solidną wiedzę na temat encji JPA, [ref1] oraz EJB i [ref2].
Tworzenie połączenia NetBeans z bazą danych
- uruchom SGBD i MySQL 5, aby BD był dostępny
- utwórz połączenie NetBeans z bazą danych [dbrdvmedecins]
![]() |
- w zakładce [Files], w gałęzi [Databases] [1], wybrać sterownik JDBC MySQL [2]
- następnie należy wybrać opcję [3] „Connect Using”, która umożliwia nawiązanie połączenia z bazą danych MySQL
- w [4] należy podać wymagane informacje
- a następnie zatwierdzić w [5]
![]() |
- w [6] połączenie zostało utworzone. Widoczne są cztery tabele podłączonej bazy danych.
Tworzenie projektu EJB
![]() |
- w [1], utworzyć nową aplikację, moduł EJB
- w [2], wybierz kategorię [Java EE], a w [3] typ [EJB Module]
![]() |
- w [4] wybierz folder dla projektu, a w [5] nadaj mu nazwę – następnie zakończ pracę kreatora
- w [6] wygenerowany projekt
Dodanie zasobu JDBC do serwera Glassfish
Dodamy zasób JDBC do serwera Glassfish.
![]() |
![]() |
- w zakładce [Services] uruchom serwer Glassfish [2, 3]
- w zakładce [Projects] kliknij prawym przyciskiem myszy na projekt Ejb i w [5] wybierz opcję [New / Other] umożliwiającą dodanie elementu do projektu.

- w [6], wybrać kategorię [Glassfish], a następnie w [7] wskazać, że chcemy utworzyć zasób JDBC, wybierając typ [JDBC Resource]
- w polu [8] należy wskazać, że zasób JDBC będzie korzystał z własnej puli połączeń
- w [9], nadać nazwę zasobowi JDBC
- w [10], przejść do następnego etapu
![]() |
- w [11] definiuje się właściwości puli połączeń zasobu JDBC
- w [12], nadać nazwę puli połączeń
- w [13], wybierz utworzone wcześniej połączenie NetBeans [dbrdvmedecins]
- w [14], przejść do następnego kroku
- w [15] – zazwyczaj nie ma potrzeby wprowadzania żadnych zmian na tej stronie. Właściwości połączenia z bazą danych MySQL [dbrdvmedecins] zostały pobrane z właściwości wcześniej utworzonego połączenia NetBeans o nazwie [dbrdvmedecins]
- w [16], przejdź do następnego kroku
![]() |
- na [17], zachowaj proponowane wartości domyślne
- w [18], zakończ pracę kreatora. Kreator utworzy plik [sun-resources.xml] [19], którego zawartość jest następująca:
Powyższy plik zawiera wszystkie informacje wprowadzone w kreatorze w formacie XML. Zostanie on wykorzystany przez IDE NetBeans do wysłania żądania do serwera GlassFish o utworzenie zasobu „jdbc/dbrdvmedecins” zdefiniowanego w wierszu 4.
Tworzenie jednostki trwałości
Jednostka trwałości [persistence.xml] konfiguruje warstwę JPA: wskazuje używaną implementację JPA (Toplink, Hibernate itp.) i dokonuje jej konfiguracji.
![]() |
![]() |
- w [1] kliknij prawym przyciskiem myszy projekt EJB i wybierz [New / Other] w [2]
- w [3], wybierz kategorię [Persistence], a następnie w [4], wskaż, że chcesz utworzyć jednostkę trwałości JPA
![]() |
- w [5], nadać nazwę utworzonej jednostce trwałości
- w [6], wybierz [Hibernate] jako implementację JPA
- w [7], wybierz właśnie utworzony zasób Glassfish „jdbc/dbrdvmedecins”
- w [8] należy wskazać, że podczas instancjonowania warstwy JPA nie należy wykonywać żadnych operacji na bazie danych
- zakończyć pracę kreatora
- w [9] plik [persistence.xml] utworzony przez kreatora
Jego zawartość jest następująca:
Ponownie, w formacie XML zawiera informacje podane w kreatorze. Ten plik nie wystarcza do pracy z bazą danych MySQL5 „dbrdvmedecins”. Musielibyśmy wskazać Hibernate, jaki typ pliku SGBD ma być obsługiwany. Zostanie to zrobione w późniejszym terminie.
Tworzenie encji JPA
![]() |
![]() |
![]() |
- w [1] kliknij prawym przyciskiem myszy na projekt, a następnie w [2] wybierz opcję [New / Other]
- w [3], wybierz kategorię [Persistence], a następnie w [4], zaznacz, że chcesz utworzyć obiekty JPA na podstawie istniejącej bazy danych.
![]() |
- w [5] należy wybrać źródło JDBC „jdbc/dbrdvmedecins”, które utworzyliśmy
- w [6], cztery tabele powiązanej bazy danych
- w [7,8], uwzględnij je wszystkie podczas generowania encji JPA
- w [9], kontynuować pracę z kreatorem
![]() |
- w [10], jednostki JPA, które zostaną wygenerowane
- w [11], nadać nazwę pakietowi elementów JPA
- w [12], wybierz typ Java, który będzie enkapsulował listy obiektów zwracanych przez warstwę JPA
- zakończyć pracę kreatora
- w [13], cztery wygenerowane encje JPA, po jednej dla każdej tabeli bazy danych.
Oto na przykład kod encji [Rv], która reprezentuje wiersz tabeli [rv] w bazie [dbrdvmedecins].
Tworzenie warstwy EJB zapewniającej dostęp do encji JPA
![]() |
![]() |
- w [1], kliknij prawym przyciskiem myszy na projekt, a następnie w [2] wybierz opcję [New / Other]
- w [3], wybierz kategorię [Persistence], a następnie w [4] typ [Session Beans for Entity Classes]
![]() |
- w [5], wcześniej utworzone jednostki JPA są wyświetlane
- w [6], zaznaczyć je wszystkie
- w [7] – zostały zaznaczone
- w pliku [8], kontynuuj pracę z kreatorem
![]() |
- w [9], nadać nazwę pakietowi EJB, które zostaną wygenerowane
- w [10], należy wskazać, że komponenty EJB muszą implementować zarówno interfejs lokalny, jak i zdalny
- zakończyć pracę kreatora
- w [11], wygenerowane komponenty EJB
Oto na przykład kod EJB, który zarządza dostępem do encji [Rv], a więc do tabeli [rv] w bazie danych [dbrdvmedecins]:
Jak już wspomniano, automatyczne generowanie kodu może być bardzo przydatne na początku projektu oraz w celu zapoznania się z encjami typu JPA i EJB. W dalszej części przepiszemy warstwy JPA i EJB, wykorzystując nasz własny kod, jednak czytelnik odnajdzie w nich informacje, które właśnie omówiliśmy w kontekście automatycznego generowania warstw.
4.6. Projekt NetBeans modułu EJB
Tworzymy nowy, pusty moduł EJB (patrz punkt 4.5):
![]() |
- pakiet [rdvmedecins.entites] zawiera encje warstwy JPA
- pakiet [rdvmedecins.dao] implementuje EJB warstwy [dao]
- pakiet [rdvmedecins.exceptions] implementuje klasę wyjątku specyficzną dla aplikacji
W dalszej części zakładamy, że czytelnik wykonał wszystkie czynności opisane w punkcie 4.5. Niektóre z nich będzie musiał powtórzyć.
4.6.1. Konfiguracja warstwy JPA
Przypomnijmy architekturę naszej aplikacji klient-serwer:
![]() |
Projekt NetBeans:
![]() |
Warstwa [JPA] jest konfigurowana za pomocą powyższych plików [persistence.xml] i [sun-resources.xml]. Oba te pliki są generowane przez znane już kreatory:
- proces generowania pliku [sun-resources.xml] został opisany w paragrafie 4.5.
- Sposób generowania pliku [persistence.xml] opisano w paragrafie 4.5.
Wygenerowany plik [persistence.xml] należy zmodyfikować w następujący sposób:
- wiersz 3: typ transakcji to JTA: transakcje będą zarządzane przez kontener EJB3 serwera GlassFish
- wiersz 4: stosowana jest implementacja JPA / Hibernate. W tym celu biblioteka Hibernate została dodana do serwera GlassFish (patrz punkt 4.4).
- wiersz 5: źródło danych JTA wykorzystywane przez warstwę JPA nosi nazwę JNDI „jdbc/dbrdvmedecins”.
- wiersz 8: ten wiersz nie jest generowany automatycznie. Należy go dodać ręcznie. Informuje on bibliotekę Hibernate, że wykorzystywanym źródłem danych SGBD jest MySQL5.
Źródło danych „jdbc/dbrdvmedecins” jest skonfigurowane w następującym pliku [sun-resources.xml]:
- wiersze 8–10: parametry JDBC źródła danych (adres URL bazy danych, nazwa użytkownika i hasło). Baza danych MySQL dbrdvmedecins jest tą samą, która została opisana w punkcie 4.1.
- wiersz 7: parametry puli połączeń powiązanej z tym źródłem danych
4.6.2. Elementy warstwy JPA
Przypomnijmy architekturę naszej aplikacji klient-serwer:
![]() |
Projekt NetBeans:
![]() |
Pakiet [rdvmedecins.entites] implementuje warstwę [Jpa].
W rozdziale 4.5 omówiliśmy, jak automatycznie wygenerować encje JPA w aplikacji. W tym przypadku nie skorzystamy z tej techniki, lecz sami zdefiniujemy encje. Będą one jednak w znacznej mierze oparte na kodzie wygenerowanym w rozdziale 4.5. W tym przypadku chcemy, aby encje [Medecin] i [Client] były klasami potomnymi klasy [Personne].
Klasa Personne służy do reprezentowania lekarzy i klientów:
- wiersz 3: należy zauważyć, że klasa [Personne] sama w sobie nie jest encją (@Entity). Będzie ona klasą nadrzędną dla encji. Adnotacja @MappedSuperClass wskazuje na tę sytuację.
Entyteta [Client] zawiera wiersze tabeli [clients]. Wywodzi się ona z poprzedniej klasy [Personne]:
- wiersz 3: klasa [Client] jest encją JPA
- wiersz 4: jest ona powiązana z tabelą [clients]
- wiersz 5: wywodzi się z klasy [Personne]
Entyteta [Medecin], która hermetyzuje wiersze tabeli [medecins], jest zbudowana według tego samego wzorca:
Entyteta [Creneau] zawiera wiersze tabeli [creneaux]:
- wiersze 15–17 modelują relację „jeden do wielu”, która istnieje między tabelą [creneaux] a tabelą [medecins] w bazie danych.
Entyteta [Rv] zawiera wiersze z tabeli [rv]:
- wiersze 15–17 modelują relację „jeden do wielu”, która istnieje między tabelą [rv] a tabelą [clients] w bazie danych, a wiersze 18–20 relację „jeden do wielu”, która istnieje między tabelą [rv] a tabelą [creneaux]
4.6.3. Klasa wyjątku
![]() |
Klasa wyjątku [RdvMedecinsException] w aplikacji ma następującą postać:
- wiersz 6: klasa ta wywodzi się z klasy [RuntimeException]. Kompilator nie wymusza zatem obsługi tej klasy za pomocą instrukcji try / catch.
- wiersz 5: adnotacja @ApplicationException sprawia, że wyjątek nie zostanie „przejęty” przez wyjątek typu [EjbException].
Aby zrozumieć adnotację @ApplicationException, wróćmy do architektury stosowanej po stronie serwera:
![]() |
Wyjątek typu [RdvMedecinsException] zostanie wygenerowany przez metody EJB warstwy [dao] wewnątrz kontenera EJB3 i przechwycony przez ten kontener. Bez adnotacji @ApplicationException kontener EJB3 hermetyzuje wystąpiony wyjątek w wyjątku typu [EjbException] i ponownie go zgłasza. Można nie chcieć takiego enkapsulowania i pozwolić, aby z kontenera Ejb3 wyszedł wyjątek typu [RdvMedecinsException]. Umożliwia to adnotacja @ApplicationException. Ponadto atrybut (rollback=true) tej adnotacji informuje kontener EJB3, że jeśli wyjątek typu [RdvMedecinsException] wystąpi wewnątrz metody wykonywanej w ramach transakcji z SGBD, transakcja ta musi zostać cofnięta. W terminologii technicznej nazywa się to wykonaniem operacji rollback na transakcji.
4.6.4. Komponent EJB warstwy [dao]
![]() |
![]() |
Interfejs Java [IDao] warstwy [dao] wygląda następująco:
Lokalny interfejs [IDaoLocal] dla EJB jest po prostu pochodną poprzedniego interfejsu [IDao]:
To samo dotyczy interfejsu zdalnego [IDaoRemote]:
EJB o nazwie [DaoJpa] implementuje oba interfejsy, zarówno lokalny, jak i zdalny:
- wiersz 3 wskazuje, że zdalny EJB nosi nazwę „rdvmedecins.dao”
- wiersz 4 wskazuje, że wszystkie metody EJB są wykonywane w ramach transakcji zarządzanej przez kontener EJB3.
- wiersz 5 pokazuje, że komponent EJB implementuje interfejsy lokalny i zdalny.
Pełny kod EJB wygląda następująco:
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 | |
- wiersz 8: obiekt EntityManager, który zarządza dostępem do kontekstu trwałości. Podczas instancjonowania klasy pole to zostanie zainicjowane przez kontener EJB dzięki adnotacji @PersistenceContext w wierszu 7.
- wiersz 15: zapytanie JPQL, które zwraca wszystkie wiersze tabeli [clients] w postaci listy obiektów [Client].
- wiersz 22: analogiczne zapytanie dotyczące lekarzy
- wiersz 32: zapytanie JPQL wykonujące połączenie tabel [creneaux] i [medecins]. Jest ono filtrowane według identyfikatora lekarza.
- wiersz 43: zapytanie JPQL wykonujące połączenie tabel [rv], [creneaux] i [medecins] oraz posiadające dwa parametry: identyfikator lekarza i dzień wizyty.
- wiersze 55–57: utworzenie wizyty, a następnie zapisanie jej w bazie danych.
- wiersz 67: usunięcie wizyty z bazy danych.
- wiersz 76: wykonuje zapytanie SELECT w bazie danych w celu znalezienia określonego klienta
- wiersz 85: to samo dla lekarza
- wiersz 94: to samo dla wizyty
- wiersz 103: to samo dla przedziału czasowego
- Wszystkie operacje z wykorzystaniem kontekstu trwałości em z linii 9 mogą napotkać problem z bazą danych. Dlatego wszystkie są otoczone blokiem try / catch. Ewentualny wyjątek jest zamknięty w „własnym” wyjątku RdvMedecinsException.
Po skompilowaniu moduł EJB tworzy plik .jar o nazwie „ ”:
![]() |
4.7. Wdrażanie modułu EJB warstwy [dao] za pomocą NetBeans
NetBeans umożliwia proste wdrożenie utworzonego wcześniej modułu EJB na serwerze GlassFish.
![]() |
- W właściwościach projektu EJB należy sprawdzić opcje uruchamiania [1].
- W polu [2] należy wpisać nazwę serwera, na którym ma zostać wdrożony komponent EJB
- w zakładce [Services] [3] uruchamia się go [4].
![]() |
- w [5], serwer Glassfish po uruchomieniu. Nie ma jeszcze modułu EJB.
- Uruchom serwer MySQL i upewnij się, że baza danych [dbrdvmedecins] jest dostępna. W tym celu możesz skorzystać z połączenia NetBeans utworzonego w punkcie 4.5.
- W zakładce [Projects] [6] wdrażamy moduł EJB [7]: aby zasób JDBC „jdbc/dbrdvmedecins” wykorzystywany przez EJB był dostępny, należy uruchomić SGBD MySQL5.
- W [8] wdrożony komponent EJB pojawia się w drzewie katalogów serwera GlassFish
![]() |
- W [9] usuwa się wdrożony komponent EJB
- w [10] EJB nie pojawia się już w drzewie katalogów serwera GlassFish.
4.8. Wdrażanie komponentu EJB z warstwy [dao] za pomocą Glassfish
Poniżej pokazujemy, jak wdrożyć komponent EJB na serwerze GlassFish z archiwum .jar.
- Uruchom serwer MySQL i upewnij się, że baza danych [dbrdvmedecins] jest dostępna. W tym celu możesz skorzystać z połączenia NetBeans utworzonego w punkcie 4.5.
Przypomnijmy konfigurację JPA modułu EJB, który ma zostać wdrożony. Konfiguracja ta znajduje się w pliku [persistence.xml]:
Wiersz 5 wskazuje, że warstwa JPA korzysta ze źródła danych JTA, c.a.d, zarządzanego przez kontener EJB3 o nazwie „jdbc/dbrdvmedecins”.
W rozdziale 4.5 omówiliśmy, jak utworzyć ten zasób JDBC w programie NetBeans. Tutaj pokazujemy, jak zrobić to bezpośrednio w GlassFish. Postępujemy zgodnie z procedurą opisaną w rozdziale 13.1.2, na stronie 79 dokumentacji [ref1].
Zaczynamy od usunięcia zasobu, aby móc go ponownie utworzyć. Robimy to z poziomu NetBeans:
![]() |
- w pliku [1] zasoby JDBC serwera GlassFish
- na [2], zasób „jdbc/dbrdvmedecins” naszego EJB
- na [3], pula połączeń tego zasobu JDBC
![]() |
- w [4] usuwamy pulę połączeń. Spowoduje to usunięcie wszystkich zasobów JDBC, które z niej korzystają, a więc zasobu „jdbc/dbrdvmedecins”.
- W plikach [5] i [6] zasób JDBC oraz pula połączeń zostały usunięte.
Teraz używamy konsoli administracyjnej serwera GlassFish do utworzenia zasobu JDBC i wdrożenia komponentu EJB.
![]() |
- w zakładce [services] [1] w NetBeans, uruchom serwer GlassFish [2], a następnie przejdź do jego konsoli administracyjnej
- pod adresem [4], zaloguj się jako administrator (hasło: adminadmin, jeśli nie zmieniłeś go podczas instalacji ani później).
![]() |
- w [5], wybierz gałąź [Connection Pools] zasobów Glassfish
- w [6], utwórz nową pulę połączeń. Przypominamy, że pula połączeń to technika służąca do ograniczenia liczby otwierania i zamykania połączeń z SGBD. Po uruchomieniu serwera N — liczba określona w konfiguracji — połączeń zostaje otwartych z SGBD. Te otwarte połączenia są następnie udostępniane komponentom EJB, które je wykorzystują do wykonania operacji z SGBD. Gdy operacja zostanie zakończona, komponent EJB zwraca połączenie do puli. Połączenie nigdy nie jest zamykane. Jest ono współdzielone między różnymi wątkami, które uzyskują dostęp do obiektu SGBD
- w [7], nadaj nazwę puli
- w [8], klasą modelującą źródło danych jest klasa [javax.sql.DataSource]
- w [9], a SGBD, który przechowuje źródło danych, to tutaj MySQl.
- w [10], przejdź do następnego etapu
![]() |
- w [11] atrybut „Connection Validation Required” powoduje, że przed przyznaniem połączenia pula sprawdza, czy jest ono operacyjne. Jeśli tak nie jest, tworzy nowe. Pozwala to aplikacji na dalsze działanie po chwilowej przerwie w połączeniu z SGBD. W trakcie przerwy żadne połączenie nie jest dostępne, a do klienta zgłaszane są wyjątki. Gdy przerwa dobiegnie końca, klienci, którzy nadal żądają połączeń, otrzymują je ponownie: dzięki atrybutowi „Connection Validation Required” wszystkie połączenia w puli zostaną odtworzone. Bez tego atrybutu pula stwierdziłaby, że początkowe połączenia zostały przerwane, ale nie próbowałaby tworzyć nowych.
- W [12] wymagany jest poziom izolacji „Read Committed” dla transakcji. Poziom ten gwarantuje, że transakcja T2 nie może odczytać danych zmodyfikowanych przez transakcję T1, dopóki ta ostatnia nie zostanie całkowicie zakończona.
- W transakcji [13] żąda się, aby wszystkie transakcje korzystały z poziomu izolacji określonego w transakcji [12]
![]() |
- w [14] i [15] należy określić adres URL transakcji BD, której połączenia są zarządzane przez pulę
- w [16], użytkownikiem będzie root
- w [17] dodaj właściwość
- w [18] dodaj właściwość „Password” o wartości () w [19]. Chociaż zrzut ekranu [19] tego nie pokazuje, nie należy wpisywać pustego ciągu znaków, lecz () (nawias otwierający, nawias zamykający), aby oznaczyć puste hasło. Jeśli użytkownik root z bazy SGBD MySQL ma hasło inne niż puste, należy wprowadzić to hasło.
- w [20], zakończ pracę kreatora tworzenia puli połączeń dla bazy MySQL [dbrdvmedecins].
![]() |
- w [21] – pula została utworzona. Należy kliknąć jej link.
- w [22] przycisk [Ping] umożliwia nawiązanie połączenia z bazą danych [dbrdvmedecins]
- w [23], jeśli wszystko przebiegło pomyślnie, pojawi się komunikat informujący o pomyślnym nawiązaniu połączenia
Po utworzeniu puli połączeń można utworzyć zasób JDBC:
![]() |
- w [1], wybieramy gałąź [JDBC Resources] z drzewa obiektów serwera
- w [2] tworzymy nowy zasób JDBC
- w [3] nadajemy nazwę zasobowi JDBC. Musi ona odpowiadać nazwie użytej w pliku [persistence.xml]:
- w pliku [4] określa się pulę połączeń, z której ma korzystać nowy zasób JDBC: tę, którą właśnie utworzono
- w pliku [5] kończymy pracę z kreatorem
![]() |
- w [6] nowy zasób JDBC
Teraz, gdy zasób JDBC został utworzony, można wdrożyć archiwum jar komponentu EJB:
![]() |
- w [1], wybierz gałąź [Enterprise Applications]
- w [2], za pomocą przycisku [Deploy], wskaż, że chcesz wdrożyć nową aplikację
- w [3], zaznacz, że aplikacja jest modułem EJB
- w [4] wybierz plik JAR modułu EJB [serveur-ejb-dao-jpa-hibernate.jar], który został Ci przekazany dla TP.
- w [5] można zmienić nazwę modułu EJB, jeśli jest taka potrzeba
- w [6] zakończ pracę kreatora wdrażania modułu EJB
![]() |
- W module [7] wdrożono moduł EJB. Można z niego teraz korzystać.
4.9. Testy modułu EJB warstwy [dao]
Teraz, gdy moduł EJB warstwy [dao] naszej aplikacji został wdrożony, możemy go przetestować. Zrobimy to za pomocą następującego klienta Java:
![]() |
Klasa [MainTestsDaoRemote] [1] jest klasą testową JUnit 4. Biblioteki w [2] składają się z jednej strony z:
- pliku JAR EJB warstwy [dao] [3] (patrz punkt 4.6.4).
- bibliotek GlassFish [4] niezbędnych dla zdalnych klientów EJB.
Klasa testowa ma następującą postać:
- wiersz 13: należy zwrócić uwagę na instancjonowanie proxy zdalnego EJB. Używa się jego nazwy JNDI „rdvmedecins.dao”.
- Metody testowe wykorzystują metody udostępniane przez EJB (patrz punkt 4.6.4).
Jeśli wszystko przebiegnie pomyślnie, testy powinny zakończyć się powodzeniem:
![]() |
Teraz, gdy EJB warstwy [dao] działa poprawnie, można przejść do jego publicznego udostępnienia za pośrednictwem serwisu internetowego.
4.10. Usługa internetowa warstwy [dao]
Krótkie wprowadzenie do pojęcia serwisu internetowego można znaleźć w paragrafie 14, na stronie 111 dokumentu [ref1].
Wróćmy do architektury serwera naszej aplikacji klient-serwer:
![]() |
W powyższym przykładzie skupiamy się na usłudze internetowej warstwy [dao]. Jedynym zadaniem tej usługi jest udostępnienie interfejsu EJB warstwy [dao] klientom korzystającym z różnych platform, którzy są w stanie komunikować się z usługą internetową.
Przypomnijmy, że istnieją dwa sposoby implementacji usługi internetowej:
- za pomocą klasy opatrzonej adnotacją @WebService, która działa w kontenerze internetowym
![]() |
- poprzez komponent EJB opatrzony adnotacją @WebService, który działa w kontenerze EJB
![]() |
W tym przypadku stosujemy pierwsze rozwiązanie. W środowisku NetBeans IDE musimy utworzyć projekt korporacyjny zawierający dwa moduły:
- moduł EJB, który będzie działał w kontenerze EJB: EJB warstwy [dao].
- moduł internetowy, który będzie działał w kontenerze internetowym: usługa internetowa, którą właśnie tworzymy.
Zrealizujemy ten projekt korporacyjny na dwa sposoby.
4.10.1. Projekt NetBeans – wersja 1
Najpierw tworzymy projekt NetBeans typu „Web Application”:
![]() |
- w [1] tworzymy nowy projekt w kategorii „Java Web” [2] typu „Web Application” [3].
![]() |
- w [4] nadajemy nazwę projektowi, a w [5] określamy folder, w którym ma zostać wygenerowany
- w pliku [6] określa się serwer aplikacji, który będzie uruchamiał aplikację internetową
- w pliku [7] określa się kontekst aplikacji
- w pliku [8] zatwierdza się konfigurację projektu.
![]() |
- w [9] – wygenerowany projekt. Tworzona przez nas usługa internetowa będzie korzystać z pliku EJB z poprzedniego projektu [10]. W związku z tym musi ona odwoływać się do pliku .jar modułu EJB [10].
- W projekcie [11] dodajemy projekt NetBeans do bibliotek projektu internetowego [12]
![]() |
- w [13] należy wybrać folder modułu EJB w systemie plików i zatwierdzić.
![]() |
- w [14] moduł EJB został dodany do bibliotek projektu internetowego.
W pliku [15] implementujemy usługę internetową za pomocą następującej klasy [WsDaoJpa]:
- W wierszu 4 klasa [WsdaoJpa] implementuje interfejs [IDao]. Przypomnijmy, że interfejs ten jest zdefiniowany w archiwum EJB warstwy [dao] w następującej postaci:
- wiersz 3: adnotacja @WebService sprawia, że klasa [WsDaoJpa] staje się usługą internetową.
- wiersze 6–7: odwołanie do EJB warstwy [dao] zostanie wstrzyknięte przez serwer aplikacji do pola w wierszu 7. Należy pamiętać, że w ten sposób zawsze wstrzykiwana jest implementacja lokalna (w tym przypadku IDaoLocal). Wstrzyknięcie to jest możliwe, ponieważ usługa internetowa działa w tej samej maszynie JVM co komponent EJB.
- Wszystkie metody serwisu internetowego są oznaczone adnotacją @WebMethod, aby były widoczne dla klientów zdalnych. Metoda nieoznaczona adnotacją @WebMethod byłaby metodą wewnętrzną serwisu internetowego i nie byłaby widoczna dla klientów zdalnych. Każda metoda M serwisu internetowego ogranicza się do wywołania odpowiadającej jej metody M z EJB wstrzykniętego w linii 7.
Utworzenie tej usługi internetowej znajduje odzwierciedlenie w nowej gałęzi projektu NetBeans:
![]() |
W [1] widoczna jest usługa internetowa WsDaoJpa, a w [2] metody, które udostępnia ona klientom zdalnym.
Przypomnijmy architekturę tworzonej usługi internetowej:
![]() |
Komponenty serwisu internetowego, które zamierzamy wdrożyć, to:
- [1]: moduł internetowy, który właśnie stworzyliśmy
- [2]: moduł EJB, który stworzyliśmy na poprzednim etapie i od którego zależy usługa internetowa
Aby wdrożyć je razem, należy połączyć oba moduły w projekcie NetBeans określanym jako „korporacyjny”:
![]() |
W projekcie [1] tworzymy nowy projekt korporacyjny o nazwie [2, 3].
![]() |
- w [4,5] nadajemy nazwę projektowi i określamy katalog, w którym zostanie utworzony
- w [6] wybiera się serwer aplikacji, na którym zostanie wdrożona aplikacja korporacyjna
- w [7]: projekt korporacyjny może składać się z trzech komponentów: aplikacji internetowej, modułu EJB oraz aplikacji klienckiej. W tym przypadku projekt jest tworzony bez żadnych komponentów. Zostaną one dodane w późniejszym terminie.
![]() |
- w [8] – nowo utworzona aplikacja korporacyjna.
![]() |
- w [9], kliknij prawym przyciskiem myszy na [Java EE Modules] i dodaj nowy moduł
- w [10] wyświetlane są wyłącznie moduły NetBeans aktualnie otwarte w IDE. W tym miejscu wybieramy moduł internetowy [serveur-webservice-1-ejb-dao-jpa-hibernate] oraz moduł EJB [serveur-ejb-dao-jpa-hibernate], które stworzyliśmy.
- W [11] widoczne są oba moduły dodane do projektu korporacyjnego.
Pozostaje nam wdrożyć tę aplikację korporacyjną na serwerze GlassFish. W dalszej kolejności należy uruchomić SGBD i MySQL, aby zapewnić dostęp do źródła danych JDBC „jdbc/dbrdvmedecins” wykorzystywanego przez moduł EJB.
![]() |
- w [1] uruchamia się serwer Glassfish
- jeśli moduł EJB [serveur-ejb-dao-jpa-hibernate] jest wdrożony, należy go wyładować za pomocą [2]
- na [3], wdraża się aplikację korporacyjną
![]() |
- w [4] – aplikacja została wdrożona. Widać, że zawiera ona dwa moduły: Web i EJB.
4.10.2. Projekt NetBeans – wersja 2
Pokażemy teraz, jak wdrożyć usługę internetową, gdy nie dysponujemy kodem źródłowym modułu EJB, a jedynie jego archiwum .jar.
Nowy projekt NetBeans dla serwisu internetowego będzie wyglądał następująco:
![]() |
Istotne elementy projektu to:
- [1]: usługa internetowa jest zaimplementowana przez projekt NetBeans typu [Web Application].
- [2]: usługa internetowa jest zaimplementowana przez omówioną już klasę [WsDaoJpa]
- [3]: archiwum EJB warstwy [dao], które umożliwia klasie [WsDaoJpa] dostęp do definicji różnych klas, interfejsów i encji warstw [dao] oraz [jpa].
Następnie tworzymy projekt korporacyjny niezbędny do wdrożenia usługi internetowej:
![]() |
- [1], tworzymy aplikację korporacyjną [ea-rdvmedecins], początkowo bez żadnego modułu.
- w [2] dodajemy poprzedni moduł internetowy [serveur-webservice-ejb-dao-jpa-hibernate]
- W pliku [3] znajduje się wynik.
W obecnej postaci aplikacja korporacyjna [ea-rdvmedecins] nie może zostać wdrożona na serwerze Glassfish z poziomu NetBeans. Pojawia się błąd. Należy zatem ręcznie wdrożyć archiwum EAR aplikacji [ea-rdvmedecins]:
![]() |
- Archiwum [ea-rdvmedecins.ear] znajduje się w folderze [dist] [2] w zakładce [Files] programu NetBeans.
- W tym archiwum [3] znajdują się dwa elementy aplikacji korporacyjnej:
- archiwum EJB o nazwie [serveur-ejb-dao-jpa-hibernate]. Archiwum to znajduje się w archiwum, ponieważ stanowiło część bibliotek, do których odwołuje się usługa internetowa.
- archiwum serwisu internetowego [serveur-webservice- ejb-dao-jpa-hibernate].
- Archiwum [ea-rdvmedecins.ear] składa się z archiwum Build oraz [4] aplikacji korporacyjnej.
- W przypadku archiwum [5] operacja wdrożenia kończy się niepowodzeniem.
Aby wdrożyć archiwum [ea-rdvmedecins.ear] aplikacji korporacyjnej, postępujemy tak, jak pokazano podczas wdrażania archiwum EJB [serveur-ejb-dao-jpa-hibernate.jar] w punkcie 4.2. Ponownie korzystamy z internetowego klienta administracyjnego serwera GlassFish. Nie powtarzamy już opisanych kroków.
Najpierw „wycofamy” aplikację korporacyjną wdrożoną w punkcie 4.10.1:
![]() |
- [1]: wybierz gałąź [Enterprise Applications] serwera Glassfish
- w [2] wybierz aplikację korporacyjną do odinstalowania, a następnie w [3] odinstaluj ją
- w [4] aplikacja korporacyjna została wyładowana
![]() |
- w [1] wybierz gałąź [Enterprise Applications] serwera Glassfish
- w [2], wdróż nową aplikację korporacyjną
- w [3], wybierz typ [Enterprise Application]
- w [4], wskaż plik .ear projektu NetBeans [ea-rdvmedecins]
- w [5] wdróż ten archiwum
![]() |
- na [6], aplikacja została wdrożona
- jako [7], usługa internetowa [WsDaoJpa] pojawia się w gałęzi [Web Services] serwera Glassfish. Należy ją wybrać.
- W [8] dostępne są różne informacje o usłudze internetowej. Najbardziej interesująca dla klienta jest informacja [9]: adres URI usługi internetowej.
- W [10] można przetestować usługę internetową
![]() |
- W [11] znajduje się adres URI usługi internetowej , do którego dodano parametr ?tester. Ten adres URI wyświetla stronę testową. Wszystkie metody (@WebMethod) udostępniane przez usługę internetową są wyświetlane i można je przetestować. W tym przypadku testujemy metodę [13], która pobiera listę klientów.
![]() |
- W przypadku [14] przedstawiamy jedynie fragment strony z odpowiedzią. Widać jednak, że metoda getAllClients rzeczywiście zwróciła listę klientów. Zrzut ekranu pokazuje, że wysyła ona swoją odpowiedź w formacie XML.
Usługa internetowa jest w całości opisana w pliku o nazwie XML, zwanym plikiem WSDL:
![]() |
- w pliku [1] w internetowym narzędziu administracyjnym serwera Glassfish, wybierz usługę internetową [WsDaoJpa]
- na [2], kliknij link [View WSDL]
![]() |
- na [3]: adres URI pliku WSDL. Jest to ważna informacja, którą należy znać. Jest ona niezbędna do skonfigurowania klientów tej usługi internetowej.
- w [4] – opis usługi internetowej XML. Nie będziemy komentować tej złożonej treści.
4.10.3. Testy serwisu internetowego JUnit
Tworzymy projekt w NetBeans, aby „odtworzyć” testy przeprowadzone wcześniej z klientem EJB, tym razem z klientem dla niedawno wdrożonej usługi internetowej. Postępujemy tutaj analogicznie do procedury opisanej w paragrafie 14.2.1, na stronie 115 dokumentu [ref1].
![]() |
- w [1], klasyczny projekt Java
- w [2] – klasa testowa
- w pliku [3] klient korzysta z archiwum EJB, aby uzyskać dostęp do definicji interfejsu warstwy [dao] oraz encji JPA. Przypominamy, że archiwum to znajduje się w podfolderze [dist] w folderze modułu EJB.
Aby uzyskać dostęp do zdalnej usługi internetowej, konieczne jest wygenerowanie klas proxy:
![]() |
Na powyższym schemacie warstwa [2] [C=Client] komunikuje się z warstwą [1] [S=Serveur]. Aby nawiązać komunikację z warstwą [S], klient [C] musi utworzyć połączenie sieciowe z warstwą [S] i komunikować się z nią zgodnie z określonym protokołem. Połączenia sieciowe to połączenia typu TCP, a protokołem transportowym jest HTTP. Warstwa [S], reprezentująca usługę internetową, jest zaimplementowana przez serwlet Java uruchamiany przez serwer Glassfish. Nie napisaliśmy tej serwletu. Jej generowanie jest zautomatyzowane przez Glassfish na podstawie adnotacji @Webservice i @WebMethod w klasie [WsDaoJpa], którą napisaliśmy. Podobnie zautomatyzujemy generowanie warstwy [C] po stronie klienta. Warstwę [C] nazywa się czasem warstwą proxy zdalnej usługi internetowej, przy czym termin proxy oznacza element pośredniczący w łańcuchu oprogramowania. W tym przypadku proxy C jest pośrednikiem między klientem, który zamierzamy napisać, a usługą internetową, którą wdrożyliśmy.
W programie NetBeans 6.5 serwer proxy C można wygenerować w następujący sposób (aby kontynuować, usługa internetowa musi być aktywna na serwerze GlassFish):
![]() |
- w [1], dodaj nowy element do projektu Java
- w [2], wybierz gałąź [Web services]
- w [3], wybierz [Web Service Client]
![]() |
- w [4] należy podać adres URI pliku WSDL usługi internetowej. Adres ten został przedstawiony w punkcie 4.10.2.
- w polu [5] należy pozostawić wartość domyślną [JAX-WS]. Inną możliwą wartością jest [JAX-RPC]
- po zatwierdzeniu kreatora tworzenia proxy serwisu internetowego projekt NetBeans został wzbogacony o gałąź [Web Service References] [6]. Gałąź ta przedstawia metody udostępniane przez zdalny serwis internetowy.
![]() |
- w zakładce [Files] [7] dodano kod źródłowy w języku Java [8]. Odpowiada on wygenerowanemu proxy w języku C.
- W pliku [9] znajduje się kod jednej z klas. Widać tam, że zostały one umieszczone w pakiecie [rdvmedecins.ws]. Nie będziemy komentować kodu tych klas, który ponownie jest dość złożony.
W przypadku klienta Java, który właśnie tworzymy, wygenerowany proxy C pełni rolę pośrednika. Aby uzyskać dostęp do metody M zdalnej usługi internetowej, klient Java wywołuje metodę M proxy C. W ten sposób klient Java wywołuje metody lokalne (wykonywane w tej samej maszynie JVM), a wywołania te są dla niego w sposób przezroczysty przekształcane na wywołania zdalne.
Pozostaje nam jeszcze dowiedzieć się, jak wywoływać metody M proxy w języku C. Wróćmy do naszej klasy testowej JUnit:
![]() |
W klasie [1] klasa testowa [MainTestsDaoRemote] jest tą samą, która została już wykorzystana podczas testowania EJB warstwy [dao]:
- W wierszu [13] test test1 pozostaje bez zmian.
- w wierszu [9] usunięto zawartość metody [init].
Na tym etapie projekt zawiera błędy, ponieważ metoda testowa [test1] wykorzystuje jednostki [Client], [Medecin], [Creneau], [Rv], które nie znajdują się już w tych samych pakietach co wcześniej. Znajdują się one w pakiecie wygenerowanego proxy C. Należy usunąć odpowiednie instrukcje import i wygenerować je ponownie za pomocą operacji „Fix Imports”.
![]() |
Wróćmy do kodu klasy testowej [MainTestsDaoRemote]:
Metoda [init] w wierszu 10 musi zainicjować odwołanie do warstwy [dao] z wiersza 7. Musimy wiedzieć, jak wykorzystać wygenerowany proxy C w naszym kodzie. NetBeans pomaga nam w tym zadaniu.
![]() |
- W [1] należy zaznaczyć metodę [getAllClients] usługi internetowej za pomocą myszy, a następnie przeciągnąć tę metodę i upuścić ją w metodzie [init] klasy testowej.
Otrzymujemy wynik [2]. Ten szkielet kodu pokazuje, jak korzystać z wygenerowanego proxy C:
- Wiersz [5] pokazuje, że metoda [getAllClients] jest metodą obiektu typu [WsDaoJpa] zdefiniowanego w wierszu 3. Typ [WsDaoJpa] jest interfejsem zawierającym te same metody, co te udostępniane przez zdalną usługę internetową.
- W wierszu [3] obiekt [WsDaoJpa port] jest uzyskiwany z innego obiektu typu [WsDaoJpaService] zdefiniowanego w wierszu 2. Typ [WsDaoJpaService] reprezentuje lokalnie wygenerowany proxy w języku C.
- Dostęp do zdalnej usługi internetowej może się nie powieść, dlatego cały kod jest otoczony blokiem try/catch.
- Obiekty proxy C znajdują się w pakiecie [rdvmedecins.ws]
Po zrozumieniu tego kodu widać, że lokalny identyfikator zdalnej usługi internetowej można uzyskać za pomocą kodu:
Kod klasy testowej JUnit przyjmuje wówczas następującą postać:
Jesteśmy teraz gotowi do przeprowadzenia testów:
![]() |
W [1] uruchamiany jest test JUnit. W [2] test zakończył się powodzeniem. Jeśli przyjrzymy się komunikatom wyświetlanym w konsoli NetBeans, znajdziemy takie wiersze jak poniżej:
Liste des clients :
rdvmedecins.ws.Client@1982fc1
rdvmedecins.ws.Client@676437
rdvmedecins.ws.Client@1e4853f
rdvmedecins.ws.Client@1e808ca
Po stronie serwera jednostka [Client] posiada metodę toString, która wyświetla różne pola obiektu typu [Client]. Podczas automatycznego generowania proxy C, encje są tworzone w proxy C, ale zawierają wyłącznie pola prywatne wraz z odpowiadającymi im metodami get / set. W związku z tym metoda toString nie została wygenerowana w encji [Client] proxy C. To wyjaśnia poprzedni wynik wyświetlania. Nie ma to jednak wpływu na wynik testu JUnit: test zakończył się powodzeniem. Od tej pory uznamy, że usługa internetowa działa poprawnie.






























































































