Skip to content

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:

INFO: Realm admin-realm of classtype com.sun.enterprise.security.auth.realm.file.FileRealm successfully created.
....
INFO: Portable JNDI names for EJB IndemniteDao : [java:global/pam-serveur-metier-dao-jpa-eclipselink/IndemniteDao!DAO.IIndemniteDaoLocal, java:global/pam-serveur-metier-dao-jpa-eclipselink/IndemniteDao!DAO.IIndemniteDaoRemote]
INFO: Glassfish-specific (Non-portable) JNDI names for EJB IndemniteDao : [DAO.IIndemniteDaoRemote#DAO.IIndemniteDaoRemote, DAO.IIndemniteDaoRemote]
...
INFO: Portable JNDI names for EJB CotisationDao : [java:global/pam-serveur-metier-dao-jpa-eclipselink/CotisationDao!DAO.ICotisationDaoLocal, java:global/pam-serveur-metier-dao-jpa-eclipselink/CotisationDao!DAO.ICotisationDaoRemote]
INFO: Glassfish-specific (Non-portable) JNDI names for EJB CotisationDao : [DAO.ICotisationDaoRemote, DAO.ICotisationDaoRemote#DAO.ICotisationDaoRemote]
INFO: Portable JNDI names for EJB Metier : [java:global/pam-serveur-metier-dao-jpa-eclipselink/Metier!metier.IMetierRemote, java:global/pam-serveur-metier-dao-jpa-eclipselink/Metier!metier.IMetierLocal]
INFO: Glassfish-specific (Non-portable) JNDI names for EJB Metier : [metier.IMetierRemote#metier.IMetierRemote, metier.IMetierRemote]
...
INFO: Portable JNDI names for EJB EmployeDao : [java:global/pam-serveur-metier-dao-jpa-eclipselink/EmployeDao!DAO.IEmployeDaoLocal, java:global/pam-serveur-metier-dao-jpa-eclipselink/EmployeDao!DAO.IEmployeDaoRemote]
INFO: Glassfish-specific (Non-portable) JNDI names for EJB EmployeDao : [DAO.IEmployeDaoRemote#DAO.IEmployeDaoRemote, DAO.IEmployeDaoRemote]
INFO: pam-serveur-metier-dao-jpa-eclipselink was successfully deployed in 12 891 milliseconds.

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:
Infos: EJB5181:Portable JNDI names for EJB Metier: [java:global/istia.st_mv-pam-ejb-metier-dao-eclipselink_ejb_1.0-SNAPSHOT/Metier!metier.IMetierLocal, java:global/istia.st_mv-pam-ejb-metier-dao-eclipselink_ejb_1.0-SNAPSHOT/Metier!metier.IMetierRemote]
Infos: EJB5182:Glassfish-specific (Non-portable) JNDI names for EJB Metier: [metier.IMetierRemote#metier.IMetierRemote, metier.IMetierRemote]

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:

run:
Valeurs saisies :
N° de sécurité sociale de l'employé : 254104940426058
Nombre d'heures travaillées : 150
Nombre de jours travaillés : 20

Informations Employé : 
Nom : Jouveinal
Prénom : Marie
Adresse : 5 rue des oiseaux
Ville : St Corentin
Code Postal : 49203
Indice : 2

Informations Cotisations : 
CSGRDS : 3.49 %
CSGD : 6.15 %
Retraite : 7.88 %
Sécurité sociale : 9.39 %

Informations Indemnités : 
Salaire horaire : 2.1 euro
Entretien/jour : 2.1 euro
Repas/jour : 3.1 euro
Congés Payés : 15.0 %

Informations Salaire : 
Salaire de base : 362.25 euro
Cotisations sociales : 97.48 euro
Indemnités d'entretien : 42.0 euro
Indemnités de repas : 62.0 euro
Salaire net : 368.77 euro

BUILD SUCCESSFUL (total time: 2 seconds)

Jeśli w właściwościach wprowadzimy nieprawidłowy numer ubezpieczenia społecznego, otrzymamy następujący wynik:

1
2
3
run:
L'erreur suivante s'est produite : L'employé de n°[254104940426058x] est introuvable
BUILD SUCCESSFUL (total time: 2 seconds)

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 porządku – można poprosić o odcinek wypłaty
    FeuilleSalaire feuilleSalaire = null;
    IMetierRemote metier=null;
    try {
       // instancja warstwy [metier]
      ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-config-client.xml");
      metier = (IMetierRemote) ctx.getBean("metier");
       // obliczenie 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;
    }
    ...

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.