ايجاد، گسترش، و تست كامپوننت‌های EJB در چند ثانيه

 

OpenEJB توسعه Enterprise JavaBeans را تسهيل مي‌بخشد

 

نويسنده: Nader Aeinehchi

Java world

مترجم: امين ايزدپناه

 

خلاصه

فكر نمي‌كنيد زندگي چقدر راحت‌تر بود اگر شما مي‌توانستيد تنها ظرف چند ثانيه اقدام به ايجاد، گسترش، و تست كامپوننت‌هاي Enterprise JavaBeans (EJB) نماييد؟ اين مقاله چگونگي تسهيل نمودن چرخه توسعه EJB را به شما نشان مي‌دهد. نويسنده به شما نشان مي‌دهد كه چگونه مي‌توان با استفاده از XStream به سادگي اقدام به ايجاد داده ورودي براي تست‌هاي JUnit خود نمود. او مزاياي اجراي تست‌هاي خود با استفاده از يك ظرف EJB تعبيه شده تحت عنوان OpenEJB را بيان مي‌نمايد و توضيح مي‌دهد كه همين تست‌ها چگونه مي‌توانند بدون تغيير بر روي ظرف EJB محصول كامل شما اجرا گردند.

 

چرخه توسعه Enterprise JavaBeans – ايجاد، گسترش، و تست – وقت‌گير است، كه يكي از دلايل آن اجراي كامپوننت‌هاي EJB درون يك ظرف EJB مي‌باشد، كه به منابع مختلفي از قبيل منابع داده و يك فراهم‌كننده JNDI
(
Java Naming and Directory Interface) وابسته است. چرخه سنتي توسعه مي‌تواند بسته به پيچيدگي برنامه كاربردي شما، تعداد beans، و مشخصه‌هاي ظرف EJB در هر جايي از چند دقيقه تا 30 دقيقه متفاوت باشد. برخي ظروف EJB اصطلاحا گسترش داغ (hot-deployment) را فراهم مي‌نمايند به گونه‌اي كه هر تغييري در برنامه كاربردي به يك گسترش مجدد خودكار منجر مي‌گردد. بر طبق تجربه من، گسترش داغ هميشه به عنوان يك پاك كننده كش به درستي كار نمي‌كند – در اغلب موارد راه‌اندازي مجدد سرور و گسترش مجدد لازم است.

چالش ديگري كه در روند توسعه EJB با آن روبرو هستيم استفاده از ابزار اشكال‌زداي (debugger) ظرف EJB است. از آنجايي كه سيستم ظرف و تست در JVMهاي متفاوت اجرا مي‌گردند، شما بايد اقدام به راه‌اندازي يك اشكال‌زداي از را دور نماييد، كه خود اين كار نيز احتمالا پيچيده خواهد بود.

تست يونيت كامپوننت‌هاي EJB (با استفاده از JUnit) نيز ساده نيست. اولا، beans بايد درون ظرف گسترش يابد، كه خود اين كار وقت‌گير است. دوما، شما در مورد تست‌هاي JUnit دو گزينه در اختيار داريد كه هر دو پيچيده و وقت‌گير هستند:

1. تست‌ها درون ظرف اجرا شوند، كه اين امر مستلزم آن است كه تست‌ها درون ظرف گسترش داده شوند

2. تست‌ها در JVMهاي متفاوت اجرا گردند و به عنوان يك كلاينت در مقابل يك ظرف EJB راه دور عمل نمايند

براي مواجهه با چالش‌هاي فوق، انجمن J2EE با راهكارهاي خوب متعددي جهت كوتاه‌تر ساختن چرخه توسعه EJB پا به عرصه گذاشت. به هر حال، چالش‌هاي بسياري همچنان پا بر جا هستند، كه دليل عمده اين امر تكنولوژي‌هاي بيشماري است (EJB، صفحات Servlet/JavaServer، سرويس Java Message، و Java Database Connectivity) كه يك برنامه كاربردي J2EE به آنها وابسته مي‌باشد. به علاوه، راهكارهاي رقابتي و مكمل فراواني در دسترس مي‌باشند، و گزينش يكي از آنها كار دشواري است.

