Skip to content

7. 版本 3:将 PAM 应用程序移植到 GlassFish 应用服务器

我们建议将 OpenEJB/EclipseLink 架构中 [业务] 和 [DAO] 层的 EJB 部署到 GlassFish 应用服务器的容器中。

当前基于 OpenEJB / EclipseLink 的实现

如上所述,[ui] 层使用 [business] 层的远程接口。

我们测试了两种执行环境:本地和远程。在远程模式下,[ui]层作为[business]层的客户端,而[business]层由EJB实现。为了在客户端/服务器模式下运行(即客户端和服务器分别在两个独立的JVM中运行),我们将把[business、DAO、JPA]层部署到GlassFish Java EE服务器上。该服务器已包含在NetBeans中。

将在 GlassFish 服务器上构建的实现

  • [UI] 层将在 Java SE(标准版)环境中运行
  • [业务逻辑、DAO、JPA] 层将在 GlassFish v3 服务器上的 Java EE(企业版)环境中运行
  • 客户端将通过 TCP/IP 网络与服务器通信。网络通信对开发人员而言是透明的,但他们仍需注意:客户端与服务器之间通过交换序列化对象进行通信,而非对象引用。此通信所使用的网络协议称为 RMI(远程方法调用),该协议仅适用于两个 Java 应用程序之间。
  • GlassFish 服务器上使用的 JPA 实现将采用 EclipseLink。

7.1. PAM 客户端/服务器应用程序中的服务器端——

7.1.1. 应用程序架构

在此,我们将探讨由 GlassFish 服务器的 EJB3 容器托管的服务器端组件:

我们的目标是将已在 OpenEJB 容器中开发并测试过的应用程序移植到 GlassFish 服务器上。这就是 OpenEJB 的优势,也是嵌入式 EJB 容器的普遍优势:它们允许我们在简化的运行时环境中测试应用程序。一旦应用程序通过测试,剩下的工作就是将其移植到目标服务器上,在本例中即 GlassFish 服务器。

7.1.1.1. NetBeans 项目

让我们从创建一个新的 NetBeans 项目开始:

  • 在 [1] 中,新建项目
  • 在 [2] 中选择 Maven 类别,在 [3] 中选择 EJB 模块类型。此操作旨在构建一个将由 EJB 容器(具体而言是 GlassFish 服务器)托管并运行的项目。
  • 使用 [4a] 按钮选择项目文件夹的父文件夹,或在 [4b] 中直接输入其名称。
  • 在 [5] 中,为项目命名
  • 在 [6] 中,选择项目将运行的应用服务器。此处选择的服务器是 [运行时 / 服务器] 选项卡中可见的选项之一,本例中为 GlassFish v3。
  • 在 [7] 中,选择 Java EE 版本。
  • 在 [1] 中,新建项目。它与标准 Java 项目有以下几点不同:
    • 会自动创建一个 [Other Sources] 分支 [2]。该分支将特别包含用于配置 JPA 层的 [persistence.xml] 文件,
    • 若您构建该项目(Build),将出现一个 [javaee-api-6.0] 依赖项 [3]。该依赖项属于“provided”类型,因为它由 Glassfish EJB 容器在运行时提供。

7.1.1.2. 配置持久化层

所谓“持久层配置”,是指编写 [persistence.xml] 文件,该文件定义:

  • 要使用的 JPA 实现
  • JPA 层所用数据源的定义。这将是一个由 GlassFish 服务器管理的 JDBC 数据源。

我们可以按以下步骤操作。首先,在 [运行时 / 数据库] 选项卡中,我们将创建 [1] 一个连接到 MySQL5 / dbpam_eclipselink 数据库的连接:

