اندكی پيشگيری:

از گلوگاه‌های لايه داده J2EE جلوگيری كنيد

بهترين راه‌حل‌ها برای رفع گلوگاه‌های داده در محيطهای J2EE

 

 

نويسنده: Christopher Keene

Java World

مترجم: نازنين حقيقي

 

خلاصه

در اين مقاله، سعي داريم سه علت معمول گلوگاه‌هاي داده برنامه كاربردي را بيان نماييم و روشهايي جهت حذف آنها پيشنهاد كنيم. در اين بحث يك قاعده تجربي مطرح شده كه به شما كمك خواهد نمود پيش‌بيني كنيد، آيا برنامه كاربردي‌تان گلوگاه‌هايي ايجاد خواهد كرد يا خير.

سرورهاي برنامه كاربردي J2EE عملكرد قابل تغييري براي تعداد زيادي از برنامه‌هاي كاربردي ارائه مي‌نمايند. به هرصورت، نوع همه منظوره J2EE (كه هدفش پرداختن نيازهاي هر شركت است) نيز توانايي در ارائه مناسبترين راه‌حل جهت برنامه‌‌هاي كاربردي بحراني را محدود مي‌نمايد. بويژه، برنامه‌هاي كاربردي data-intensive يك گلوگاه داده عمده در تمام معماري‌هاي سرور J2EE ايجاد مي‌كند.

در تحقيق اخير در مورد 360 كاربر J2EE اين نتيجه بدست آمد كه منشا 57 درصد مسائل عملكرد و قابليت دسترسي برنامه كاربردي را مي‌توان در مشكلات دستيابي بي‌نتيجه داده جستجو كرد، و تنها 42 درصد برنامههاي كاربردي طي استقرار اوليه بگونه برنامه‌ريزي شده عمل مي‌كنند. تعجبي ندارد كه در اين تحقيق برنامه‌هاي كاربردي جاوا نميتوانند در 60 درصد موارد انتظارات كاربر را برآورده سازند. بدتر اينكه، در تحقيقي در سال 2004 كه توسط Forrester Research صورت گرفت مشخص شد كه بيش از دوسوم پاسخ دهندگان مشكلات عملكرد برنامه كاربردي را تنها هنگامي دريافتند كه يك كاربر براي كمك مراجعه مي‌نمود.

معمولا سرورهاي J2EE هر درخواستي براي داده‌هاي پايدار را به يك يا تعداد بيشتري دستور SQL تبديل مي‌نمايند. در مورد برنامه‌هاي كاربردي با مدلهاي پيچيده آبجكت و حجم سنگين درخواست، اين شيوه همانگونه كه در شكل1 نشان داده شده است، مشكلات اجتناب ناپذيري مي‌آفريند.

 

تصوير1: گلوگاه سرور برنامه كاربردي J2EE

 

اين مقاله سه علت معمول گلوگاه‌هاي داده‌هاي برنامه كاربردي را مشخص مي‌كند و يك شيوه مبتكرانه جهت حذف آنها پيشنهاد مي‌نمايد. همچنين معماري با استفاده از يك برنامه كاربردي real-world J2EE با يك لايه، سرويسهاي داده را نشان مي‌دهد كه به‌طور عمومي مستقر شده است و اكنون عملكرد بالايي ارائه مي‌كند.

 

علايم گلوگاه‌هاي احتمالي داده‌هاي برنامه كاربردي

دو خصوصيت برنامه كاربردي كه اغلب باعث ايجاد گلوگاه‌هاي دادهها مي‌شوند عبارتند از:

1. تعداد آبجكتهاي داده‌ها كه پيچيدگي نگاشت رابطهاي را سبب مي‌شود (تبديل جزء واحد به يك ذخيره پايدار)

2. حداكثر ميزان تراكنش، كه حجم درخواستهاي پايگاه اطلاعاتي را پديد مي‌آورد.

برنامه‌هاي كاربردي كه در معرض خطر مواجهه با مشكلات جدي هستند يكي از سه نياز زير را دارند:

