7. Wersja 3: Przeniesienie aplikacji PAM na serwer aplikacji Glassfish
Planujemy umieścić pliki EJB z warstw [metier] i [DAO] architektury OpenEJB / EclipseLink w kontenerze serwera aplikacji Glassfish.
Obecna implementacja z warstwami OpenEJB / EclipseLink
![]() |
Powyżej warstwa [ui] korzysta z interfejsu zdalnego warstwy [metier].
Przetestowaliśmy dwa konteksty wykonania: local oraz distant. W tym ostatnim trybie warstwa [ui] była klientem warstwy [metier], zaimplementowanej przez EJB. Aby działać w trybie klient/serwer, w którym klient i serwer działają w dwóch różnych instancjach JVM, umieścimy warstwy [metier, DAO, jpa] na serwerze Java Glassfish o nazwie EE. Serwer ten jest dostarczany wraz z NetBeans.
Implementacja do zbudowania z wykorzystaniem serwera Glassfish
![]() |
- warstwa [ui] będzie działać w środowisku Java SE (Standard Edition)
- Warstwy [metier, DAO, JPA] będą działać w środowisku Java EE (Enterprise Edition) na serwerze GlassFish v3
- klient będzie komunikował się z serwerem za pośrednictwem sieci TCP/IP. Wymiana danych sieciowych jest dla programisty przezroczysta, z tym że musi on jednak mieć świadomość, że klient i serwer wymieniają między sobą zserializowane obiekty w celu komunikacji, a nie odwołania do obiektów. Protokół sieciowy wykorzystywany do tej wymiany nosi nazwę RMI (Remote Method Invocation) i może być stosowany wyłącznie między dwiema aplikacjami Java.
- Implementacja JPA używana na serwerze Glassfish będzie miała nazwę EclipseLink.
7.1. Część serwerowa aplikacji klient-serwer PAM
7.1.1. Architektura aplikacji
W tym miejscu analizujemy część serwerową, która będzie hostowana przez kontener EJB3 na serwerze Glassfish:
![]() |
Chodzi o przeniesienie na serwer Glassfish tego, co zostało już zrealizowane i przetestowane w kontenerze OpenEJB. Na tym polega zaleta kontenera OpenEJB oraz ogólnie kontenerów wbudowanych typu EJB: umożliwiają nam one testowanie aplikacji w uproszczonym środowisku uruchomieniowym. Po przetestowaniu aplikacji pozostaje jedynie przenieść ją na serwer docelowy, w tym przypadku serwer Glassfish.
7.1.1.1. Projekt NetBeans
Zacznijmy od utworzenia nowego projektu NetBeans:
![]() |
- w [1], nowy projekt
- w [2], wybierz kategorię Maven, a w [3] typ EJB Moduł. Chodzi bowiem o stworzenie projektu, który będzie hostowany i uruchamiany przez kontener EJB, czyli kontener serwera Glassfish.
![]() |
- za pomocą przycisku [4a] wybierz folder nadrzędny dla folderu projektu lub wpisz jego nazwę bezpośrednio w polu [4b].
- w polu [5] nadać nazwę projektowi
- w [6], wybierz serwer aplikacji, na którym projekt będzie uruchamiany. Wybrany tutaj serwer jest jednym z tych widocznych w zakładce [Runtime / Servers], w tym przypadku Glassfish v3.
- w [7], wybierz wersję Javy EE.
![]() |
- w [1] – nowy projekt. Różni się on od klasycznego projektu Java pod kilkoma względami:
- automatycznie tworzona jest gałąź [Other Sources] [2]. Będzie ona zawierać w szczególności plik [persistence.xml], który konfiguruje warstwę JPA,
- jeśli skompilujemy projekt (Build), pojawi się zależność [javaee-api-6.0] od pliku [3]. Jest to zależność typu provided, ponieważ jest ona dostarczana podczas wykonywania przez kontener EJB w Glassfish.
7.1.1.2. Konfiguracja warstwy trwałości
Przez konfigurację warstwy trwałości rozumiemy utworzenie pliku [persistence.xml], który definiuje:
- implementację JPA, która ma być używana
- definicję źródła danych wykorzystywanych przez warstwę JPA. Będzie to źródło JDBC zarządzane przez serwer Glassfish.
![]() |
Można postępować w następujący sposób. Najpierw, w zakładce [Runtime / Databases], należy utworzyć połączenie z bazą danych MySQL5 / dbpam_eclipselink:
![]() |
Po wykonaniu tej czynności można przejść do utworzenia zasobu JDBC wykorzystywanego przez moduł EJB:
![]() |
- w [1] należy utworzyć nowy plik – przed wykonaniem tej operacji należy upewnić się, że wybrany jest projekt EJB
- w [2], projekt EJB
- w [3] wybieramy kategorię [Glassfish]
- w [4] chcemy utworzyć zasób JDBC
![]() |
- w [5] należy wskazać, że zasób JDBC będzie korzystał z nowej puli połączeń. Przypominamy, że pula połączeń to zbiór otwartych połączeń, który służy do przyspieszenia wymiany danych między aplikacją a bazą danych.
- w pliku [6] nadać nazwę JNDI utworzonemu zasobowi JDBC. Nazwa ta może być dowolna, ale często ma postać jdbc/nom. Ta nazwa JNDI zostanie użyta w pliku [persistence.xml] w celu wskazania źródła danych, z którego ma korzystać implementacja JPA.
- W [7] nadaj dowolną nazwę puli połączeń, która zostanie utworzona
- z listy rozwijanej [8] należy wybrać połączenie JDBC utworzone wcześniej w oparciu o bazę MySQL / dbpam_eclipselink.
- W oknie [9] wyświetla się podsumowanie właściwości puli połączeń – nie należy niczego zmieniać
![]() |
- w pliku [10] można określić wiele właściwości puli połączeń – pozostawiamy wartości domyślne
- w pliku [11], po zakończeniu pracy kreatora tworzenia zasobu JDBC dla modułu EJB, w gałęzi [Other Sources] utworzono plik [glassfish-resources.xml]. Zawartość tego pliku jest następująca:
<?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>
Plik [glassfish-resources.xml] jest plikiem XML, który zawiera wszystkie dane zebrane przez kreatora. Zostanie on wykorzystany przez NetBeans w celu, aby podczas wdrażania modułu EJB na serwerze GlassFish zażądać utworzenia zasobu JDBC, którego potrzebuje ten moduł.
Można teraz utworzyć plik [persistence.xml], który skonfiguruje warstwę JPA modułu EJB:
![]() |
- w [1], należy utworzyć nowy plik – przed wykonaniem tej operacji należy upewnić się, że wybrany jest projekt EJB
- w [2], projekt EJB
- w [3] wybieramy kategorię [Persistence]
- w [4] chcemy utworzyć jednostkę trwałości
![]() |
- w [5], nadajemy nazwę jednostce trwałości
- w [6] proponowanych jest kilka implementacji JPA. W tym przypadku wybierzemy [EclipseLink]. Inne implementacje można wykorzystać pod warunkiem umieszczenia bibliotek, które je implementują, wraz z bibliotekami serwera Glassfish.
- W listy rozwijanej [7] należy wybrać źródło danych JDBC [jdbc/dbpam_eclipselink], które właśnie zostało utworzone.
- W [8] należy wskazać, że transakcje są zarządzane przez kontener EJB
- w [9] należy wskazać, że podczas wdrażania modułu EJB na serwerze nie należy wykonywać żadnych operacji na źródle danych. Moduł EJB będzie bowiem korzystał z już utworzonej bazy [dbpam_eclipselink].
- Pod koniec pracy kreatora utworzono plik [persistence.xml] o nazwie [10]. Jego zawartość jest następująca:
<?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>
- wiersz 3: nazwa jednostki trwałości [mv-pam-ejb-metier-dao-eclipselinkPU] oraz typ transakcji (JTA dla kontenera EJB)
- wiersz 5: nazwa JNDI źródła danych wykorzystywanego przez warstwę trwałości: jdbc/dbpam_eclipselink
- wiersz 6: encje JPA nie są określone. Będą one wyszukiwane w ścieżce Classpath modułu EJB.
- Nie podano nazwy używanej implementacji JPA (Hibernate, EclipseLink, ...). W takim przypadku Glassfish v3 domyślnie używa EclipseLink.
7.1.1.3. Wstawianie warstw [jpa, DAO, metier]
Teraz, gdy plik [persistence.xml] został zdefiniowany, możemy przejść do wstawiania do projektu warstw [metier, dao, jpa] aplikacji korporacyjnej [pam]:
![]() |
Te trzy warstwy są identyczne jak w przypadku pliku OpenEJB. Można je po prostu skopiować i wkleić między tymi dwoma projektami. Właśnie to teraz robimy:
![]() |
- w [1], wynik skopiowania pakietów [jpa, dao, metier, exception] z projektu [mv-pam-openejb-eclipselink] do modułu EJB [mv-pam-ejb-metier-dao-jpa-eclipselink]
7.1.1.4. Konfiguracja serwera Glassfish
Pozostaje nam skonfigurować serwer Glassfish w dwóch aspektach:
- warstwa JPA jest zaimplementowana przez EclipseLink. Należy upewnić się, że serwer Glassfish posiada biblioteki tej implementacji JPA.
- Źródłem danych jest baza danych o nazwie MySQL. Należy upewnić się, że serwer Glassfish posiada sterownik JDBC dla tej bazy danych SGBD.
Brak tych bibliotek można wykryć podczas wdrażania modułu EJB. Oto jeden z wielu sposobów dodania brakujących bibliotek do serwera GlassFish:
![]() |
- na [1], wyświetlić właściwości serwera Glassfish
- w [2], zanotować lokalizację folderu domen serwera. Oznaczamy go dalej jako <domains>
- w folderze <domains>\domain1\lib umieść brakujące biblioteki. W przykładzie dodano biblioteki Hibernate (lib / hibernate-tools) oraz sterownik JDBC z MySQL (lib / diverse). Domyślnie serwer Glassfish posiada biblioteki EclipseLink. Dodamy zatem jedynie sterownik JDBC z pakietu MySQL.
![]() |
- w [1], w zakładce [Services] uruchamiamy serwer Glassfish v3
- w [2], jest on aktywny
7.1.1.5. Wdrożenie modułu EJB
Teraz wdrażamy moduł EJB na serwerze Glassfish:
![]() |
- w [1], moduł EJB został wdrożony
- na [2], struktura katalogów serwera Glassfish zostaje odświeżona
- na [3], po wdrożeniu moduł EJB pojawia się w gałęzi [Applications] serwera Glassfish
- w [4] na serwerze Glassfish utworzono zasób JDBC [jdbc / dbpam_eclipselink]. Przypominamy, że zdefiniowaliśmy go w punkcie 7.1.1.2.
Podczas wdrażania serwer Glassfish rejestruje w konsoli interesujące informacje:
W wierszach
- 3, 6, 8 i 11 na nazwy przenośne JNDI wdrożonych instancji EJB. W wersji Java EE 6 wprowadzono pojęcie nazwy przenośnej JNDI. Oznacza to nazwę JNDI rozpoznawaną przez wszystkie serwery Java EE 6. W przypadku Java EE 5 nazwy JNDI są specyficzne dla używanego serwera.
- 4, 7, 9, 12: nazwy JNDI modułów EJB wdrożonych w formie specyficznej dla Glassfish v3.
Nazwy te będą przydatne dla aplikacji konsolowej, którą zamierzamy napisać w celu korzystania z wdrożonego modułu EJB.
7.2. Konsola kliencka – wersja 1
Teraz, gdy wdrożyliśmy część serwerową naszej aplikacji klient-serwer, przechodzimy do analizy części klienckiej [1]:
![]() |
7.2.1. Projekt klienta
Tworzymy nowy projekt Maven typu [Java Application] o nazwie [mv-pam-client-ejb-metier-dao-eclipselink]:
![]() |
- w projekcie klienta [1]
W pliku [pom.xml] dodajemy następujące zależności:
<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>
- wiersze 31–35: zależność od biblioteki [gf-client], która umożliwia klientowi Classfish komunikację ze zdalnym serwerem,
- wiersze 36–41: zależność od projektu Maven modułu EJB. Chcemy tutaj pobrać definicje encji JPA oraz różnych interfejsów, a także klasy wyjątku [PamException],
Z projektu [mv-pam-openejb-eclipselink] kopiujemy klasę [MainRemote]:
![]() |
Klasa [MainRemote] musi uzyskać odwołanie do klasy EJB z warstwy [metier]. Kod klasy [MainRemote] zmienia się w następujący sposób:
// w porządku – można poprosić o odcinek wypłaty
FeuilleSalaire feuilleSalaire = null;
IMetierRemote metier = null;
try {
// kontekst JNDI serwera Glassfish
InitialContext initialContext = new InitialContext();
// instancjonowanie warstwy biznesowej
metier = (IMetierRemote) initialContext.lookup("java:global/istia.st_mv-pam-ejb-metier-dao-eclipselink_ejb_1.0-SNAPSHOT/Metier!metier.IMetierRemote");
// obliczanie listy płac
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;
}
- wiersz 6: inicjalizacja kontekstu JNDI serwera Glassfish.
- wiersz 8: żądamy od tego kontekstu JNDI odwołania do zdalnego interfejsu warstwy [metier]. Z logów Glassfish wynika, że zdalny interfejs warstwy [metier] może mieć dwie nazwy:
Wiersz 1: nazwa JNDI, która może być używana z dowolnym serwerem aplikacji: JAVA, EE, 6. Wiersz 2: nazwa JNDI, specyficzna dla Glassfish. W kodzie, w wierszu 9, używamy nazwy przenośnej JNDI.
- Pozostała część kodu pozostaje bez zmian
![]() |
W pliku [1] konfigurujemy projekt tak, aby uruchamiał klasę [MainRemote] z argumentami. Jeśli wszystko przebiegnie pomyślnie, wykonanie projektu da następujący wynik:
Jeśli w właściwościach wprowadzimy nieprawidłowy numer ubezpieczenia społecznego, otrzymamy następujący wynik:
7.3. Konsola klienta – wersja 2
W poprzednich wersjach środowisko JNDI serwera Glassfish było konfigurowane na podstawie pliku [jndi.properties] znajdującego się gdzieś w archiwach projektu. Jego domyślna zawartość jest następująca:
# dostęp JNDI do serwera aplikacji Sun
java.naming.factory.initial=com.sun.enterprise.naming.SerialInitContextFactory
java.naming.factory.url.pkgs=com.sun.enterprise.naming
# Wymagane jest dodanie javax.naming.spi.StateFactory dla CosNaming, który
# obsługuje dynamiczne RMI-IIOP.
java.naming.factory.state=com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl
org.omg.CORBA.ORBInitialHost=localhost
org.omg.CORBA.ORBInitialPort=3700
Wiersze 7 i 8 określają maszynę, na której działa usługa JNDI, oraz port, na którym ta usługa nasłuchuje. Plik ten nie pozwala na nawiązywanie połączeń z serwerem JNDI innym niż localhost ani z serwerem działającym na porcie innym niż 3700. Jeśli chcemy zmienić te dwa parametry, możemy stworzyć własny plik [jndi.properties] lub skorzystać z konfiguracji Spring. Pokażemy tę drugą technikę.
Zaczynamy od utworzenia nowego projektu na podstawie projektu początkowego [pam-client-metier-dao-jpa-eclipselink].
![]() |
- w [1], nowy projekt
- na [2], plik konfiguracyjny Springa o nazwie [spring-config-client.xml]. Jego zawartość jest następująca:
Plik konfiguracyjny Springa wygląda następująco:
<?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">
<!-- zawód -->
<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>
W tym przypadku używamy tagu <jee> (wiersz 14), który pojawił się wraz z Springiem 2.0. Zastosowanie tego tagu wymaga zdefiniowania schematu, do którego należy, w wierszach 4, 10 i 11.
- wiersz 14: tag <jee:jndi-lookup> pozwala uzyskać odwołanie do obiektu z serwisu JNDI. W tym przypadku kojarzymy bean o nazwie „metier” z zasobem JNDI powiązanym z EJB i [Metier]. Nazwa JNDI użyta w tym miejscu jest nazwą przenośną (Java EE 6) dla EJB.
- Zawartość pliku [jndi.properties] staje się zawartością tagu <jee:environment> (wiersz 15), który służy do definiowania parametrów połączenia z usługą JNDI.
Główna klasa [MainRemote] zmienia się w następujący sposób:
W wierszach 7–8 do Springa kierowane jest żądanie dotyczące odwołania typu [IMetierRemote] w warstwie [metier]. Rozwiązanie to zapewnia elastyczność naszej architektury. W rzeczywistości, gdyby EJB z warstwy [metier] stało się lokalne, c.a.d byłby uruchamiany w tej samej warstwie JVM co nasz klient [MainRemote], jego kod nie uległby zmianie. Zmieniłaby się jedynie zawartość pliku [spring-config-client.xml]. Uzyskalibyśmy wówczas konfigurację analogiczną do architektury Spring / JPA omówionej w paragrafie 5.11.
Zachęcamy czytelnika do przetestowania tej nowej wersji.
7.4. Klient Swing
Teraz tworzymy klienta swing dla naszej aplikacji klient-serwer EJB.
![]() |
Plik [pom.xml] musi posiadać niezbędne zależności od aplikacji Swing:
<dependency>
<groupId>org.swinglabs</groupId>
<artifactId>swing-layout</artifactId>
<version>1.0.3</version>
</dependency>
Powyżej klasa [PamJFrame] została pierwotnie napisana do działania w środowisku Spring / JPA:
![]() |
Teraz ta klasa musi stać się zdalnym klientem klasy EJB wdrożonej na serwerze Glassfish.
![]() |
Ćwiczenie praktyczne: kierując się przykładem klienta konsolowego [ui.console.MainRemote] z projektu, zmień sposób, w jaki metoda [doMyInit] (patrz punkt 5.12.4) klasy [PamJFrame] w celu uzyskania odwołania do warstwy [metier], która jest obecnie zdalna.

