完成此操作后,我们可以继续创建 EJB 模块所使用的 JDBC 资源

  • 在 [1] 中,创建一个新文件——执行此操作前请确保已选中 EJB 项目
  • 在 [2] 中,选择 EJB 项目
  • 在 [3] 中,选择 [Glassfish] 类别
  • 在 [4] 中,选择“创建 JDBC 资源”
  • 在 [5] 中,指定该 JDBC 资源将使用一个新的连接池。请注意,连接池是一组已打开的连接,用于加快应用程序与数据库之间的交互。
  • 在 [6] 中,为创建的 JDBC 资源指定一个 JNDI 名称。该名称可以是任意内容,但通常采用 jdbc/name 的形式。此 JNDI 名称将在 [persistence.xml] 文件中用于指定 JPA 实现必须使用的数据源。
  • 在 [7] 中,为即将创建的连接池指定任意名称
  • 在下拉列表 [8] 中,选择之前为 MySQL 数据库 / dbpam_eclipselink 创建的 JDBC 连接。
  • 在 [9] 中,显示连接池属性的摘要——保持所有设置不变
  • 在 [10] 中,您可以指定连接池的若干属性——保留默认值
  • 在 [11] 中,完成为 EJB 模块创建 JDBC 资源的向导后,[Other Sources] 分支中生成了一个 [glassfish-resources.xml] 文件。该文件的内容如下:
// it's okay - you can ask for the payslip in the [metier] layer
    IMetierRemote metier = null;
    FeuilleSalaire feuilleSalaire = null;
    try {
       // configure the embedded Open EJB container
...
       // remote business layer instantiation
      metier = (IMetierRemote) initialContext.lookup("MetierRemote");
       // wage sheet calculation
      feuilleSalaire = metier.calculerFeuilleSalaire(args[0], nbHeuresTravaillées, nbJoursTravaillés);
    } catch (PamException ex) {
...
    } catch (Exception ex) {
...
    }

[glassfish-resources.xml] 文件是一个 XML 文件,其中包含向导收集的所有数据。当将 EJB 模块部署到 GlassFish 服务器时,NetBeans 将使用该文件请求创建此模块所需的 JDBC 资源。

现在您可以创建 [persistence.xml] 文件,该文件将配置 EJB 模块的 JPA 层:

  • 在 [1] 中,创建一个新文件——执行此操作前请确保已选中 EJB 项目
  • 在 [2] 中,选择 EJB 项目
  • 在 [3] 中,选择 [持久化] 类别
  • 在 [4] 中,创建一个持久化单元
  • 在 [5] 中,为持久化单元命名
  • 在 [6] 中,提供了多种 JPA 实现。此处请选择 [EclipseLink]。若需使用其他实现,请确保在 GlassFish 服务器库之外,同时包含这些实现的库。
  • 在下拉列表 [7] 中,选择刚刚创建的 JDBC 数据源 [jdbc/dbpam_eclipselink]。
  • 在 [8] 中,指定事务由 EJB 容器管理
  • 在 [9] 中,指定将 EJB 模块部署到服务器时,不应对数据源执行任何操作。这是因为 EJB 模块将使用已创建的 [dbpam_eclipselink] 数据库。
  • 向导结束时,将生成一个 [persistence.xml] 文件 [10]。其内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<!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>
  • 第 3 行:持久化单元的名称 [mv-pam-ejb-metier-dao-eclipselinkPU] 以及事务类型(EJB 容器的 JTA)
  • 第 5 行:持久化层使用的数据源的 JNDI 名称:jdbc/dbpam_eclipselink
  • 第 6 行:未指定 JPA 实体。系统将在 EJB 模块的类路径中搜索这些实体。
  • 未指定所使用的 JPA 实现(Hibernate、EclipseLink 等)的名称。在此情况下,GlassFish v3 默认使用 EclipseLink

7.1.1.3. 插入 [JPA、DAO、业务] 层

现在 [persistence.xml] 文件已定义完毕,我们可以继续将 [pam] 企业应用程序的 [业务、DAO、JPA] 层插入到项目中:

这三个层与 OpenEJB 中的完全一致。我们只需在两个项目之间进行复制粘贴即可。这就是我们目前正在做的事情:

  • 在 [1] 中,将 [mv-pam-openejb-eclipselink] 项目中的 [jpa, dao, business, exception] 包复制到 EJB 模块 [mv-pam-ejb-business-dao-jpa-eclipselink] 中的结果

7.1.1.4. 配置 GlassFish 服务器

