7. الإصدار 3: نقل التطبيق PAM إلى خادم تطبيقات Glassfish
نقترح وضع EJB من الطبقات [metier] و [DAO] من بنية OpenEJB / EclipseLink في حاوية خادم تطبيقات Glassfish.
التنفيذ الحالي مع OpenEJB / EclipseLink
![]() |
فيما سبق، تستخدم الطبقة [ui] الواجهة البعيدة للطبقة [metier].
لقد اختبرنا سياقين للتنفيذ: local و distant. في الوضع الأخير، كانت الطبقة [ui] عميلة للطبقة [metier]، وهي طبقة تم تنفيذها بواسطة EJB. للعمل في وضع العميل/الخادم، حيث يتم تشغيل العميل والخادم في JVM مختلفين، سنضع طبقات [metier, DAO, jpa] على خادم Java EE Glassfish. يأتي هذا الخادم مع Netbeans.
التنفيذ الذي سيتم إنشاؤه باستخدام خادم Glassfish
![]() |
- سيتم تشغيل الطبقة [ui] في بيئة Java SE (الإصدار القياسي)
- ستعمل الطبقات [metier, DAO, JPA] في بيئة Java EE (Enterprise Edition) على خادم Glassfish v3
- سيتواصل العميل مع الخادم عبر شبكة TCP/IP. تكون عمليات التبادل عبر الشبكة شفافة بالنسبة للمطور، إلا أنه يجب أن يدرك أن العميل والخادم يتبادلان كائنات متسلسلة للتواصل وليس مراجع الكائنات. يُسمى بروتوكول الشبكة المستخدم لهذه التبادلات RMI (استدعاء الطريقة عن بُعد)، وهو بروتوكول قابل للاستخدام فقط بين تطبيقين Java.
- سيكون التنفيذ JPA المستخدم على خادم Glassfish هو EclipseLink.
7.1. الجزء الخادم من تطبيق العميل/الخادم PAM
7.1.1. بنية التطبيق
ندرس هنا الجزء الخاص بالخادم الذي سيتم استضافته بواسطة الحاوية EJB3 لخادم Glassfish:
![]() |
يتعلق الأمر بنقل ما تم إنجازه واختباره بالفعل مع الحاوية OpenEJB إلى خادم Glassfish. وهنا تكمن أهمية OpenEJB وبشكل عام للحاويات المدمجة EJB: فهي تسمح لنا باختبار التطبيق في بيئة تشغيل مبسطة. وعندما يتم اختبار التطبيق، لا يبقى سوى نقله إلى خادم مستهدف، وهو في هذه الحالة خادم Glassfish.
7.1.1.1. مشروع Netbeans
لنبدأ بإنشاء مشروع Netbeans جديد:
![]() |
- في [1]، مشروع جديد
- في [2]، اختر الفئة Maven وفي [3] النوع EJB Module. يتعلق الأمر في الواقع بإنشاء مشروع سيتم استضافته وتنفيذه بواسطة حاوية EJB، وهي حاوية خادم Glassfish.
![]() |
- باستخدام الزر [4a]، اختر المجلد الأصلي لمجلد المشروع أو اكتب اسمه مباشرةً في [4b].
- في [5]، قم بتسمية المشروع
- في [6]، اختر خادم التطبيق الذي سيتم تشغيله عليه. الخادم المختار هنا هو أحد الخوادم الظاهرة في علامة التبويب [Runtime / Servers]، وهو هنا Glassfish v3.
- في [7]، اختر إصدار Java EE.
![]() |
- في [1]، المشروع الجديد. يختلف عن مشروع Java التقليدي في بعض النقاط:
- يتم إنشاء فرع [Other Sources] [2] تلقائيًا. وسيحتوي بشكل خاص على الملف [persistence.xml] الذي يقوم بتكوين الطبقة JPA،
- إذا قمنا ببناء المشروع (Build)، فسنرى ظهور [3] كاعتماد لـ [javaee-api-6.0]. وهي من النوع provided لأنها يتم توفيرها عند التنفيذ بواسطة حاوية EJB من Glassfish.
7.1.1.2. تكوين طبقة الاستمرارية
نعني بتكوين طبقة الاستمرارية كتابة الملف [persistence.xml] الذي يحدد:
- التنفيذ JPA المراد استخدامه
- تعريف مصدر البيانات الذي تستخدمه الطبقة JPA. وسيكون هذا المصدر هو JDBC الذي يديره خادم Glassfish.
![]() |
يمكننا المتابعة على النحو التالي. أولاً، في علامة التبويب [Runtime / Databases]، سننشئ [1] اتصالاً بقاعدة البيانات MySQL5 / dbpam_eclipselink:
![]() |
بعد ذلك، يمكننا الانتقال إلى إنشاء المورد JDBC الذي يستخدمه الوحدة النمطية EJB:
![]() |
- في [1]، قم بإنشاء ملف جديد – تأكد من تحديد المشروع EJB قبل القيام بهذه العملية
- في [2]، مشروع EJB
- في [3]، حدد الفئة [Glassfish]
- في [4]، نريد إنشاء مورد JDBC
![]() |
- في [5]، نحدد أن المورد JDBC سيستخدم مجموعة اتصالات جديدة. نذكر أن مجموعة الاتصالات هي مجموعة من الاتصالات المفتوحة تُستخدم لتسريع تبادل البيانات بين التطبيق وقاعدة البيانات.
- في [6]، قم بتسمية المورد JDBC الذي تم إنشاؤه باسم JNDI. يمكن أن يكون هذا الاسم أي اسم، ولكنه غالبًا ما يكون على شكل jdbc/nom. سيُستخدم هذا الاسم JNDI في الملف [persistence.xml] للإشارة إلى مصدر البيانات الذي يجب أن يستخدمه التنفيذ JPA.
- في [7]، قم بتسمية مجموعة الاتصالات التي سيتم إنشاؤها بأي اسم تريده
- في القائمة المنسدلة [8]، اختر الاتصال JDBC الذي تم إنشاؤه مسبقًا على أساس MySQL / dbpam_eclipselink.
- في [9]، ملخص لخصائص مجموعة الاتصالات - لا نغير أي شيء
![]() |
- إلى [10]، يمكن تحديد العديد من خصائص مجموعة الاتصالات - نترك القيم الافتراضية
- في [11]، عند انتهاء مساعد إنشاء مورد JDBC للوحدة النمطية EJB، تم إنشاء ملف [glassfish-resources.xml] في الفرع [Other Sources]. محتوى هذا الملف هو كما يلي:
<?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>
الملف [glassfish-resources.xml] هو ملف XML الذي يتضمن جميع البيانات التي جمعها المساعد. سيتم استخدامه بواسطة Netbeans، عند نشر الوحدة النمطية EJB على خادم Glassfish، لطلب إنشاء المورد JDBC الذي تحتاجه هذه الوحدة النمطية.
يمكننا الآن إنشاء الملف [persistence.xml] الذي سيقوم بتكوين الطبقة JPA للوحدة النمطية EJB:
![]() |
- في [1]، قم بإنشاء ملف جديد – تأكد من تحديد المشروع EJB قبل القيام بهذه العملية
- في [2]، مشروع EJB
- في [3]، نختار الفئة [Persistence]
- في [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"?>
<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: اسم وحدة الاستمرارية [mv-pam-ejb-metier-dao-eclipselinkPU] ونوع المعاملات (JTA لحاوية EJB)
- السطر 5: اسم JNDI لمصدر البيانات المستخدم من قبل طبقة الاستمرارية: jdbc/dbpam_eclipselink
- السطر 6: الكيانات JPA غير محددة. سيتم البحث عنها في مسار الفصل (Classpath) للوحدة النمطية EJB.
- لم يتم تحديد اسم التنفيذ JPA (Hibernate، EclipseLink، ...) المستخدم. في هذه الحالة، يستخدم Glassfish v3 EclipseLink بشكل افتراضي.
7.1.1.3. إدراج طبقات [jpa, DAO, metier]
الآن بعد تعريف الملف [persistence.xml]، يمكننا الانتقال إلى إدراج طبقات [metier, dao, jpa] لتطبيق المؤسسة [pam] في المشروع:
![]() |
هذه الطبقات الثلاث مطابقة لما كانت عليه مع OpenEJB. يمكننا إجراء عملية نسخ ولصق بسيطة بين المشروعين. وهذا ما سنقوم به الآن:
![]() |
- في [1]، نتيجة نسخ حزم [jpa, dao, metier, exception] من مشروع [mv-pam-openejb-eclipselink] إلى الوحدة النمطية EJB [mv-pam-ejb-metier-dao-jpa-eclipselink]
7.1.1.4. تكوين خادم Glassfish
يبقى علينا تكوين خادم Glassfish في نقطتين:
- يتم تنفيذ الطبقة JPA بواسطة EclipseLink. يجب أن نتأكد من أن خادم Glassfish يحتوي على مكتبات هذا التنفيذ JPA.
- مصدر البيانات هو قاعدة بيانات MySQL. يجب التأكد من أن خادم Glassfish يحتوي على برنامج التشغيل JDBC الخاص بـ SGBD.
يمكن اكتشاف عدم وجود هذه المكتبات عند نشر الوحدة النمطية EJB. فيما يلي إحدى الطرق المتاحة لإضافة المكتبات المفقودة إلى خادم Glassfish:
![]() |
- إلى [1]، اعرض خصائص خادم Glassfish
- في [2]، قم بتدوين مجلد مجالات الخادم. سنقوم بتدوينه لاحقًا <domains>
- في المجلد <domains>\domain1\lib، ضع المكتبات المفقودة. في المثال، تمت إضافة مكتبات Hibernate (lib / hibernate-tools) وبرنامج التشغيل JDBC من MySQL (lib / divers). بشكل افتراضي، يحتوي خادم Glassfish على مكتبات EclipseLink. لذلك، لن نضيف سوى برنامج التشغيل JDBC من MySQL.
![]() |
- إلى [1]، في علامة التبويب [Services]، نقوم بتشغيل خادم Glassfish v3
- في [2]، وهو نشط
7.1.1.5. نشر الوحدة النمطية EJB
نقوم الآن بنشر الوحدة النمطية EJB على خادم Glassfish:
![]() |
- في [1]، تم نشر الوحدة النمطية EJB
- في [2]، يتم تحديث شجرة خادم Glassfish
- إلى [3]، بعد النشر، تظهر الوحدة النمطية EJB في الفرع [Applications] لخادم Glassfish
- في [4]، تم إنشاء المورد JDBC [jdbc / dbpam_eclipselink] على خادم Glassfish. نذكر أننا قمنا بتعريفه في الفقرة 7.1.1.2.
أثناء النشر، يسجل خادم Glassfish معلومات مهمة في وحدة التحكم:
يُلاحظ في الأسطر
- 3 و 6 و 8 و 11 أسماء JNDI القابلة للنقل لـ EJB التي تم نشرها. أدخلت Java EE 6 مفهوم الاسم JNDI القابل للنقل. وهذا يشير إلى اسم JNDI معترف به من قبل جميع خوادم Java EE 6. مع Java EE 5، تكون الأسماء JNDI خاصة بالخادم المستخدم.
- 4، 7، 9، 12: أسماء JNDI لـ EJB التي تم نشرها في شكل خاص بـ Glassfish v3.
ستكون هذه الأسماء مفيدة لتطبيق وحدة التحكم الذي سنقوم بكتابته لاستخدام الوحدة النمطية EJB التي تم نشرها.
7.2. وحدة التحكم العميل - الإصدار 1
الآن بعد أن قمنا بنشر الجزء الخاص بالخادم من تطبيق العميل/الخادم الخاص بنا، ننتقل إلى دراسة الجزء الخاص بالعميل [1]:
![]() |
7.2.1. مشروع العميل
نقوم بإنشاء مشروع Maven جديد من النوع [Java Application] باسم [mv-pam-client-ejb-metier-dao-eclipselink]:
![]() |
- في [1]، مشروع العميل
في الملف [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-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>
- الأسطر 31-35: التبعية لمكتبة [gf-client] التي تتيح لعميل Classfish التواصل مع خادم بعيد،
- الأسطر 36-41: التبعية لمشروع Maven الخاص بالوحدة النمطية EJB. نريد هنا استرداد تعريفات الكيانات JPA وتلك الخاصة بالواجهات المختلفة بالإضافة إلى تعريف فئة الاستثناء [PamException]،
من مشروع [mv-pam-openejb-eclipselink]، ننسخ الفئة [MainRemote]:
![]() |
يجب أن تحصل الفئة [MainRemote] على مرجع إلى EJB من الطبقة [metier]. يتطور كود الفئة [MainRemote] على النحو التالي:
// حسناً - يمكننا طلب كشف الراتب
FeuilleSalaire feuilleSalaire = null;
IMetierRemote metier = null;
try {
// سياق JNDI لخادم Glassfish
InitialContext initialContext = new InitialContext();
// إنشاء مثيل لطبقة الأعمال
metier = (IMetierRemote) initialContext.lookup("java:global/istia.st_mv-pam-ejb-metier-dao-eclipselink_ejb_1.0-SNAPSHOT/Metier!metier.IMetierRemote");
// حساب كشف الراتب
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;
}
- السطر 6: تهيئة سياق JNDI لخادم Glassfish.
- السطر 8: يُطلب من هذا السياق JNDI مرجعًا على الواجهة البعيدة للطبقة [metier]. وفقًا لسجلات Glassfish، نعلم أن الواجهة البعيدة للطبقة [metier] لها اسمان محتملان:
السطر 1، الاسم JNDI القابل للاستخدام مع أي خادم تطبيقات JAVA EE 6. السطر 2، الاسم JNDI الخاص بـ Glassfish. في الكود، السطر 9، نستخدم الاسم القابل للنقل JNDI.
- لا يتغير باقي الكود
![]() |
في [1]، نقوم بتكوين المشروع ليقوم بتنفيذ الفئة [MainRemote] مع الحجج. إذا سارت الأمور على ما يرام، فإن تنفيذ المشروع يعطي النتيجة التالية:
إذا أدخلنا رقم ضمان اجتماعي غير صحيح في الخصائص، فسنحصل على النتيجة التالية:
7.3. وحدة التحكم العميل - الإصدار 2
في الإصدارات السابقة، تم تكوين بيئة JNDI لخادم Glassfish من ملف [jndi.properties] الموجود في مكان ما في أرشيفات المشروع. محتواه الافتراضي هو التالي:
# الوصول إلى Sun Application Server
java.naming.factory.initial=com.sun.enterprise.naming.SerialInitContextFactory
java.naming.factory.url.pkgs=com.sun.enterprise.naming
# مطلوب لإضافة javax.naming.spi.StateFactory لـ CosNaming الذي
# 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
تشير السطران 7 و 8 إلى جهاز خدمة JNDI ومنفذ الاستماع الخاص به. لا يسمح هذا الملف بالاستعلام عن خادم JNDI بخلاف localhost أو يعمل على منفذ بخلاف المنفذ 3700. إذا أردنا تغيير هذين المعلمتين، يمكننا إنشاء ملف [jndi.properties] خاص بنا أو استخدام تكوين Spring. سنعرض هذه التقنية الثانية.
نبدأ بإنشاء مشروع جديد من المشروع الأولي [pam-client-metier-dao-jpa-eclipselink].
![]() |
- في [1]، المشروع الجديد
- إلى [2]، ملف تكوين Spring [spring-config-client.xml]. محتواه كما يلي:
ملف تكوين Spring هو كما يلي:
<?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">
<!-- métier -->
<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>
نستخدم هنا علامة <jee> (السطر 14) التي ظهرت مع Spring 2.0. يتطلب استخدام هذه العلامة تعريف المخطط الذي تنتمي إليه، الأسطر 4 و10 و11.
- السطر 14: تسمح العلامة <jee:jndi-lookup> بالحصول على مرجع كائن من خدمة JNDI. هنا، نربط الكائن المسمى "metier" بالمورد JNDI المرتبط بـ EJB [Metier]. الاسم JNDI المستخدم هنا هو الاسم القابل للنقل (Java EE 6) لـ EJB.
- يصبح محتوى الملف [jndi.properties] هو محتوى العلامة <jee:environment> (السطر 15) التي تُستخدم لتعريف معلمات الاتصال بالخدمة JNDI.
تتطور الفئة الرئيسية [MainRemote] على النحو التالي:
السطران 7-8، يُطلب من Spring مرجع النوع [IMetierRemote] على الطبقة [metier]. يضفي هذا الحل مرونة على بنيتنا. في الواقع، إذا أصبح EJB من الطبقة [metier] محليًا، c.a.d. في نفس JVM مثل عميلنا [MainRemote]، فلن يتغير كود هذا الأخير. سيتغير فقط محتوى الملف [spring-config-client.xml]. عندئذٍ سنجد تكوينًا مشابهًا لهيكل Spring / JPA الذي تمت دراسته في الفقرة 5.11.
ندعو القارئ إلى تجربة هذه النسخة الجديدة.
7.4. عميل Swing
نقوم الآن بإنشاء عميل swing لتطبيقنا العميل / الخادم EJB.
![]() |
يجب أن يحتوي الملف [pom.xml] على التبعية اللازمة لتطبيقات Swing:
<dependency>
<groupId>org.swinglabs</groupId>
<artifactId>swing-layout</artifactId>
<version>1.0.3</version>
</dependency>
أعلاه، تمت كتابة الفئة [PamJFrame] في البداية لتعمل في بيئة Spring / JPA:
![]() |
الآن يجب أن تصبح هذه الفئة العميل البعيد لـ EJB المنشور على خادم Glassfish.
![]() |
تدريب عملي: باتباع مثال عميل وحدة التحكم [ui.console.MainRemote] في المشروع، قم بتعديل الطريقة التي تستخدمها الطريقة [doMyInit] (انظر الفقرة 5.12.4) من الفئة [PamJFrame] للحصول على مرجع على الطبقة [metier] التي أصبحت الآن بعيدة.

