راهكارهاي متعددي وجود دارد، همان گونه كه در مورد توسعه EJB اينگونه است. يكي از راهكارها عبارت است از تست برنامه كاربردي هنگامي كه به يك ظرف EJB گسترش يافت. راهكار ديگر تست برنامه كاربردي بر روي يك ظرف EJB شبيه‌سازي شده است. در راهكار دوم، هنگامي كه كامپوننت‌ها يا منابع ديگر EJB (مثلا يك منبع داده) مورد نياز باشند، يك ظرف EJB خارجي فراخواني مي‌گردد. با اين همه، اين راهكارها پيچيدگي‌ها و نقائص خود را دارند.

در اين مقاله، من شما را با تركيبي از ابزارها آشنا مي‌سازم كه به ميزان قابل توجهي چرخه توسعه EJB را تسهيل و كوتاه مي‌نمايند. براي اين كه صرفه‌جويي در زمان را بهتر درك كنيد ايده‌اي را مطرح مي‌كنم: در صورتي كه از يك لپ‌تاپ مجهز به يك پردازنده Mobile Intel Pentium 4 2/2 گيگاهرتزي استفاده نماييم، از زماني كه يك كد تغيير داده مي‌شود تا زماني كه نتايج تست بر روي صفحه ظاهر مي‌گردد تنها سه ثانيه طول مي‌كشد.

اين مقاله بر مبناي تجربه من در يك پروژه توسعه‌ كه هنوز به اتمام نرسيده است نوشته شده است. در سرتاسر مقاله، من به ابزارهايي رجوع مي‌كنم كه در پروژه مورد استفاده قرار گرفته‌اند: XStream، OpenEJB، Eclipse، و Oracle OC4J.

 

نماي نزديك

به جاي اجراي تست خود درون يك ظرف (container)، ظرف را درون تست خود اجرا نماييد. در اين مقاله، ما از يك ظرف EJB قابل تعبيه سبك وزن درون تست‌هايمان استفاده مي‌كنيم، همان گونه كه در شكل 1 نشان داده شده است.

 

شكل 1

تصوير سمت چپ توسعه سنتي EJB را نشان مي‌دهد، كه در آن برنامه كاربردي و تست به يك ظرف EJB در حال اجرا گسترش داده شده‌اند. تصوير سمت راست نشان مي‌دهد كه برنامه كاربردي و ظرف EJB مي‌توانند درون تست آغاز و پايان يابند.

اغلب ظرف‌هاي EJB داراي يك footprint‌ هستند كه براي تعبيه شدن درون يك برنامه كاربردي بيش از حد بزرگ است. به هر حال، OpenEJB، براي تعبيه شدن طراحي گرديده و داراي يك footprint كوچك است. من OpenEJB را يك انتخاب كامل براي توسعه EJB يافتم.

به عنوان يك ظرف EJB تعبيه شده، OpenEJB تنها ظرف مدت 2 ثانيه كار خود را آغاز مي‌نمايد (بر روي يك لپ‌تاپ مجهز به يك پردازنده Mobile Intel Pentium 4 2/2 گيگاهرتزي). در هنگام اجرا، OpenEJB همانند ساير ظرف‌هاي EJB داراي مشخصه‌هاي كامل مي‌باشد.

اما اگر پروژه شما قبلا يك ظرف EJB خاص را برگزيده باشد وضعيت به چه شكل خواهد بود؟ آيا باز هم شما مي‌توانيد از OpenEJB بهره‌ ببريد؟ آيا تست‌هاي شما مي‌توانند هم به OpenEJB و هم به ظرف EJB منتخب شما انتقال يابند؟ در صورت مثبت بودن پاسخ، آيا شما ناچاريد تنظيمات بسياري را انجام دهيد؟ چه ميزان كار براي تعبيه OpenEJB مورد نياز است؟ به عنوان پاسخي به سوالات فوق، اجازه دهيد اعلام كنم كه راه‌اندازي OpenEJB تنها چند دقيقه زمان مي‌برد، و تست‌هاي شما مي‌توانند بدون تغيير هم بر روي OpenEJB و هم بر روي ظرف EJB منتخب شما اجرا گردند.

 

ابزارها

بياييد در ابتداي كار به بررسي ابزارهاي مورد نياز خود بپردازيم:

· ابزارهاي اجباري:

· براي كامپايل و اجراي تست‌ها

· OpenEJB (0.9.2): به عنوان ظرف تعبيه شده