· نيازهاي model-intensive: همچنانكه اندازه مدل آبجكت برنامه كاربردي افزايش مي‌يابد، مشكل تعيين يك نگاشت رابطه‌اي كارآمد زياد ميشود. نگاشت بسيار كار‌آمد جهت جلوگيري از گلوگاه‌هاي نگاشت ضروري است.

· نيازهاي Transaction-intensive: همينطور كه حجم درخواست برنامه كاربردي زياد مي‌شود، پايگاه دادهها براساس حجم خالص جستجوهاي پايگاه دادههاي يك گلوگاه خواهد شد. Caching هوشمندانه جهت جلوگيري از گلوگاه‌هاي جستجو لازم است.

· نيازهاي Data-intensive: حذف گلوگاه‌هاي داده‌ها در برنامههاي كاربردي هم با مدلهاي آبجكت پيچيده و هم حجم بالاي درخواست نياز به روشي سيستميك‌تر دارد.

يك لايه سرويسهاي داده، نرم‌افزار زيربناي دسترسي داده است كهCaching, نگاشت را به‌طور آشكار درون يك سرور J2EE ادغام مي‌كند تا گلوگاه‌هاي داده‌ها را جهت برنامه‌هاي كاربردي data-intensive حذف نمايد.

 

قاعده تجربي50/50

در حاليكه برنامه‌هاي كاربردي شركت پيچيده هستند و ممكن است بنا به دلايل گوناگون عملكرد ضعيفي داشته باشند، يك قاعده تجربي مناسب جهت پيش‌بيني گلوگاه‌هاي داده‌ها قانون 50/50 است.

برنامه‌هاي كاربردي J2EE كه بيش از 50 كلاس داده و يا بيش از 50 تراكنش در ثانيه طي ساعات پر ازدحام دارند، به احتمال بسيار زياد با گلوگاه‌هاي مهم داده مواجه مي‌شوند.

تصوير2 نشان مي‌دهد چگونه برنامه كاربردي‌تان را با استفاده از قانون 50/50 براي گلوگاه‌هاي داده‌ها ارزيابي نماييد.

 

تصوير2: قاعده تجربي 50/50

 

بسياري از برنامه‌هاي كاربردي بنيادي ريسك پايين گلوگاه‌هاي دادهها را دارند، و عملكرد ارائه شده توسط يك سرور J2EE استاندارد كافي است. برنامههاي كاربردي odel-intensiveM توسط كلاسهاي متعدد داده و يا روابط پيچيده بين آبجكتها مشخص مي‌شوند. در حال حاضر همچنانكه تقاضا افزايش مي‌يابد برنامه‌هاي كاربردي Transaction-intensive ميزان بالايي درخواست دارند يا انتظار مي‌رود داشته باشند. برنامه‌هاي كاربردي Data-intensive هم از تعداد بسيار زياد كلاسهاي داده و هم از ميزان تراكنش بالا آسيب مي‌بينند. باقي اين مقاله به بهترين راهكارها جهت رفع به گلوگاه‌هاي داده‌ها كه توسط اين نوع برنامههاي كاربردي درون يك محيط J2EE ايجاد شده است مي‌پردازد.

 

بهترين راهكارها براي برنامه‌هاي كاربردي model-intensive

در مورد برنامههاي كاربردي با تعداد اندكي كلاس داده، تنظيم دستي نگاشت مولفه‌هاي برنامه كاربردي به يك پايگاه داده، رابطه‌اي نسبتا آسان است. به هرصورت، در مورد مدلهاي آبجكت بزرگتر، كدگذاري دستي اجراهاي كلاس داده يا مشخص نمودن نگاشت از طريق توصيف‌گرها به‌طور فزاينده‌اي غيرقابل كنترل مي‌شود.

به‌طور كلي، هر برنامه كاربردي كه يك مدل آبجكت با بيست آبجكت داده يا بيشتر دارد، با مشكلات عملكرد مربوط به چالشهاي نگاشت رابطهاي (ORM) Object-relational درگير خواهد بود. نگاشتهاي ناموثر سبب مي‌شود برنامه‌ كاربردي در جستجوهاي زمانبر پايگاه داده‌ گير بيفتد.

 

روشهاي متعددي گلوگاه‌هاي ORM را برطرف مي‌سازند:

