Skip to content

6. نسخهٔ ۲: معماری OpenEJB / JPA

6.1. مقدمه‌ای بر اصول مهاجرت

در اینجا اصولی را که بر انتقال یک برنامه JPA / Spring / Hibernate به یک برنامه JPA / OpenEJB / EclipseLink حاکم است، تشریح می‌کنیم. برای ایجاد پروژه‌های Maven تا بخش 6.2 صبر خواهیم کرد.

6.1.1. دو معماری

پیاده‌سازی فعلی با Spring / Hibernate

پیاده‌سازی که باید با استفاده از OpenEJB / EclipseLink ساخته شود

6.1.2. کتابخانه‌های پروژه

  • لایه‌های [DAO] و [metier] دیگر توسط Spring نمونه سازی نمی‌شوند. آنها توسط کانتینر OpenEJB نمونه سازی می‌شوند.
  • کتابخانه‌ها و پیکربندی کانtejner Spring با کتابخانه‌ها و پیکربندی کانtejner OpenEJB جایگزین می‌شوند.
  • کتابخانه‌های لایه JPA / Hibernate با کتابخانه‌های لایه JPA / EclipseLink جایگزین می‌شوند

  • فایل [META-INF/persistence.xml] که لایه JPA را پیکربندی می‌کند، به شرح زیر درمی‌آید:
<?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="pam-openejb-ui-metier-dao-jpa-eclipselinkPU" transaction-type="JTA">
     <!-- وجودیت‌ها JPA -->
    <class>jpa.Cotisation</class>
    <class>jpa.Employe</class>
    <class>jpa.Indemnite</class>
     <!--ارائه‌دهنده JPA است EclipseLink -->
    <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
     <!-- ویژگی‌های ارائه‌دهنده -->
    <properties>
      <property name="eclipselink.ddl-generation" value="create-tables"/>
    </properties>
  </persistence-unit>
</persistence>
  • خط ۳: تراکنش‌های درون یک کانتینر EJB از نوع JTA (تراکنش جاوا API) هستند. با Spring، آن‌ها از نوع RESOURCE_LOCAL بودند.
  • خط ۹: پیاده‌سازی JPA که استفاده می‌شود، EclipseLink است.
  • خطوط ۵–۷: اِنتِتی‌های مدیریت‌شده توسط لایه JPA
  • خطوط ۱۱–۱۳: ویژگی‌های ارائه‌دهنده EclipseLink
  • خط ۱۲: جداول در هر اجرا ایجاد خواهند شد

ویژگی‌های JDBC منبع داده JTA که توسط کانتینر OpenEJB استفاده می‌شود، توسط فایل پیکربندی زیر [conf/openejb.conf] مشخص خواهد شد:

1
2
3
4
5
6
7
8
<?xml version="1.0"?>
<openejb>
  <Resource id="Default JDBC Database">
    JdbcDriver com.mysql.jdbc.Driver
    JdbcUrl jdbc:mysql://localhost:3306/dbpam_eclipselink
    UserName root
  </Resource>
</openejb>
  • خط ۳: شناسه «پایگاه داده پیش‌فرض JDBC» هنگام کار با یک کانتینر OpenEJB که در خود برنامه تعبیه شده است، استفاده می‌شود.
  • خط ۵: ما از پایگاه داده MySQL [dbpam_eclipselink] استفاده می‌کنیم

6.1.4. پیاده‌سازی لایه [DAO] توسط EJB

  • کلاس‌های پیاده‌ساز لایه [DAO] به EJB تبدیل می‌شوند. بیایید مثال کلاس [CotisationDao] را در نظر بگیریم:

رابط [ICotisationDao] در نسخه Spring به شرح زیر بود:

package dao;

import java.util.List;
import jpa.Cotisation;

public interface ICotisationDao {
   //ایجاد عضویت جدید
  Cotisation create(Cotisation cotisation);
   // ویرایش یک مشارکت موجود
  Cotisation edit(Cotisation cotisation);
   // حذف یک مشارکت موجود
  void destroy(Cotisation cotisation);
   // جستجوی یک مشارکت خاص
  Cotisation find(Long id);
   //بازیابی تمام سوابق مشارکت
  List<Cotisation> findAll();

}

EJB این همان رابط را در دو شکل مختلف پیاده‌سازی خواهد کرد: یک شکل محلی و یک شکل از راه دور. رابط محلی می‌تواند توسط کلاینتی که در همان JVM اجرا می‌شود، استفاده شود، در حالی که رابط راه دور می‌تواند توسط کلاینتی که در یک JVM متفاوت اجرا می‌شود، استفاده شود.

رابط محلی:

1
2
3
4
5
6
7
package dao;

import javax.ejb.Local;

@Local
public interface ICotisationDaoLocal extends ICotisationDao{
}
  • خط ۶: رابط [ICotisationDaoLocal] از رابط [ICotisationDao] ارث می‌برد تا تمام متدهای آن را به کار گیرد. این رابط هیچ متد جدیدی اضافه نمی‌کند.
  • خط ۵: تگ @Local آن را به یک رابط محلی برای EJB تبدیل می‌کند که آن را پیاده‌سازی خواهد کرد.

رابط راه دور:

1
2
3
4
5
6
7
package dao;

import javax.ejb.Remote;

@Remote
public interface ICotisationDaoRemote extends ICotisationDao{
}
  • خط ۶: رابط [ICotisationDaoRemote] از رابط [ICotisationDao] ارث می‌برد تا تمام متدهای آن را به کار گیرد. این رابط هیچ متد جدیدی اضافه نمی‌کند.
  • خط ۵: تگ @Remote آن را به یک رابط از راه دور برای EJB تبدیل می‌کند که آن را پیاده‌سازی خواهد کرد.

لایه [DAO] توسط کلاسی به نام EJB پیاده‌سازی شده است که هر دو رابط را پیاده‌سازی می‌کند (این امر اجباری نیست):