· JUnit: براي نوشتن و خودكارسازي تست‌ها

 

· ابزارهاي اختياري:

· XStream: براي شبيه‌سازي داده‌هاي ورودي

· Eclipse: براي IDE

· يك سرور J2EE: سرور برنامه كاربردي منتخب شما براي اهداف توليد

در پروژه توسعه‌اي كه من در آن همكاري دارم، ما از Eclipse به عنوان IDE و از Oracle OC4J به عنوان ظرف EJB براي توليد استفاده مي‌كنيم. گذشته از اين، ما XStream را به منظور persist اشياء جاوا به فايل‌هاي XML به كار مي‌بريم. اين فايل‌هاي XML جهت شبيه‌سازي داده‌هاي ورودي براي تست‌هاي ما مورد استفاده قرار مي‌گيرند.

 

آغاز كار

در ادامه خلاصه‌اي از مراحلي را كه شما بايد براي تست كامپوننت‌هاي EJB خود پشت سر بگذاريد مشاهده مي‌نماييد:

1. به عنوان داده ورودي، تست‌هاي خود را توسط فايل‌هاي XML (شبيه‌سازي) تغذيه نماييد. اين امر شما را از تايپ مكرر ورودي‌هاي يكسان بي‌نياز مي‌سازد. در اينجا، من استفاده از XStream را توصيه مي‌كنم، يك كتابخانه كوچك اما قدرتمند كه XML را به/از جاوا نگاشت مي‌نمايد. به جاي XML، شما مي‌توانيد از اشياء جاوا كه به روش سخت كدنويسي شده‌اند (hard-coded) به عنوان ورودي استفاده نماييد، اما اين امر باعث آلوده شدن كد شما مي‌گردد – نيازي به توضيح اين مطلب نيست كه چه ميزان زمان براي ايجاد و نگهداري چنين كدي مورد نياز است. اين مرحله اختياري است.

 

شكل 2

 

2.   تست‌هاي خود را با JUnit يا يك framework تست يونيت ديگر بنويسيد. JUnit از يك انجمن توسعه‌دهنده بزرگ بهره مي‌گيرد، و اين ابزار داراي extension هاي بسياري است.

3.   OpenEJB را نصب نماييد (من بعدا به تفصيل گام‌هاي آن را بررسي خواهم نمود).

4.    فايل‌هاي jar برنامه كاربردي خود را تنها يكبار به OpenEJB گسترش دهيد. اين مرحله مادامي كه شما اقدام به حذف كامپوننت‌هاي EJB يا تغيير پيكربندي ننماييد نيازي به تكرار شدن ندارد. فرايند گسترش معمولا يكي دو ثانيه زمان مي‌برد.

5.    هنگامي كه قطعه‌اي از كد را تغيير مي‌دهيد، شما مي‌توانيد تست‌ها را با يك ظرف OpenEJB تعبيه شده اجرا كنيد. در اغلب موارد شما اين مرحله را تكرار خواهيد كرد.

6.    هنگامي كه كيفيت كد در مرحله 5 رضايت شما را جلب نمود، همين تست‌ها را بر روي سرور كاربردي منتخب خود براي توليد اجرا نماييد. هدف اين مرحله يافتن ناسازگاري‌هاي ممكن ميان OpenEJB و سرور كاربردي توليد شما است.

 

نمودار توالي زير چگونگي دريافت يك ارجاع به يك bean از سوي تست JUnit با تعبيه نمودن OpenEJB به عنوان ظرف EJB را به تصوير مي‌كشد.

 

شكل 3

 

 

 

ايجاد XML-mock ها (اختياري)

براي تسريع روند تست، ما مي‌توانيم از XStream به منظور ايجاد XML-mock ها به عنوان داده ورودي براي تست‌ها استفاده كنيم. براي استفاده از XStream، اطمينان حاصل نماييد كه فايل‌هاي jar  زير در مسير كلاس شما قرار دارند:

 

xpp3-1.1.3.4d_b4_min.jar

xstream-1.0.2.jar

 

دستورات زير كار ايجاد يك ارائه XML از هر كدام از اشياء جاوا را انجام مي‌دهند:

XStream xstream = new XStream();

String xml = xstream.toXML(someJavaObject));

 