بارگذاري آرام را به كار گيريد: يك اشتباه معمول در مورد آبجكتهاي داده، تغيير دادن مسير همه آبجكتها يا رده‌بندهاي آبجكت از پايگاه داده در زماني است كه بخشي از آبجكت يا شاخهاي از رده‌بندي براي نيازهاي برنامه كاربردي كافي است. بارگذاري داده تنها زماني كاملا مورد نياز است كه يك راه‌ كاهش ترافيك كلي پايگاه داده است.

محدوديتهاي نگاشتJ2EE  را بشناسيد: سرورهاي J2EE ويژگي‌هايي جهت اتوماتيك كردن نگاشت بين اجزاء واحد و داده‌هاي رابطه‌اي ارائه مي‌‌دهند. اين نگاشتهاي اتوماتيك در زمان طراحي بطور قابل توجهي صرفه‌جويي مي‌نمايند. به هرصورت، گاهي اوقات نگاشتهاي رابطه‌اي ساده، ممكن است ضعيف عمل كنند و دسترسي به داده‌هاي موجود را مشكل سازند.

از پروسه‌هاي ذخيره شده استفاده كنيد: يك راه بهينه‌سازي كارآيي پايگاه داده براي داده‌هاي پيچيده بهره‌گيري از امكانات پروسه ذخيره شده پايگاه داده است. كلاسهاي داده سپس مستقيما به مجموعه‌اي از پروسه‌هاي ذخيره شده جهت خواندن و نوشتن داده‌ها تبديل مي‌شوند.

توابع API محلي پايگاه داده را به كار گيريد: توابع API،JDBC Connectivity) Database Java) عدم كارآيي مربوط به كتابخانههاي پايگاه داده محلي C از قبيل Oracle Call Interface (OCI) را شناسايي كرده‌اند. به علاوه، توابع API محلي پارامترهاي تنظيم و كار‌آمدي ارائه مي‌دهند كه از طريق لايه JDBC ژنريك ارائه شده توسط يك سرور J2EE غيرقابل دسترسي است.

 

بهترين راهكارها براي برنامه‌هاي كاربردي transaction-intensive

برنامه‌هاي كاربردي كه بايد در هر ثانيه درخواستهاي بسياري را پشتيباني نمايند در مورد گلوگاه‌هاي داده‌ها تقريبا به‌طور قطع اتفاق مي‌افتد. اين بويژه در مورد برنامه‌هاي كاربردي با استقرار توزيعي مبتني‌بر وب و نيازهاي پاسخگويي بسيار سريع صحت دارد.

معماري استاندارد J2EE كه يك يا تعداد بيشتري جستجوي SQL براي هر دسترسي آبجكت داده ايجاد مي‌كند، سرور پايگاه داده در يك برنامه كاربردي transaction-intensive را اشباع مي‌كند.  اين گلوگاه‌ها از راه افزايش ظرفيت پايگاه داده از طريق كپيسازي يا از راه كاهش درخواستهاي پايگاه داده با استفاده از Caching درون سرور J2EE رفع مي‌گردند.

كپيسازي پايگاه داده: كپيسازي پايگاه داده يك روش تقسيم سيل درخواست‌هاست، بطوريكه بتوان آنها را به‌طور موثرتري كنترل نمود. اين همچنين راهي مناسب براي تقويت عملكرد در يك ناحيه خاص را فراهم مي‌نمايد. به هرصورت، اين شيوه مي‌تواند روشي پرهزينه باشد كه نياز به سرورها و مجوزهاي پايگاه داده اضافي دارد.

Caching آبجكت درون يك سرور J2EE: Caching آبجكتهاي موجود در حافظه كه اغلب از آنها استفاده مي‌شود درون سرور J2EE بار پايگاه داده را كاهش مي‌دهد و زمان پاسخ را بهبود مي‌بخشد. برخي سرورهاي J2EE، Caching محدود براي اجزاي (Container-managed Persistence) CMP را در برمي‌گيرند. به هرصورت، اين ممكن است بطور مناسب مشكلات عملكرد را همانگونه كه بعدا در اين مقاله ذكر مي‌شود رفع ننمايد.