1
2
3
4
5
6
@Stateless()
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public class CotisationDao implements ICotisationDaoLocal, ICotisationDaoRemote {

  @PersistenceContext
  private EntityManager em;
  • خط ۱: تگ @Stateless که کلاس را به یک EJB تبدیل می‌کند
  • خط ۲: anotasyon @TransactionAttribute، که تضمین می‌کند هر متد در کلاس در داخل یک تراکنش اجرا شود.
  • خط ۵: anotasyon @PersistenceContext که EntityManager را از لایه JPA به کلاس [CotisationDao] تزریق می‌کند. این دقیقاً مشابه چیزی است که در نسخه Spring داشتیم.

وقتی از رابط محلی لایه [DAO] استفاده می‌شود، کلاینت این رابط در همان JVM اجرا می‌شود.

در مثال بالا، لایه‌های [metier] و [DAO] اشیاء را به‌صورت مرجع مبادله می‌کنند. وقتی یکی از لایه‌ها شیء مشترک را تغییر می‌دهد، لایه دیگر این تغییر را مشاهده می‌کند.

وقتی از رابط دوربرد لایه [DAO] استفاده می‌شود، کلاینت آن رابط معمولاً در یک JVM دیگر اجرا می‌شود.

در مثال بالا، لایه‌های [metier] و [DAO] اشیاء را به صورت مقدار مبادله می‌کنند (سریال‌سازی شیء مبادله شده). وقتی یک لایه یک شیء مشترک را تغییر می‌دهد، لایه دیگر تنها در صورتی این تغییر را مشاهده می‌کند که شیء اصلاح شده برای آن ارسال شود.

6.1.5. پیاده‌سازی لایه [metier] توسط یک EJB

  • کلاسی که لایه [metier] را پیاده‌سازی می‌کند، همچنین به یک EJB تبدیل می‌شود که یک رابط محلی و یک رابط از راه دور را پیاده‌سازی می‌کند. رابط اصلی [IMetier] به شرح زیر بود:
package metier;

import java.util.List;
import jpa.Employe;

public interface IMetier {
   // بازیابی فیش حقوقی
  FeuilleSalaire calculerFeuilleSalaire(String SS, double nbHeuresTravaillées, int nbJoursTravaillés );
   // فهرست کارمندان
  List<Employe> findAllEmployes();
}

ما یک رابط محلی و یک رابط از راه دور را بر اساس رابط قبلی ایجاد می‌کنیم:

1
2
3
4
5
6
7
package metier;

import javax.ejb.Local;

@Local
public interface IMetierLocal extends IMetier{
}
1
2
3
4
5
6
7
package metier;

import javax.ejb.Remote;

@Remote
public interface IMetierRemote extends IMetier{
}

کلاس EJB در لایه [metier] این دو رابط را پیاده‌سازی می‌کند:

@Stateless()
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public class Metier implements IMetierLocal, IMetierRemote {

   // مرجع لایه‌های محلی [DAO]
  @EJB
  private ICotisationDaoLocal cotisationDao = null;
  @EJB
  private IEmployeDaoLocal employeDao = null;
  @EJB
  private IIndemniteDaoLocal indemniteDao = null;
  • خطوط ۱–۲: یک EJB را تعریف می‌کنند که در آن هر متد درون یک تراکنش اجرا می‌شود.
  • خط ۷: مرجعی به رابط محلی EJB [CotisationDao].
  • خط ۶: تذکر @EJB به کانtejینر EJB دستور می‌دهد تا مرجعی به رابط محلی EJB [CotisationDao] تزریق کند.
  • خطوط ۸–۱۱: همین فرایند برای رابط‌های محلی EJB، [EmployeDao] و [IndemniteDao] تکرار می‌شود.

در نهایت، هنگامی که EJB و [Metier] نمونه برداری می‌شوند، میدان‌های روی خطوط ۷، ۹ و ۱۱ با ارجاع به رابط‌های محلی سه نمونه EJB در لایه [DAO] مقداردهی اولیه خواهند شد. بنابراین در اینجا فرض می‌کنیم که لایه‌های [metier] و [DAO] در همان JVM اجرا خواهند شد.

6.1.6. مشتریان EJB

در نمودار بالا، برای برقراری ارتباط با لایه [metier]، لایه [ui] باید یک مرجع به رابط دور لایه EJB را از لایه [metier] دریافت کند.

در نمودار بالا، برای ارتباط با لایه [metier]، لایه [ui] باید یک مرجع به رابط محلی لایه EJB را از لایه [metier] دریافت کند. روش به‌دست‌آوردن این ارجاعات از یک کانتینر به کانتینر دیگر متفاوت است. برای کانتینر OpenEJB، می‌توان رویه زیر را دنبال کرد:

مرجع روی رابط محلی:

1
2
3
4
5
6
7
8
9
     //پیکربندی کانتینر Open جاسازی‌شده EJB
    Properties properties = new Properties();
    properties.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.apache.openejb.client.LocalInitialContextFactory");
      // ابتدایی‌سازی زمینه JNDI با ویژگی‌های قبلی
    InitialContext initialContext = new InitialContext(properties);
     //نمونه‌سازی لایه‌های DAO
    employeDao = (IEmployeDaoLocal) initialContext.lookup("EmployeDaoLocal");
    cotisationDao = (ICotisationDaoLocal) initialContext.lookup("CotisationDaoLocal");
indemniteDao = (IIndemniteDaoLocal) initialContext.lookup("IndemniteDaoLocal");
  • خطوط ۲–۵: کانتینر OpenEJB مقداردهی اولیه می‌شود.
  • خط ۵: یک زمینه JNDI (رابط نام‌گذاری و دایرکتوری جاوا) وجود دارد که امکان به‌دست‌آوردن ارجاعات برای EJBها را فراهم می‌کند. هر EJB با نام JNDI مشخص می‌شود:
  • (ادامه)
    • برای رابط محلی، «Local» به نام EJB اضافه می‌شود (خطوط ۷–۹)
    • برای رابط محلی، «Local» به نام EJB اضافه می‌شود

با جاوا EE 5، این قوانین بسته به کانتینر EJB متفاوت هستند. این یک چالش ایجاد می‌کند. جاوا EE 6 نشانه‌ای JNDI را معرفی کرد که در تمام سرورهای برنامه‌ای قابل حمل است.

کد قبلی ارجاعات به رابط‌های محلی EJB را از طریق نام‌های JNDI بازیابی می‌کند. قبلاً اشاره کردیم که این ارجاعات را می‌توان از طریق حاشیه‌نویسی @EJB نیز به‌دست آورد. بنابراین ممکن است بخواهیم بنویسیم:

@EJB
private IemployeDaoLocal employeDaoLocal ;

توضیحیه @EJB تنها در صورتی معتبر است که به کلاسی تعلق داشته باشد که توسط کانتینر EJB بارگذاری شده باشد. برای مثال، این مورد برای کلاس [Metier] صدق می‌کند. با این حال، کد بالا به یک کلاس کنسول تعلق دارد که توسط کانتینر EJB بارگذاری نخواهد شد. بنابراین، ما مجبوریم از نام‌های JNDI یا EJB استفاده کنیم.

کد زیر برای به دست آوردن یک مرجع به رابط دور EJB و [Metier] است:

1
2
3
4
5
6
7
8
     //پیکربندی کانتینر Open جاسازی‌شده EJB
    Properties properties = new Properties();
    properties.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.apache.openejb.client.LocalInitialContextFactory");
     // ابتدایی‌سازی زمینه JNDI برای مخزن EJB
    InitialContext initialContext = new InitialContext(properties);

     // مثال‌سازی لایه کسب‌وکار راه دور
metier = (IMetierRemote) initialContext.lookup("MetierRemote");

6.2. تمرین عملی

ما پیشنهاد می‌کنیم که اپلیکیشن NetBeans Spring/Hibernate را به معماری OpenEJB / EclipseLink منتقل کنیم.

پیاده‌سازی فعلی با استفاده از Spring / Hibernate

پیاده‌سازی مورد نظر با استفاده از OpenEJB / EclipseLink

اگر وجود ندارد، پایگاه داده MySQL [dbpam_eclipselink] را ایجاد کنید. اگر وجود دارد، تمام جداول آن را حذف کنید. یک اتصال NetBeans به این پایگاه داده همان‌طور که در بخش 6.2.1 توضیح داده شده است ایجاد کنید.

6.2.2. پیکربندی اولیه پروژه NetBeans

  • پروژه Maven با شناسه [mv-pam-spring-hibernate] را بارگذاری کنید
  • یک پروژه جدید Maven Java به نام [mv-pam-openejb-eclipselink] و [1] ایجاد کنید
  • در زبانه [Files] [2]، یک پوشه با نام [conf] [3] زیر ریشه پروژه ایجاد کنید
  • فایل زیر را در این پوشه قرار دهید: [openejb.conf] [4]:
1
2
3
4
5
6
7
8
<?xml version="1.0"?>
<openejb>
  <Resource id="Default JDBC Database">
    JdbcDriver com.mysql.jdbc.Driver
    JdbcUrl jdbc:mysql://localhost:3306/dbpam_eclipselink
    UserName root
  </Resource>
</openejb>
  • پوشه [src / main/ resources/ META-INF] [5] را ایجاد کنید
  • فایل‌های زیر را در آن قرار دهید: [persistence.xml] [6]:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.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_1_0.xsd">
  <persistence-unit name="dbpam_eclipselinkPU" transaction-type="JTA">
    <!--ارائه‌دهنده JPA است EclipseLink -->
    <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
    <!-- اشیاء Jpa -->
    <class>jpa.Cotisation</class>
    <class>jpa.Employe</class>
    <class>jpa.Indemnite</class>
    <!-- ویژگی‌های ارائه‌دهنده EclipseLink -->
    <properties>
      <property name="eclipselink.logging.level" value="FINE"/>
      <property name="eclipselink.ddl-generation" value="drop-and-create-tables"/>
    </properties>
  </persistence-unit>
</persistence>
  • خط ۱۲: لاگ‌های تفصیلی از EclipseLink درخواست می‌شود،
  • خط ۱۳: جداول هنگام نمونه‌سازی لایه JPA ایجاد خواهند شد،
  • کتابخانه‌های OpenEJB، EclipseLink و درایور JDBC از MySQL را به فایل [pom.xml] در پروژه اضافه کنید:

<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-openejb-eclipselink</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>mv-pam-openejb-eclipselink</name>
  <url>http://maven.apache.org</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.apache.openejb</groupId>
      <artifactId>openejb-core</artifactId>
      <version>4.0.0</version>
    </dependency>                
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.10</version>
      <scope>test</scope>
      <type>jar</type>
    </dependency>
    <dependency>
      <groupId>org.eclipse.persistence</groupId>
      <artifactId>eclipselink</artifactId>
      <version>2.3.0</version>
    </dependency>
    <dependency>
      <groupId>org.eclipse.persistence</groupId>
      <artifactId>javax.persistence</artifactId>
      <version>2.0.3</version>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.6</version>
    </dependency>
    <dependency>
      <groupId>org.swinglabs</groupId>
      <artifactId>swing-layout</artifactId>
      <version>1.0.3</version>
    </dependency>
  </dependencies>
  
  <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>
  </repositories>
  
</project>
  • خطوط ۱۸–۲۲: وابستگی OpenEJB،
  • خطوط ۳۰–۳۹: وابستگی‌های EclipseLink،
  • خطوط ۴۱–۴۴: وابستگی درایور JDBC به MySQL

6.2.3. پورت کردن لایه [DAO]

ما لایه [DAO] را با کپی کردن بسته‌ها از پروژه [mv-pam-spring-hibernate] به پروژه [mv-pam-openejb-eclipselink] منتقل خواهیم کرد.

  • پکیج‌های [dao, exception, jpa] را کپی کنید
 

خطاهای گزارش‌شده در بالا به این دلیل است که لایه کپی‌شده [DAO] از Spring استفاده می‌کند و کتابخانه‌های Spring دیگر بخشی از پروژه نیستند.

6.2.3.1. EJB [CotisationDao]

ما در حال ایجاد رابط‌های محلی و راه دور برای EJB و [CotisationDao] آینده هستیم:

رابط محلی ICotisationDaoLocal:

1
2
3
4
5
6
7
package dao;

import javax.ejb.Local;

@Local
public interface ICotisationDaoLocal extends ICotisationDao{
}

برای دریافت بسته‌های صحیح import، [clic droit sur le code / Fix Imports] را اجرا کنید.

رابط راه دور ICotisationDaoRemote:

1
2
3
4
5
6
7
package dao;

import javax.ejb.Remote;

@Remote
public interface ICotisationDaoRemote extends ICotisationDao{
}

سپس کلاس [CotisationDao] را اصلاح می‌کنیم تا آن را به EJB تبدیل کنیم:

...
import javax.persistence.PersistenceContext;
import jpa.Cotisation;

@Stateless
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public class CotisationDao implements ICotisationDaoLocal, ICotisationDaoRemote {

  @PersistenceContext
  private EntityManager em;
...  

import که این کلاس روی فریم‌ورک Spring تولید می‌کرد دیگر وجود ندارد. یک [Clean and Build] برای پروژه تولید کنید:

در [1]، دیگر هیچ خطایی در کلاس [CotisationDao] وجود ندارد.

6.2.3.2. EJB، [EmployeDao] و [IndemniteDao]

ما همین رویه را برای سایر عناصر لایه [DAO] تکرار می‌کنیم:

  • رابط‌های IEmployeDaoLocal و IEmployeDaoRemote که از IEmployeDao مشتق شده‌اند
  • EJB و EmployeDao که این دو رابط را پیاده‌سازی می‌کنند
  • رابط‌های IIndemniteDaoLocal و IIndemniteDaoRemote، مشتق‌شده از IIndemniteDao
  • EJB و IndemniteDao، که این دو رابط را پیاده‌سازی می‌کنند

پس از انجام این کار، دیگر هیچ خطایی در پروژه [2] وجود ندارد.

6.2.3.3. کلاس [PamException]

کلاس [PamException] همان‌طور که بود باقی می‌ماند، با یک تفاوت جزئی:

package exception;

import javax.ejb.ApplicationException;

@ApplicationException(rollback=true)
public class PamException extends RuntimeException {

   // کد خطا
  private int code;
...

خط ۵ اضافه شده است. برای دریافت import صحیح، آن را به [Fix imports] تغییر دهید.

برای درک حاشیه‌نویسی روی خط ۵، مهم است به خاطر داشته باشید که هر متد در کلاس EJB در لایه [DAO] ما:

  • درون یک تراکنش که توسط کانتینر EJB آغاز و پایان می‌یابد، اجرا می‌شود
  • به محض اینکه مشکلی پیش بیاید، یک استثنا از نوع [PamException] پرتاب می‌کند

وقتی لایه [metier] متد M از لایه [DAO] را فراخوانی می‌کند، این فراخوانی توسط کانتینر EJB رهگیری می‌شود. گویی یک کلاس واسط بین لایه [metier] و لایه [DAO] – که در اینجا [Proxy EJB] نامیده می‌شود – وجود دارد که تمام فراخوانی‌های لایه [DAO] را رهگیری می‌کند. وقتی فراخوانی متد M در لایه [DAO] رهگیری می‌شود، پروکسی EJB یک تراکنش را آغاز می‌کند و سپس کنترل را به متد M در لایه [DAO] واگذار می‌کند که سپس در آن تراکنش اجرا می‌شود. روش M ممکن است با یا بدون استثنا خاتمه یابد.

  • اگر متد M بدون خطا به پایان برسد، اجرای کد به پروکسی EJB بازمی‌گردد که با commit کردن تراکنش از طریق commit، آن را خاتمه می‌دهد. سپس جریان اجرایی به متد فراخوانی‌کننده در لایه [metier] بازمی‌گردد.
  • اگر متد M با یک استثنا خاتمه یابد، کنترل به پروکسی EJB بازمی‌گردد، که تراکنش را از طریق rollback باطل کرده و آن را خاتمه می‌دهد. علاوه بر این، این استثنا را در یک نوع EJBException محصور می‌کند. کنترل سپس به متد فراخوانی‌کننده لایه [metier] بازگردانده می‌شود که در نتیجه یک EJBException دریافت می‌کند. تفسیر متنی در خط ۵ بالا از این پوشانندگی جلوگیری می‌کند. لایه [metier] در نتیجه یک PamException دریافت خواهد کرد. علاوه بر این، ویژگی rollback=true به پروکسی EJB دستور می‌دهد که وقتی یک PamException دریافت می‌کند، باید تراکنش را بازگردانَد.

6.2.3.4. آزمایش لایه [DAO]

لایه [DAO] ما که توسط EJB پیاده‌سازی شده است، آماده آزمایش است. ما با کپی کردن بسته [dao] از [Test Packages] در پروژه [mv-pam-springhibernate] به پروژه در حال توسعه [1] شروع می‌کنیم:

ما فقط کلاس تست [JUnitInitDB] را که پایگاه داده را با برخی از داده‌های [2] اولیه می‌کند، نگه می‌داریم. ما نام کلاس [ JUnitInitDbLocal] را به [3] تغییر می‌دهیم. کلاس [JUnitInitDBLocal] از رابط محلی EJB در لایه [DAO] استفاده خواهد کرد.

ابتدا، کلاس [JUnitInitDBLocal] را به شرح زیر اصلاح می‌کنیم:

public class JUnitInitDBLocal {

  static private IEmployeDaoLocal employeDao = null;
  static private ICotisationDaoLocal cotisationDao = null;
  static private IIndemniteDaoLocal indemniteDao = null;

  @BeforeClass
  public static void init() throws Exception {
     //پیکربندی کانتینر تعبیه‌شده Open EJB
    Properties properties = new Properties();
    properties.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.apache.openejb.client.LocalInitialContextFactory");
      // ابتدایی‌سازی زمینه JNDI با ویژگی‌های قبلی
    InitialContext initialContext = new InitialContext(properties);
     // نمادسازی لایه‌های محلی DAO
    employeDao = (IEmployeDaoLocal) initialContext.lookup("EmployeDaoLocal");
    cotisationDao = (ICotisationDaoLocal) initialContext.lookup("CotisationDaoLocal");
    indemniteDao = (IIndemniteDaoLocal) initialContext.lookup("IndemniteDaoLocal");
}

...
  • خطوط ۳–۵: ارجاع به رابط‌های محلی EJB در لایه [DAO]
  • خط ۷: @BeforeClass متدی را که هنگام شروع تست JUnit اجرا می‌شود، نشانه‌گذاری می‌کند
  • خطوط ۱۰–۱۳: инициалиزه‌سازی کانتینر OpenEJB. این инициалиزه‌سازی اختصاصی است و برای هر کانتینر EJB متفاوت است.
  • خط ۱۳: یک زمینه JNDI (رابط نام‌گذاری و دایرکتوری جاوا) وجود دارد که دسترسی به EJB را از طریق نام‌ها فراهم می‌کند. در OpenEJB، رابط محلی یک EJB E توسط ELocal و رابط راه دور توسط ERemote تعیین می‌شود.
  • خطوط ۱۵–۱۷: از زمینه JNDI خواسته شده است مرجعی به رابط‌های محلی EJB و [EmployeDao, CotisationDao, IndemniteDao] ارائه دهد.

پروژه را بسازید، در صورت لزوم سرور MySQL را راه‌اندازی کنید و تست JUnitInitDBLocal را اجرا کنید. لطفاً توجه داشته باشید که فایل [persistence.xml] طوری پیکربندی شده است که در هر اجرا، جداول را دوباره ایجاد کند. قبل از اجرای تست، توصیه می‌شود هرگونه جدول را از پایگاه‌های داده MySQL و [dbpam_eclipselink] حذف کنید.

  • در [1]، در برگه [Services]، جداول را از اتصال NetBeans که در بخش 6.2.1 ایجاد شده است، حذف کنید.
  • در [2]، پایگاه داده [dbpam_eclipselink] دیگر هیچ جدولی ندارد
  • در [3]، پروژه ساخته می‌شود
  • در [4]، تست JUnitInitDBLocal اجرا می‌شود
  • در [5]، تست با موفقیت انجام شد
  • در [6]، اتصال NetBeans تازه می‌شود
  • در [7]، چهار جدولی که توسط لایه JPA ایجاد شده‌اند نمایش داده می‌شوند. هدف از این تست، پر کردن آن‌ها بود. محتوای یکی از آن‌ها نمایش داده می‌شود
  • در [8]، محتویات جدول [EMPLOYES]

کانتینر OpenEJB لاگ‌ها را در کنسول نمایش داد:

Infos - PersistenceUnit(name=dbpam_eclipselinkPU, provider=org.eclipse.persistence.jpa.PersistenceProvider) - provider time 396ms
Infos - Jndi(name=CotisationDaoLocal) --> Ejb(deployment-id=CotisationDao)
Infos - Jndi(name=global/classpath.ear/mv-pam-openejb-eclipselink/CotisationDao!dao.ICotisationDaoLocal) --> Ejb(deployment-id=CotisationDao)
Infos - Jndi(name=CotisationDaoRemote) --> Ejb(deployment-id=CotisationDao)
Infos - Jndi(name=global/classpath.ear/mv-pam-openejb-eclipselink/CotisationDao!dao.ICotisationDaoRemote) --> Ejb(deployment-id=CotisationDao)
Infos - Jndi(name=global/classpath.ear/mv-pam-openejb-eclipselink/CotisationDao) --> Ejb(deployment-id=CotisationDao)
Infos - Jndi(name=EmployeDaoLocal) --> Ejb(deployment-id=EmployeDao)
Infos - Jndi(name=global/classpath.ear/mv-pam-openejb-eclipselink/EmployeDao!dao.IEmployeDaoLocal) --> Ejb(deployment-id=EmployeDao)
Infos - Jndi(name=EmployeDaoRemote) --> Ejb(deployment-id=EmployeDao)
Infos - Jndi(name=global/classpath.ear/mv-pam-openejb-eclipselink/EmployeDao!dao.IEmployeDaoRemote) --> Ejb(deployment-id=EmployeDao)
Infos - Jndi(name=global/classpath.ear/mv-pam-openejb-eclipselink/EmployeDao) --> Ejb(deployment-id=EmployeDao)
Infos - Jndi(name=IndemniteDaoLocal) --> Ejb(deployment-id=IndemniteDao)
Infos - Jndi(name=global/classpath.ear/mv-pam-openejb-eclipselink/IndemniteDao!dao.IIndemniteDaoLocal) --> Ejb(deployment-id=IndemniteDao)
Infos - Jndi(name=IndemniteDaoRemote) --> Ejb(deployment-id=IndemniteDao)
Infos - Jndi(name=global/classpath.ear/mv-pam-openejb-eclipselink/IndemniteDao!dao.IIndemniteDaoRemote) --> Ejb(deployment-id=IndemniteDao)
Infos - Jndi(name=global/classpath.ear/mv-pam-openejb-eclipselink/IndemniteDao) --> Ejb(deployment-id=IndemniteDao)
Infos - existing thread singleton service in SystemInstance() org.apache.openejb.cdi.ThreadSingletonServiceImpl@624a240d
Infos - OpenWebBeans Container is starting...
Infos - Adding OpenWebBeansPlugin : [CdiPlugin]
Infos - All injection points were validated successfully.
Infos - OpenWebBeans Container has started, it took [70] ms.
Infos - Created Ejb(deployment-id=IndemniteDao, ejb-name=IndemniteDao, container=Default Stateless Container)
Infos - Created Ejb(deployment-id=EmployeDao, ejb-name=EmployeDao, container=Default Stateless Container)
Infos - Created Ejb(deployment-id=CotisationDao, ejb-name=CotisationDao, container=Default Stateless Container)
Infos - Started Ejb(deployment-id=IndemniteDao, ejb-name=IndemniteDao, container=Default Stateless Container)
Infos - Started Ejb(deployment-id=EmployeDao, ejb-name=EmployeDao, container=Default Stateless Container)
Infos - Started Ejb(deployment-id=CotisationDao, ejb-name=CotisationDao, container=Default Stateless Container)
Infos - Deployed Application(path=D:\data\istia-1112\netbeans\glassfish\mv-pam\tmp\mv-pam-openejb-eclipselink\classpath.ear)
  • خطوط ۲–۳: دو نام JNDI از EJB و [CotisationDaoLocal
  • خطوط ۴–۵: دو نام JNDI از EJB و [CotisationDaoRemote
  • سطور ۷–۸: دو نام JNDI از EJB و [EmployeDaoLocal
  • سطور ۹–۱۰: دو نام JNDI از EJB و [EmployeDaoRemote],
  • سطور ۱۲–۱۳: دو نام JNDI از EJB و [IndemniteDaoLocal],
  • سطور 14–15: دو نام JNDI از EJB و [EmployeDaoRemote].

ما همان آزمایش را تکرار می‌کنیم، این بار با استفاده از رابط دور EJB.

در [1]، کلاس [JUnitInitDBLocal] به [JUnitInitDBRemote] کپی/پیست شده است. در این کلاس، ما رابط‌های محلی را با رابط‌های از راه دور جایگزین می‌کنیم:

public class JUnitInitDBRemote {

  static private IEmployeDaoRemote employeDao = null;
  static private ICotisationDaoRemote cotisationDao = null;
  static private IIndemniteDaoRemote indemniteDao = null;

  @BeforeClass
  public static void init() throws Exception {
     // کانتینر Open EJB جاسازی‌شده پیکربندی شده است
    Properties properties = new Properties();
    properties.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.apache.openejb.client.LocalInitialContextFactory");
      // ابتدایی‌سازی زمینه JNDI با ویژگی‌های قبلی
    InitialContext initialContext = new InitialContext(properties);
     //نمونه‌سازی لایه‌های DAO از راه دور
    employeDao = (IEmployeDaoRemote) initialContext.lookup("EmployeDaoRemote");
    cotisationDao = (ICotisationDaoRemote) initialContext.lookup("CotisationDaoRemote");
    indemniteDao = (IIndemniteDaoRemote) initialContext.lookup("IndemniteDaoRemote");
}

پس از انجام این کار، می‌توان کلاس تست جدید را اجرا کرد. پیش از آن، با استفاده از اتصال NetBeans با شناسه [dbpam_eclipselink]، جداول را از پایگاه داده [dbpam_eclipselink] حذف کنید.

 

با استفاده از اتصال NetBeans [dbpam_eclipselink]، بررسی کنید که پایگاه داده پر شده است.

6.2.4. پورت کردن لایه [metier]

ما لایه [metier] را با کپی کردن بسته‌ها از پروژه [mv-pam-spring-hibernate] به پروژه [mv-pam-openejb-eclipselink] منتقل خواهیم کرد.

خطاهای گزارش‌شده در بالا ([1]) به این دلیل است که لایه کپی‌شده [metier] از Spring استفاده می‌کند و کتابخانه‌های Spring دیگر بخشی از پروژه نیستند.

6.2.4.1. EJB [Metier]

ما همان رویه‌ای را که برای EJB و [CotisationDao] توضیح داده شده است، دنبال می‌کنیم. ابتدا، در [2]، ما رابط‌های محلی و راه دوری را برای EJB و [Metier] آینده ایجاد می‌کنیم. هر دو از رابط اولیه [IMetier] مشتق شده‌اند.

1
2
3
4
5
6
7
8
package metier;

import javax.ejb.Local;

@Local
public interface IMetierLocal extends IMetier{

}
1
2
3
4
5
6
7
8
package metier;

import javax.ejb.Remote;

@Remote
public interface IMetierRemote extends IMetier{

}

پس از انجام این کار، در [3] کلاس [Metier] را اصلاح می‌کنیم تا به EJB تبدیل شود:

@Stateless
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public class Metier implements IMetierLocal, IMetierRemote {

   //ارجاعات به لایه محلی [DAO]
  @EJB
  private ICotisationDaoLocal cotisationDao = null;
  @EJB
  private IEmployeDaoLocal employeDao = null;
  @EJB
  private IIndemniteDaoLocal indemniteDao = null;

   // بازیابی فیش حقوقی
  public FeuilleSalaire calculerFeuilleSalaire(String SS,
          double nbHeuresTravaillées, int nbJoursTravaillés) {
     //بازیابی اطلاعات مرتبط با کارمند
...
  • خط ۱: anotation @Stateless کلاس را به EJB تبدیل می‌کند
  • خط ۲: هر متد در کلاس در داخل یک تراکنش اجرا خواهد شد
  • خط ۳: کلاس EJB [Metier] هر دو رابط محلی و راه دوری را که به تازگی تعریف کرده‌ایم پیاده‌سازی می‌کند
  • خط ۷: EJB از [Metier] از طریق رابط محلی آن استفاده خواهد کرد. این بدان معناست که لایه‌های [metier] و [DAO] باید در همان JVM اجرا شوند.
  • خط ۶: حاشیه‌نویسی @EJB تضمین می‌کند که خود کانتینر EJB ارجاع را به رابط محلی EJB [CotisationDao] تزریق کند. رویکرد دیگری که با آن مواجه شده‌ایم، استفاده از یک زمینه JNDI است.
  • خطوط ۸–۱۱: همان مکانیزم برای دو نمونه دیگر EJB در لایه [DAO] استفاده می‌شود.

6.2.4.2. آزمایش لایه [metier]

لایه [metier] ما که توسط EJB پیاده‌سازی شده است، اکنون قابل آزمایش است. ما با کپی کردن بسته [metier] از [Test Packages] در پروژه [mv-pam-spring-hibernate] به پروژه در حال توسعه [1] شروع می‌کنیم:

  • به [1]، نتیجهٔ کپی
  • به [2]؛ تست اول حذف شد
  • در [3]، تست باقی‌مانده به [JUnitMetierLocal] تغییر نام داده می‌شود

کلاس [JUnitMetierLocal] به صورت زیر در می‌آید:

public class JUnitMetierLocal {

//لایه کسب‌وکار محلی
  static private IMetierLocal metier;

  @BeforeClass
  public static void init() throws NamingException {
     //پیکربندی کانتینر Open EJB جاسازی‌شده
    Properties properties = new Properties();
    properties.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.apache.openejb.client.LocalInitialContextFactory");
     // ابتدایی‌سازی زمینه JNDI با ویژگی‌های قبلی
    InitialContext initialContext = new InitialContext(properties);

     // نمونه سازی لایه‌های محلی DAO
    IEmployeDaoLocal employeDao = (IEmployeDaoLocal) initialContext.lookup("EmployeDaoLocal");
    ICotisationDaoLocal cotisationDao = (ICotisationDaoLocal) initialContext.lookup("CotisationDaoLocal");
    IIndemniteDaoLocal indemniteDao = (IIndemniteDaoLocal) initialContext.lookup("IndemniteDaoLocal");
     // مثال‌سازی لایه کسب‌وکار محلی
    metier = (IMetierLocal) initialContext.lookup("MetierLocal");

     // پایگاه داده تخلیه می‌شود
...
}
  • خط ۴: مرجعی به رابط محلی EJB [Metier]
  • خطوط ۸–۱۲: پیکربندی کانتینر OpenEJB، مشابه پیکربندی انجام‌شده در تست لایه [DAO]
  • خطوط ۱۵–۱۹: زمینه JNDI از خط ۱۲ پرس‌وجو می‌شود، برای ارجاع به سه نمونه EJB در لایه [DAO] و به نمونه EJB در لایه [metier]. EJB در لایه [DAO] برای راه‌اندازی پایگاه داده استفاده خواهد شد، در حالی که EJB در لایه [metier] برای انجام آزمون‌های محاسبه حقوق و دستمزد استفاده خواهد شد.

اجرای تست [JUnitMetierLocal] نتیجه زیر را تولید می‌کند، [1]:

در [2]، ما [JUnitMetierLocal] را به عنوان [JUnitMetierRemote] برای آزمایش رابط دور EJB و [Metier] کپی می‌کنیم. کد [JUnitMetierRemote] برای استفاده از این رابط دور تغییر یافته است. سایر موارد بدون تغییر باقی مانده‌اند.

public class JUnitMetierRemote {

   //لایه کسب‌وکار راه دور
  static private IMetierRemote metier;

  @BeforeClass
  public static void init() throws NamingException {
     //پیکربندی کانتینر Open EJB جاسازی‌شده
    Properties properties = new Properties();
    properties.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.apache.openejb.client.LocalInitialContextFactory");
     // ابتدایی‌سازی زمینه JNDI با ویژگی‌های قبلی
    InitialContext initialContext = new InitialContext(properties);

     // نمونه سازی لایه‌های راه دور DAO
    IEmployeDaoRemote employeDao = (IEmployeDaoRemote) initialContext.lookup("EmployeDaoRemote");
    ICotisationDaoRemote cotisationDao = (ICotisationDaoRemote) initialContext.lookup("CotisationDaoRemote");
    IIndemniteDaoRemote indemniteDao = (IIndemniteDaoRemote) initialContext.lookup("IndemniteDaoRemote");
     // مثال‌سازی لایه کسب‌وکار راه دور
    metier = (IMetierRemote) initialContext.lookup("MetierRemote");

     // پایگاه داده تخلیه می‌شود
    for(Employe employe:employeDao.findAll()){
      employeDao.destroy(employe);
    }
    for(Cotisation cotisation:cotisationDao.findAll()){
      cotisationDao.destroy(cotisation);
    }
    for(Indemnite indemnite : indemniteDao.findAll()){
      indemniteDao.destroy(indemnite);
    }
     //پایگاه داده پر شده است
    Indemnite indemnite1=new Indemnite(1,1.93,2,3,12);
    Indemnite indemnite2=new Indemnite(2,2.1,2.1,3.1,15);
    indemnite1=indemniteDao.create(indemnite1);
    indemnite2=indemniteDao.create(indemnite2);
    employeDao.create(new Employe("254104940426058","Jouveinal","Marie","5 rue des oiseaux","St Corentin","49203",indemnite2));
    employeDao.create(new Employe("260124402111742","Laverti","Justine","La brûlerie","St Marcel","49014",indemnite1));
    cotisationDao.create(new Cotisation(3.49,6.15,9.39,7.88));
  }
}
  • خطوط ۴ و ۱۹: از رابط دور EJB و [Metier] استفاده می‌شود.
  • خطوط ۱۵–۱۷: رابط‌های دور لایه [DAO] استفاده می‌شوند
  • خطوط ۳۴–۳۵: از آنجا که در رابط‌های از راه دور، اشیایی که بین کلاینت و سرور مبادله می‌شوند به صورت مقدار (value) ارسال می‌شوند، باید نتیجه بازگردانده شده توسط متد create(Indemnite i) دریافت شود. این کار در رابط‌های محلی ضروری نبود، زیرا در آنجا اشیاء به صورت مرجع (reference) ارسال می‌شوند.

پس از انجام این کار، پروژه قابل ساختن است و تست [JUnitMetierRemote] قابل اجرا خواهد بود:

  

6.2.5. پورت کردن لایه [console]

ما لایه [console] را با کپی کردن پکیج‌ها از پروژه [mv-pam-spring-hibernate] به پروژه [mv-pam-openejb-eclipselink] منتقل خواهیم کرد.

خطاهای گزارش‌شده در بالا برای [1] به این دلیل است که لایه کپی‌شده [metier] از Spring استفاده می‌کند و کتابخانه‌های Spring دیگر بخشی از پروژه نیستند. در [2]، کلاس [Main] به [MainLocal] تغییر نام داده شده است. این کلاس از رابط محلی EJB [Metier] استفاده خواهد کرد.

کد کلاس [MainLocal] به شرح زیر تغییر می‌کند:

  public static void main(String[] args) {
     // داده‌های محلی
    final String syntaxe = "pg num_securite_sociale nb_heures_travaillées nb_jours_travaillés";
...
     // خطایی وجود دارد؟
    if (erreurs.size() != 0) {
      for (int i = 0; i < erreurs.size(); i++) {
        System.err.println(erreurs.get(i));
      }
      return;
    }
     // بسیار خب – می‌توانیم فیش حقوقی را از لایه [métier] درخواست کنیم
    IMetierLocal metier = null;
    FeuilleSalaire feuilleSalaire = null;
    try {
       //پیکربندی کانتینر Open EJB جاسازی‌شده
      Properties properties = new Properties();
      properties.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.apache.openejb.client.LocalInitialContextFactory");
       // ابتدایی‌سازی زمینه JNDI با ویژگی‌های قبلی
      InitialContext initialContext = new InitialContext(properties);
       // مصداق‌سازی لایه کسب‌وکار محلی
      metier = (IMetierLocal) initialContext.lookup("MetierLocal");
       // محاسبه فیش حقوقی
      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;
    }
     // نمایش تفصیلی
    String output = "Valeurs saisies :\n";
    output += ajouteInfo("N° de sécurité sociale de l'employé", args[0]);
....

تغییرات در خطوط ۱۳ تا ۲۵ انجام شده است. به این ترتیب، ارجاع به لایه [metier] تغییر داده شده است (خطوط ۱۷ تا ۲۲). ما کد جدید را توضیح نمی‌دهیم، زیرا قبلاً در مثال‌های قبلی پوشش داده شده است. پس از اعمال این تغییرات، پروژه دیگر هیچ خطایی ندارد (به [3] مراجعه کنید).

ما پروژه را برای اجرا با آرگومان‌های زیر پیکربندی می‌کنیم: [1]:

برای اینکه برنامه کنسولی به‌طور عادی اجرا شود، باید در پایگاه داده داده وجود داشته باشد. برای این کار، باید فایل [META-INF/persistence.xml] را اصلاح کنیم:


<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.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_1_0.xsd">
  <persistence-unit name="dbpam_eclipselinkPU" transaction-type="JTA">
    <!-- ارائه‌دهنده JPA است EclipseLink -->
    <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
    <!-- اشیاء Jpa -->
    <class>jpa.Cotisation</class>
    <class>jpa.Employe</class>
    <class>jpa.Indemnite</class>
    <!--ویژگی‌های ارائه‌دهنده EclipseLink -->
    <properties>
      <property name="eclipselink.logging.level" value="FINE"/>
      <!--
      <property name="eclipselink.ddl-generation" value="drop-and-create-tables"/>
      -->
    </properties>
  </persistence-unit>
</persistence>

خط ۱۴ که باعث می‌شد جداول پایگاه داده در هر اجرا دوباره ایجاد شوند، غیرفعال شده است. برای اینکه این تغییر اعمال شود، پروژه باید دوباره ساخته شود (پاکسازی و ساخت). پس از انجام این کار، برنامه قابل اجرا خواهد بود. اگر همه چیز به درستی پیش برود، خروجی کنسول مشابه موارد زیر خواهد بود:

.......
INFO - Created EJB(deployment-id=IndemniteDao, ejb-name=IndemniteDao, container=Default Stateless Container)
INFO - Deployed Application(path=classpath.ear)
[EL Info]: 2009-09-30 15:09:21.109--ServerSession(16658781)--EclipseLink, version: Eclipse Persistence Services - 1.1.2.v20090612-r4475
[EL Info]: 2009-09-30 15:09:21.937--ServerSession(16658781)--file:/C:/temp/09-09-28/pam-console-metier-dao-openejb-eclipselink-0910/build/classes/-jpa login successful
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: 4 seconds)

در اینجا، از رابط محلی لایه [metier] استفاده کرده‌ایم. اکنون در یک کلاس کنسول دوم از رابط راه دور آن استفاده می‌کنیم:

در [1]، کلاس [MainLocal] به صورت [MainRemote] کپی شده است. کد [MainRemote] اصلاح شده است تا از رابط دوربرد لایه [metier] استفاده کند:

// مشکلی نیست – می‌توانیم فیش حقوقی را از لایه [metier] درخواست کنیم
    IMetierRemote metier = null;
    FeuilleSalaire feuilleSalaire = null;
    try {
       // ما کانتینر OpenEJB جاسازی‌شده را پیکربندی می‌کنیم
...
       // ایجاد لایه کسب‌وکار راه دور
      metier = (IMetierRemote) initialContext.lookup("MetierRemote");
       //محاسبه فیش حقوقی
      feuilleSalaire = metier.calculerFeuilleSalaire(args[0], nbHeuresTravaillées, nbJoursTravaillés);
    } catch (PamException ex) {
...
    } catch (Exception ex) {
...
    }

تغییراتی در خطوط ۲ و ۸ اعمال شده است. پروژه [2] برای اجرای کلاس [MainRemote] پیکربندی شده است. اجرای آن نتایج مشابهی با قبل دارد.

6.3. Conclusion

ما نشان داده‌ایم چگونه یک معماری Spring/Hibernate را به معماری OpenEJB/EclipseLink مهاجرت کنیم.

معماری Spring/Hibernate

معماری OpenEJB / EclipseLink

فرآیند انتقال به‌خوبی پیش رفت زیرا برنامهٔ اصلی به‌صورت لایه‌لایه ساختار یافته بود. این نکتهٔ مهمی است که باید درک شود.