我们还需要在两个方面配置 GlassFish 服务器:

  • JPA 层由 EclipseLink 实现。我们需要确保 GlassFish 服务器已安装该 JPA 实现所需的库。
  • 数据源为 MySQL 数据库。我们需要确保 GlassFish 服务器已安装该数据库管理系统的 JDBC 驱动程序。

在部署 EJB 模块时,您可能会发现这些库缺失。以下是向 GlassFish 服务器添加缺失库的几种方法之一:

  • 在 [1] 中,查看 GlassFish 服务器的属性
  • 在 [2] 中,记下服务器的 domains 文件夹。我们将它称为 <domains>
  • 在 <domains>\domain1\lib 文件夹中放置缺失的库文件。在本例中,添加了 Hibernate 库(lib/hibernate-tools)和 MySQL JDBC 驱动程序(lib/misc)。默认情况下,GlassFish 服务器已包含 EclipseLink 库。因此,我们只需添加 MySQL JDBC 驱动程序。
  • 在 [1] 中,于 [Services] 选项卡上,启动 GlassFish v3 服务器
  • 在 [2] 中,服务器处于活动状态

7.1.1.5. 部署 EJB 模块

现在,我们将 EJB 模块部署到 GlassFish 服务器上:

  • 在[1]中,已部署了EJB模块
  • 在 [2] 中,刷新了 GlassFish 服务器的目录树
  • 在 [3] 中,部署完成后,EJB 模块出现在 GlassFish 服务器的 [Applications] 分支中
  • 在 [4] 中,已在 GlassFish 服务器上创建了 JDBC 资源 [jdbc / dbpam_eclipselink]。请注意,我们在第 7.1.1.2 节中定义了该资源。

部署过程中,GlassFish 服务器会在控制台记录以下信息:


<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
  <persistence-unit name="mv-pam-ejb-metier-dao-eclipselinkPU" transaction-type="JTA">
    <jta-data-source>jdbc/dbpam_eclipselink</jta-data-source>
    <exclude-unlisted-classes>false</exclude-unlisted-classes>
    <properties/>
  </persistence-unit>
</persistence>

请注意,第

  • 3、6、8 和 11 行:部署的 EJB 的可移植 JNDI 名称。Java EE 6 引入了可移植 JNDI 名称的概念。这表示一个被所有 Java EE 6 服务器识别的 JNDI 名称。在 Java EE 5 中,JNDI 名称是特定于所使用的服务器的。
  • 第 4、7、9 和 12 行:部署的 EJB 的 JNDI 名称,采用 GlassFish v3 专有的格式。

这些名称对于我们即将编写、用于调用已部署 EJB 模块的控制台应用程序将非常有用。

7.2. 控制台客户端 - 版本 1

既然我们已经部署了客户端/服务器应用程序的服务器端,接下来我们将探讨客户端 [1]:

7.2.1. 客户端项目

我们创建一个名为 [mv-pam-client-ejb-metier-dao-eclipselink] 的 [Java 应用程序] 类型的新 Maven 项目:

  • 在 [1] 中,客户端项目

在 [pom.xml] 文件中,我们添加以下依赖项:

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.
  • 第 31–35 行:对 [gf-client] 库的依赖,该库允许 GlassFish 客户端与远程服务器通信,
  • 第 36–41 行:对 EJB 模块 Maven 项目的依赖。此处,我们需要获取 JPA 实体和各种接口的定义,以及异常类 [PamException] 的定义,

从 [mv-pam-openejb-eclipselink] 项目中,我们复制 [MainRemote] 类:

[MainRemote] 类必须获取 [business] 层中 EJB 的引用。 [MainRemote] 类的代码修改如下:


<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>
  • 第 6 行:初始化 GlassFish 服务器的 JNDI 上下文。
  • 第 8 行:查询此 JNDI 上下文以获取 [business] 层的远程接口引用。根据 GlassFish 日志,我们知道 [business] 层的远程接口有两个可能的名称:

// it's OK - we can ask for the payslip
    FeuilleSalaire feuilleSalaire = null;
    IMetierRemote metier = null;
    try {
      // context JNDI of Glassfish server
      InitialContext initialContext = new InitialContext();
      // business layer instantiation
      metier = (IMetierRemote) initialContext.lookup("java:global/istia.st_mv-pam-ejb-metier-dao-eclipselink_ejb_1.0-SNAPSHOT/Metier!metier.IMetierRemote");
      // wage sheet calculation
      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;
    }