Caching افزوده آبجكت: بسياري از محصولات Caching مستقل، قابل دسترسي‌اند، براي مثال محصولاتي كه J Cache API را پشتيباني مي‌كنند. به هرصورت، اين Cacheها فاقد انسجام محكمي با چرخه زندگي آبجكت پايدار J2EE هستند و انسجام دادههاي Cache را كاملا در اختيار هر طراح مي‌گذارند تا به‌طور دستي آن را كنترل كند.

تصوير3: دو خطاي متداول انسجام معماري‌هايي را نشان مي‌دهد كه لايه داده‌ را از كارآمدي Caching جدا مي‌كنند. هر يك از اين خطاها ميتواند سبب شود Cache اطلاعات نادرستي را در اختيار برنامه كاربردي قرار دهد.

 

تصوير3: دو خطاي Cache

 

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

 

بهترين راهكارها جهت برنامه‌هاي كاربردي data-intensive

دشوارترين گلوگاه‌هاي داده‌ها در برنامه‌هاي كاربردي‌ اتفاق ميافتند كه هم مدلهاي آبجكت پيچيده و هم بارهاي تراكنش بالا دارند. قانون 50/50 كه در بالا توضيح داده شد مي‌تواند به معماران و طراحان كمك كند گلوگاه‌هاي داده‌ها را پيش‌بيني نمايند.

در مورد برنامههاي كاربردي data-intensive، نگاشت مناسب و نه Caching آبجكت، مي‌توانند به تنهايي نتايج قابل قبولي ارائه كنند. اين برنامههاي كاربردي نياز به يك راه‌حل منسجم‌تر لايه سرويسهاي داده دارند كه يك لايه نگاشت رابطه‌اي موثر را با يك Cache آبجكت هوشمند كه با چرخه زندگي آبجكت J2EE مرتبط است تركيب مي‌كند.

سرورهاي J2EE پرفروش، از قبيل Web Sphere, Web Logic چند ويژگي Caching ارائه ميدهند، اما اين قابليتها برحسب انواع آبجكتها مي‌توانند Cache شوند و در سطح انسجام فراهم شده در سراسر يك گروه Cache محدود مي‌شوند. جدول زير برخي محدوديتهاي Caching ارائه شده درون سرور معمول J2EE را توضيح مي‌دهد.

 

Cache function J2EE caching Intelligent caching Benefit
Object access Objects accessible only within transactions Objects accessible across transactions Easier to access cached objects
Object query By reference and primary key only By reference, primary key, attribute, relationship, and indexed queries Cache queries speed performance
Object model Only caches simple objects, no relationships Caches complete object model, including relationships No "impedance mismatch" with cache
Clustering Only sends invalidation sync messages Sends changed state in sync messages Clustered caches have complete state
Completeness No sync messages sent for inserts, relationship changes All cache changes are replicated across caches Clustered caches are consistent with database
Coherency No guaranteed messaging, can lose sync messages Guaranteed sync messaging Clustered caches have guaranteed integrity
Portability Proprietary-caching capabilities not portable across J2EE servers Supports WebLogic, WebSphere, Java, C++, .Net Caches can sync across multiple platforms

 

 

در مورد برنامه‌هاي كاربردي data-intensive، لايه دستيابي داده بايد طراحي شود تا نيازهاي عملكرد، تغييرپذيري و قابليت دسترسي روندهاي تجاري يا نتايج يك گلوگاه را پشتيباني نمايد. سرورهاي J2EE استاندارد نمي‌توانند اين نيازها را بر‌آورده سازند، بنابراين برنامه‌هاي كاربردي data-intensive يا نياز به كدگذاري دستي قابل ملاحظه جهت غلبه ‌بر محدوديتهاي حيطه J2EE و يا خريد محصول سرويسهاي داده شركتهاي ديگر دارند كهCaching,  نگاشت را به‌طور آشكار درون حيطه J2EE ادغام مي‌كند.

 

لايه‌هاي سرويسهاي داده براي برنامه‌هاي كاربردي data-intensive J2EE