بياييد ببينيم چگونه مي‌توانيم از كد فوق براي توليد داده ورودي براي تست‌هاي خود استفاده كنيم. فرض كنيم شما مي‌خواهيد كد زير را تست نماييد:

 

public class CarReservation(){

   public boolean reserveCar(Car car){

   // Do something with the car

     }

}

 

متد reserveCar () يك اتومبيل را به عنوان پارامتر دريافت مي‌كند. حالا، سوال اين است كه چگونه مي‌توان شيء Car را ايجاد نمود؟ البته، يك متد شيء اتومبيل را به صورت سخت كدنويسي خواهد كرد. متد ديگر اقدام به تهيه يك تصوير از يك شيء Car خواهد نمود و از اين تصوير مجددا به عنوان ورودي براي تست‌ها استفاده خواهد كرد. در راهكار ما، ما از متد دوم استفاده مي‌كنيم. فرض كنيد كه برنامه كاربردي شما حاوي كد زير است:

 

public void getCarInformationFromUser(Request request){

   Car car = request.getCar();

}

شما مي‌توانيد اين كد را به شكل زير تغيير دهيد تا تصويري از شيء Car تهيه نمايد:

 

public void getCarInformationFromUser(Request request){

   Car car = request.getCar();

   XStream xstream = new XStream();

   System.out.println("car=" + xstream.toXML(car));

}

 

اگر شما يك بار برنامه كاربردي خود را اجرا كنيد، مي‌توانيد  ارائه XML از شيء Car را از خروجي استاندارد كپي نماييد و آن را در يك فايل ذخيره كنيد.

اكنون شما مي‌توانيد متد reserveCar() را با XML-mockهايي كه ايجاد نموديد تست كنيد:

 

public void testReserveCar(){

 

   /*

   * Read the XML string from a file

   */

   String xml = SomeFileReader.getCarXML();

 

   XStream xstream = new XStream();

   Car mockCar = (Car) xstream.fromXML(xml);

 

   /*

   * Continue the test

   */

   CarReservation reservation = new CarReservation();

   boolean ok = reservation.reserveCar(mockCar);

   assertTrue(ok);

 

}

 

ممكن است شما تصميم بگيريد كه روش تبديل اشياء به XML را با ايجاد يك رابط و فراهم آوردن امكان پياده‌سازي آن از سوي كلاس‌هايتان (يا ترجيحا سوپركلاس‌ها) استاندارد نماييد:

public interface XMLStylate {

    /**

   * @return a string that contains the XML

   * representation of an object

   */

   String toXML();

}

 

كلاس CarReservation شما مي‌تواند با متد زير بسط داده شود:

 

public class CarReservation() implements XMLStylate{

   public boolean reserveCar(Car car){

   // Do something with the car

   }

 

   public String toXML(){

      XStream xstream = new XStream();

      return xstream.toXML(this);

   }

}

 

همان گونه كه مي‌بينيد، CarReservation يك روش استاندارد تبديل خود به XML را فراهم مي‌سازد.

براي تضمين كيفيت، يك قطعه از كد بايد تحت سناريوهاي مختلف تست گردد، به نحوي كه با هر سناريو يك مجموعه از داده‌هاي ورودي مورد استفاده قرار گيرد. XML در تركيب با XStream كار بعدي ما را به ميزان قابل توجهي تسهيل مي‌بخشد.

 

تست‌هاي JUnit خود را بنويسيد

تست‌هاي EJB شما شبيه كدي خواهد بود كه درون يك ظرف EJB اجرا مي‌گردد، بدون هيچ كد خاص framework. بياييد يك تست ساده براي برنامه‌ي كاربردي Car Reservation خود بنويسيم:

public class CarEjbTest extends TestCase {

 

   private Context context;

   private Car mockCar;

   public final static String CAR_HOME = "ejb/car/Car";

 

   protected void setUp() throws Exception {

      super.setUp();

      context = new InitialContext();

 

      /*

      *  You have to write some file reader that reads

      *  the XML representing the car object

      */

      String xml =  SomeFileReader.getCarXML();

      mockCar = (Car) (new XStrem()).fromXML(xml);

   }

 

   protected void tearDown() throws Exception {

      super.tearDown();

   }