第 1 行:可在任何 Java EE 6 应用服务器上使用的 JNDI 名称。第 2 行:GlassFish 专用的 JNDI 名称。在代码的第 9 行中,我们使用了可移植的 JNDI 名称。

  • 其余代码保持不变

在[1]中,我们配置了项目以带参数运行[MainRemote]类。如果一切顺利,运行该项目将得到以下结果:

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]

如果在属性中输入了错误的社会保障号码,将得到以下结果:

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)

7.3. 客户端控制台 - 版本 2

在以前的版本中,Glassfish 服务器的 JNDI 环境是通过位于项目归档文件中的 [jndi.properties] 文件进行配置的。其默认内容如下:

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

第 7 行和第 8 行指定了 JNDI 服务机及其监听端口。此文件不允许您查询除 localhost 以外的 JNDI 服务器,或查询运行在 3700 端口以外的 JNDI 服务器。如果您想更改这两个设置,可以创建自己的 [jndi.properties] 文件,或者使用 Spring 配置。我们将演示后一种方法。

首先,我们基于初始的 [pam-client-metier-dao-jpa-eclipselink] 项目创建一个新项目。

  • 在[1]中,新项目
  • 在 [2] 中,是 Spring 配置文件 [spring-config-client.xml]。其内容如下:

Spring 配置文件如下:


# accès JNDI à Sun Application Server
java.naming.factory.initial=com.sun.enterprise.naming.SerialInitContextFactory
java.naming.factory.url.pkgs=com.sun.enterprise.naming
# Required to add a javax.naming.spi.StateFactory for CosNaming that
# supports dynamic RMI-IIOP.
java.naming.factory.state=com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl
org.omg.CORBA.ORBInitialHost=localhost
org.omg.CORBA.ORBInitialPort=3700

这里我们使用了 Spring 2.0 中引入的 <jee> 标签(第 14 行)。使用该标签需要定义其所属的模式,即第 4、10 和 11 行。

  • 第 14 行:<jee:jndi-lookup> 标签用于从 JNDI 服务获取对象引用。在此,我们将名为“metier”的 Bean 与关联 [Metier] EJB 的 JNDI 资源相关联。此处使用的 JNDI 名称是该 EJB 的可移植名称(Java EE 6)。
  • [jndi.properties] 文件的内容将成为 <jee:environment> 标签(第 15 行)的内容,该标签用于定义 JNDI 服务的连接参数。

主类 [MainRemote] 发生如下变化:


<?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">
 
  <!-- business -->
  <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>

第 7–8 行:向 Spring 请求 [business] 层中的 [IMetierRemote] 类型引用。该方案为我们的架构带来了灵活性。事实上,如果 [business] 层中的 EJB 变为本地化——即与我们的 [MainRemote] 客户端在同一 JVM 中执行——客户端的代码将保持不变。 只需修改 [spring-config-client.xml] 文件的内容即可。届时,我们的配置将与第 5.11 节中讨论的 Spring/JPA 架构类似。

欢迎读者测试此新版本。

7.4. Swing 客户端

接下来,我们将为我们的 EJB 客户端/服务器应用程序构建 Swing 客户端。

[pom.xml] 文件必须包含 Swing 应用程序所需的依赖项:

...
     // it's okay - we can ask for the payslip
    FeuilleSalaire feuilleSalaire = null;
    IMetierRemote metier=null;
    try {
       // instantiation layer [metier]
      ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-config-client.xml");
      metier = (IMetierRemote) ctx.getBean("metier");
       // wage sheet calculation
      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;
    }
    ...

上文中的 [PamJFrame] 类最初是为在 Spring/JPA 环境中运行而编写的:

现在,该类必须成为部署在 Glassfish 服务器上的 EJB 的远程客户端。


实践练习:参照项目中 [ui.console.MainRemote] 控制台客户端的示例,修改 [PamJFrame] 类中 [doMyInit] 方法(参见第 5.12.4 节)的调用方式,以获取对 [business] 层的引用——该层现已变为远程层。