طي دو سال گذشته، شركتهاي نرم‌افزاري متعددي لايه‌هاي سرويس‌هاي داده‌اي را معرفي كرده‌اند كه نگاشت رابطهاي را با قابليتهاي Caching هوشمند در هم مي‌آميزند و به‌طور آشكار با سرورهاي J2EE ادغام مي‌شوند. اين محصولات مي‌توانند هم به برنامه‌هاي كاربردي model-intensive و هم به برنامه‌هاي كاربردي transaction-intensive بپردازند، اما به ويژه براي رفع اساسي‌ترين گلوگاه‌هاي داده‌ها مناسب هستند كه برنامه‌هاي كاربردي data-intensive ايجاد ميكنند. نمونهها شامل EdgeX tend ازTop Link, Persistence از Oracle هستند.

محصولات سرويسهاي داده بهترين راهكار براي گلوگاه‌هاي طبيعي در معماري J2EE ارائه مي‌دهند. قابليتهاي يك لايه سرويسهاي داده موارد زير را در بردارند:

طراحي Model-driven: يك لايه سرويسهاي داده بايد از يك شيوه model-driven استفاده كنند تا آبجكت‌هاي داده‌اي را توليد كنند تا داده رابطه‌اي نگاشته شود. ابزارهاي لايه سرويسهاي داده بايد بوسيله ابزارهاي (Unified Modeling Language) UML با ديكشنريهاي داده تلفيق شوند.

ادغام يكدست J2EE: EJBهاي (Enterprise Java Beans) واحد توليد شده بايد براي مثال با استفاده از اينترفيس تراكنش JCA (Java Connector Architecture) به طور يكدست درون سرور J2EE ادغام شوند تا با چرخه زندگي آبجكت پايدار تركيب گردند.

Caching آشكار: تمام آبجكتهاي داده، Caching آشكار را در حاليكه قوانين Caching كه از طريق يك فايل پيكربندي مشخص شده‌اند اجرا مي‌كنند.

هماهنگسازي قطعي: تغييرات داده‌هاي Cache به‌طور قابل اطميناني به ساير Cacheها منتقل ميشوند و تغييرپذيري، failover و دسترسي بالا را با استفاده از JMS (Java Message Service)، Tibco يا MQ فعال مي‌سازند.

انتقال هشدار قانونمند: تغييرات داده‌هاي Cache ممكن است براساس قوانين انتقال به ساير سرورها، دستگاهها يا كاربران منتقل شود.

Cross-Platform: يك لايه سرويسهاي داده بايد سرورهاي J2EE متعددي را پشتيباني نمايند و قادر به اجرا در يك محيط جاواي صرف باشد. به‌طور ايده‌آل، لايه سرويسهاي داده بايد چندين پلات‌فرم برنامه‌ كاربردي شامل C ++، Portable Java Objects (PDOs)، آبجكتهاي J2EE و اسمبلي‌هاي C# را پشتيباني نمايند.

شكل4 نشان مي‌دهد چگونه يك لايه سرويسهاي داده درون يك معماري استاندارد J2EE قرار مي‌گيرد. توجه كنيد كه API براي لايه سرويسهاي داده مي‌تواند يا اجزاي واحد باشد يا آبجكتهاي قديمي ساده جاوا، بطوريكه وجود لايه سرويسهاي داده براي باقي برنامه كاربردي آشكار است.

 

تصوير4: لايه سرويسهاي داده J2EE

 

مثال برنامه كاربردي لايه سرويسهاي داده

براي نشان دادن مزاياي ارائه شده توسط يك لايه سرويسهاي داده، برنامه‌ كاربردي مديريتي يك مجموعه مالي با شرايط زير را در نظر بگيريد:

اجزاي پيچيده تجاري: مجموعه‌ها، موقعيتها و اطلاعات امنيتي كه اعداد اصلي‌اش به ترتيب صد، هزاروده‌هزار است.

به روزرساني‌هاي فوري موقعيت: هر تغيير در يك مجموعه مدل سبب تغييرات اتوماتيك در مجموعه‌هاي حاصل مي‌شود كه هر يك تابع مجموعه‌اي از قوانين تجاري است.

تعداد زياد كاربران: صدها مدير مجموعه از برنامه‌ كاربردي استفاده مي‌كنند تا بطور فعال مجموعه را مديريت نمايند و يا طرحهاي احتمالي را برنامه‌ريزي كنند.

جهت برآوردن اين نيازهاي تجاري، هر راه‌حل مبتني‌بر سرور جاوا بايد:

· تضمين‌هاي عملكرد تقريبا بي‌درنگ

· سطوح بالاي قطعي انسجام داده تجاري و عملكرد 7-24

· توانايي توازن با بارهاي تجاري افزايش يافته را ارائه نمايد.

در طراحي برنامه كاربردي مديريت مجموعه، بهترين لايه سرويسهاي داده، نخست يك روش model-driven جهت تعريف نمودن مدل آبجكت و مشخص كردن اينكه چگونه بايد جهت حداكثر كارآيي به پايگاه داده نگاشت شود را ارائه مي‌كند. استفاده از توابع API محلي پايگاه داده و ويژگي‌هاي عملكرد مختص پايگاه داده به بهينهسازي عملكرد نگاشت كمك مي‌نمايد.

براي مثال، ابزارهاي لايه سرويسهاي داده ميتوانند ورودي مدلسازي آبجكت را از يك ديكشنري داده، Rational Rose يا Eclipse بپذيرند، و سپس آن اطلاعات را جهت ايجاد اجزاي واحد استاندارد به كار گيرند كه به‌طور آشكار درون سرور J2EE اجرا مي‌شوند.

در استقرار برنامه كاربردي مديريتي مجموعه، بهترين لايه سرويسهاي داده كپي‌هاي محلي از داده‌هاي تجاري كه اغلب در دسترسند را Cache مي‌كند، كه حداكثر ميزان بازدهي را تضمين مي‌نمايد، و در هماهنگسازي تغييرات داده با ساير Cacheها همكاري مي‌كند تا اطمينان حاصل ‌شود كه كپي محلي داده در تمام نمونه‌هاي سرور هماهنگ ميگردد. لايه سرويسهاي داده بطور موثري يك مدل مجازي داده براي معماري‌هاي J2EE فراهم مي‌كند كه بطور عمده‌اي پيچيدگي تغييرپذيري يك برنامه كاربردي J2EE را كاهش ميدهد، ضمن اينكه بطور همزمان استحكام و انسجامش را بهبود مي‌بخشد.

در مورد برنامه‌هاي كاربردي توزيعي، يك لايه سرويسهاي داده مي‌تواند حتي ارزش بيشتري داشته باشد. هر نمونه سرور مستقل كه بطور جغرافيايي توزيع شده است از يك Cache محلي بعنوان لايه داده‌اش استفاده مي‌كند. Cache محلي دسترسي خواندن در اين سايتهاي دور را كنترل مي‌كند كه عملكرد را بطور عمده تسريع مي‌نمايد، در حاليكه هنوز پايگاه داده اوليه Write back را مي‌نويسد و مديريت داده متمركز را فراهم مي‌كند.

 

خلاصه و نتيجه‌گيري

تحت ويژگي J2EE، محفظه نگاشت بين مولفههاي جاوا و طرح اصلي پايگاه، داده را كنترل مي‌نمايد. اين شيوه يك معماري مولفه بي‌نقص و حساب شده ارائه مي‌دهد، اما اين محدوديت ذاتي را دارد كه هر عمل دسترسي داده منجر به يك يا تعداد بيشتري I/O ديسك فيزيكي ميشود. قانون تجربي 50/50 به معماران و طراحان روشي آسان جهت ارزيابي احتمال گلوگاه‌هاي دادهها را مي‌دهد.

حتي در مورد سرورهاي پرفروش برنامه‌ كاربردي از قبيل Web Logic وWeb Sphere، معماري استاندارد J2EE منجر به گلوگاه‌هاي داده‌ها مي‌شود كه تنها مي‌تواند با كدگذاري دسترسي قابل ملاحظه يا انتخاب يك لايه سرويس‌هاي داده شركتهاي ديگر برطرف شود. بهترين روش براي يك لايه سرويس‌هاي داده بايد بطور آشكار درون J2EE قرار گيرد و نگاشت و Caching را با تراكنش‌هاي J2EE ادغام نمايد.

 

 

 

منابع:

 

Copyright 2004 IDG News Service. All right reserved.
Copyright 2004 JavaWorld. All right reserved.
Copyright 2004, PC World Iran, All rights reserved.