   public void testCar() throws Exception {

      try {

         /*

         * Get the reference to home object

         */

         Object objref = context.lookup(CAR_HOME);

 

         CarReservationHome carReservationHome = (CarReservationHome)

         PortableRemoteObject.narrow(objref,

         CarReservationHome.class);

 

         CarReservation carReservation = carReservationHome.create();

         boolean ok = reservation.reserveCar(mockCar);

         assertTrue(ok);

      } catch (NamingException e) {

         e.printStackTrace();

      } catch (RemoteException e) {

         e.printStackTrace();

      }

   }

}

 

به شكلي جالب توجه، تست فوق مي‌تواند هم درون و هم بيرون يك ظرف EJB اجرا گردد، با جايگزين‌هاي زير:

1. تست، كامپوننت‌هاي EJB كه به عنوان بخشي از يك ظرف OpenEJB تعبيه شده (embedded) گسترش يافته و اجرا شده‌اند را فراخواني مي‌نمايد

2. تست، فراخواني‌هاي راه دور را در مورد كامپوننت‌هاي EJB كه در يك ظرف EJB گسترش يافته‌اند انجام مي‌دهد

3. تست، در يك ظرف EJB اجرا مي‌گردد و فراخواني‌هاي محلي را به كامپوننت‌هاي EJB صورت مي‌دهد

 

OpenEJB را نصب نماييد

OpenEJB مي‌تواند به عنوان يك سرور مستقل يا يك ظرف تعبيه شده نصب گردد:

1.   OpenEJB را دانلود نماييد.

2.  آن را درون يك فولدر از حالت فشرده خارج سازيد، مثلا در c:\java\openejb .

3.   يك پيكربندي جديد را از ابتدا ايجاد كنيد:

A. پيكربندي قبلي را حذف نماييد

B. يك فولدر خالي تحت عنوان conf ايجاد نماييد

C. در فولدر conf، فايل‌هاي زير را ايجاد نماييد:

· openejb.conf

· logging.conf

· myapp.cmp.global-database.xml

· myapp.cmp.local-database.xml

· myapp.cmp.or-mapping.xml

D. openejb.conf را ويرايش نماييد و اطمينان حاصل كنيد كه آن به درستي myapp.cmp.global-database.xml، myapp.cmp.local-database.xml، و myapp.cmp.or-mapping.xml را مورد ارجاع قرار مي‌دهد. توجه داشته بايد كه مسير از c:\java\openejb محاسبه گرديده است. براي مثال، مسير به myapp.cmp.global-database.xml در openejb.conf به صورت conf/myapp.cmp.global-database.xml خواهد بود. در تمامي فايل‌هاي فوق، شما بايد ظرف‌هاي متعدد persistence اداره شده توسط ظرف و persistence اداره شده توسط bean را تعريف نماييد. به علاوه، شما بايد اتصالگرهايي براي منابع داده مختلف تعريف كنيد.

4.  فايل jar خود را به پيكربندي OpenEJB بيافزاييد. در openejb.conf، يك ارجاع به فايل jar خود بيافزاييد. براي مثال: <Deployments jar="C:\dev\myapp\deploy\myapp-ejb.jar"/>

5.   تغييرات لازم را در هر يك از فايل‌هاي فوق ايجاد نماييد.

 

گسترش به OpenEJB

فايل jar برنامه كاربردي خود را فقط يك بار به OpenEJB گسترش دهيد. OpenEJB يك فايل تحت عنوان openejb-jar.xml را به فايل jar شما خواهد افزود. فايل openejb-jar.xml حاوي اطلاعات پيكربندي خاص OpenEJB مي‌باشد. اگر شما bean هاي EJB را مي‌افزاييد يا حذف مي‌كنيد، بايد مجددا فرايند گسترش را انجام دهيد.

در اينجا نحو مورد استفاده براي گسترش يك فايل jar را مشاهده مي‌نماييد:

 

openejb deploy -a -c C:\dev\myapp\deploy\myapp-ejb.jar

 

اكنون، نيازمند پشتيبان‌گيري از openejb-jar.xml هستيد. شما آن را درون فولدر META-INF فايل jar برنامه كاربردي خواهيد يافت. به خط فرمان برويد و دستور زير را براي استخراج فايل‌ها اجرا نماييد:

 

jar xvf C:\dev\myapp\deploy\myapp-ejb.jar

 

توجه: هر بار كه شما يك تركيب جديد را مي‌سازيد،  بايد اطمينان حاصل كنيد كه openejb-jar.xml