الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

Java Distributed Objects

بدأه Wajdy Essam في 23 سبتمبر 2008 · 13 رد · 6,056 مشاهدة · في JavaSE
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

التطبيقات الشبكيه عاده تكون لنوعين من البرامج ، الأول يهتم بكيفيه تبادل الملفات والبيانات بين الClient والـ Server ويتم التعامل مع هذا النوع باستخدام بروتكولات FTP,SMTP,HTTP وغيرها من البروتكولات .. النوع الثاني من التطبيقات يهتم بكيفيه تشغيل برنامج أو داله في الجهاز الأخر مثل Telnet و Remote Procedure Call أختصارا RPC .

لغه جافا من اللغات الموجهه للكائنات OO ، وبالتالي سوف تطبق مفهوم RPC ولكن عن طريق الكائنات ، ومن هنا جائت تقنيات الـ Distributed Object والتي تسمح لي باستدعاء داله موجوده في كائن بعيد بدون الدخول في تفاصيل ارسال واستقبال البيانات ...

والطريقه الأولى و التقليديه في كتابه برامج الشكبات Client-Server معروفه لدى الأغلب ، حيث يقوم العميل (مثلا طالب) بتعبئه Form ويرسل البيانات الى الخادم ، والذي يقوم بمعالجتها ويرجع النتيجه أو تخزينها للأستفاده منها لاحقا ...

الشكل التالي بين Classical Client-Server Model :

RMI1.PNG

لكي يقوم المبرمج ببرمجه برنامج مثل هذا عليه أن يقوم بتحديد عده امور مهمه :

طريقه برمجه الـ Client-Server ، هل هم بالأعتماد على TCP-Socket أو UDP-Socket.

كيفيه ارسال البيانات الى الجهه الأخرى بصوره مفهمومه للجهه الأخرى Formatted Understandable Data

كيفيه عمل parse للرساله القادمه واستخلاص المعلومات منها ..

كل هذه الأمور على المبرمج أن يضعها في الأعتبار قبل و أثناء كتابه البرنامج ، والا فلن يعمل بالشكل المطلوب ..فمثلا لو قام الطالب Client بالضفط على زر Delete .. يجب أن تذهب هذه المعلومه الى الطرف الأخر بطريقه ما .. ويقوم الطرف الأخر بمعرفه هذه المعلومه والقيام بالوظيفه المطلوبه ...ويرجع النتيجه بطريقه مفهومه يفهمها الكلاينت .

الذي نريده هنا هو ميكانيكه تسمح لي عمل نفس الـ Client-Server بدون الحاجه الى تلك الخطوات أعلاه ، فقط أقوم بالضغط على الزر ولا أهتم بكيفيه الأرسال ، ولا كيفيه فهم المعلومه ولا توجد حاجه لparse ولا غيره ، أيضا قد يكون الخادم مكتوب باللغه أخرى مثلا سي++ .المشكله هنا أن الكائن الذي يقوم بهذا العمل غير موجود لدينا في الجهاز المحلي ..

الحل سوف يكون عن طريق عمل "واجهه تستطيع التعامل مع الخادم" Proxy في جهه العميل ، وعندما نضغط على ذالك الزر يقوم العميل باستدعاء الداله الموجوده في الـ Proxy .. وبعدها يقوم هذا الـ Proxy بالأتصال مع الخادم بطريقه يعرفها هو ... بنفس الأمر السيرفر قد لا يعلم عن الكلاينت شيء ، لذلك يكون فيه Proxy هو الأخر يتخاطب مع الكلاينت .. أي أن الـ Server-Proxy يقوم بالتخاطب مع الـ client-Proxy .

أنظر الصوره التاليه لتبين لك :

RMI2.PNG

هنا طريقه التخاطب بين تلك الـ Proxies قد يعتمد على التنقيه المستخدمه ، وأشهر ثلاث تقنيات هما :

RMI , CORBA , SOAP

ٌRMI :

أختصار لـ Remote Method Invocation وتعتمد على فكره أستدعاء الكلاينت (يعمل في JVM) لداله في كائن بعيد موجود في الخادم (يعمل على JMV مختلفه) ، ويشترط أن يكون الخادم والعميل مكتوبين بجافا ، وتستخدم هذه التقنيه RMI Protocol لتطبيق التخاطب الذي يتعامل مع TCP Socket .وباستخدام RMI سوف نعطي العميل الشعور بأنه لا وجود لكائنات بعيده .. بالضبط كأنه يتعامل مع كائن محلى ، وهذه أحد ميزات الـ RMI .

CORBA :

أختصار لـ Comman Object Request Borker Architecture ..

وظيفتها نفس وظفيه الـ RMI وهي أستدعاء داله في كائن بعيد ، لكن يمكن أن يكون الكائن البعيد مكتوب بأي لغه لا يهم .. وتستخدم CORBA بروتكول يسمى Internet Inter-ORB Protocol واختصارا IIOP .

SOAP :

أختصار لـ Simple Object Access Protocol

وهي تقريبا نفس وظيفه CORBA ، ولكن تعتمد في عملها على ملفات XML .

CORBA و SOAP تعمل على أي لغه لا يشترط جافا ، وهنا سوف نستخدم لغه خاصه لوصف الدوال الموجوده في السيرفر والتي يستطيع الكلاينت التعامل معها .. في CORBA نستخدم لغه تسمى Interface Defenition Langauge واختصارا IDL (يمكن أن نستخدم RMI مع بروتكول IIOP ولن نحتاج الى IDL) . أما في SOAP نستخدم Web Services Description Language أختصارا WSDL . طبعا هذه ليست لغات كجافا أو سي ، لكنها فقط لغه لوصف الـ Interface الذي يستطيع أستخدامه الكلاينت ...

طبعا أسهل هذه التقنيات هي RMI وهي موضوع حديثنا اليوم ، أما CORBA فسوف نتكلم عنها بمشيئه الله الموضوع القادم .. أما SOAP فهي تستخدم عند العمل مع Web-Service ولا توجد لدى معرفه حاليا بهالموضوع ...

RMI Conecpt

بنظره سريعه في أي كود RMI ، سوف نجد أنه يبعدنا تماما من التفاصيل ، فعندما نريد أن نستدعي داله موجوده في الخادم Server ، كل ما على أن أكتب سطر واحد وسأحصل على Reference لهذا الكائن . بعدها أستدعى الداله بشكل عادي كما لو أنها كانت Local . اذا كانت هناك قيمه مرسله Parameters و قيم راجعه return Value ، ستذهب بطريقه ما الى المكان الصحيح وبدون تدخل منك .. فعلا RMI تجعل الحياه أكثر سهوله :) .

أنظر للصوره التاليه والتي توضح هذه العمليه بشكل Abstract :

RMI3.PNG

لندخل الى العمق أكثر ولنرى كيف يقوم RMI بارسال القيم المرسله والراجعه .. أولا عندما يقوم الكلاينت باستدعاء داله موجوده في كائن بعيد سوف يقوم client باستدعاء الداله stub الموجوده في Proxy لديه .. ويمرر لها المتغيرات المطلوب ارسالها .. بعد ذلك يبدأ الـ Stub بعمل معالجه لتلك المتغيرات حتى يرسلها .. فلن يرسلها هكذا .. هذه العمليه تسمى Parameter Marshalling .

في عمليه الـ Marshalling سوف ينظر أولا الى نوع المتغير أو الكائن ، فاذا كان متغير عادي Primitve Data Type فيقوم بارساله كما هو بالطبع بعد تحويله الى بايت Byte بترميز يعرف بـ Big-Endian ..

أما اذا كان المتغير هو كائن reference فلا يمكن أن يرسل بهذه الطريقه ، لأنه يمكن أن يحتوي على كائنات أخرى فيه وتلك الكائنات تحتوي أيضا على كائنات وهكذا . لذلك في عمليه ارسال الكائن سوف نقوم بترميزه بـ serialization وبالتالي تذهب البايتات بطريقه معينه متسلسله الى الجهه الأخرى .وعندما تصل الى الجهه الثانيه سوف يقوم بتجميع هذه البايتات بعمليه تدعى De-Serialization . لذلك في حاله التعامل مع الكائنات (قيم مرسله أو راجعه) يجب أن يكون الكائن لديك يطبق impelements الـ serialization .

الأن بعدما أنتهي الـ stub من عمليه Encoded سوف يرسل هذه البايتات الى الجهه الثانيه ، ويقوم الReceiver Object باستقبال هذه البايتات وتجميعها unmarshals the parameters وبعدها يقوم يتحديد الكائن المراد ثم استدعاء الداله المراده وفي حال كان هناك قيمه راجعه منها يقوم هذا الـ receiver object بعمل Marshals لهذه القيمه الراجعه ويعيدها الى Stub الذي يقوم بعمليه unmarshals للقيمه الراجعه ويرجعها الى الكلاينت الذي استدعى الـ Stub .

الصوره التاليه توضح عمليه Parameter Marshalling :

RMI4.PNG

ربما تبدوا العمليه معقده وغير مفهومه ، لكن كما ذكرت لا حاجه للمبرمج بمعرفه تلك الخطوات ، لأن من ميزات RMI هي عمل أخفاء لكل هذه التفاصيل Good Tranceparency .

RMI Programming Steps :

هناك خطوات معينه للبرمجه في RMI سوف نذكرها هنا بشكل سريع وسوف تتضح بشكل أكبر عندما تنتاول الأمثله ان شاء الله .

أولا الخادم server :

عمل ملف Interface نضع فيه جميع الدوال التي نريد العميل أن يستدعيها .

نقوم بعمل ملف Impl للأنترفيس أعلاه ونقوم بتعريف الدوال التي صرحنا عنها .

نقوم بعمل ملف Server ونقوم بعمل Object من الملف Impl ونقوم بتسجيله في ملف يسمى registry باسم ما .

الأن الـ server جاهز لأتصال العميل واستدعاء الدوال من الكائن الموجود به .

ثانيا الكلاينت Client :

نقوم بالأتصال بملف registry ونطلب الأسم الذي كتبه السيرفر عند تسجيله للكائن .

سوف نحصل على ذلك الكائن ومنه نستدعي الداله المطلوبه .. (حتى يتضح لك سوف نحصل على Special Reference للكائن وعندما نستدعي الداله سوف يستدعي الداله الصحيحه وتنفذ هناك في السيرفر وترجع القيمه الراجعه لدينا) .

طبعا مع التعامل مع الـ Exception المناسبه .. وهي في حالتنا هذه RemoteException .. وهكذا تكون أصبحت خبير في RMI Programming :) .

Hello RMI World :

نأخذ الأن مثال على RMI Programming .. ونبدأ بالمثال الشهير Hello World . نريد أن نكتب كائن يعمل في السيرفر يحتوي على داله ترجع Hello ، ويقوم العميل باستدعاء هذه الداله عن بعد ..

نبدأ بالبرمجه في جهه الخادم :

سوف نحتاج أولا الى Interface يحتوي على جميع الدوال التي نريد العميل استدعائها وهنا فقط سوف تكون لدينا داله واحده وهي طباعه ..هذا الـ Interface يجب أن يقوم بعمل وراثه من الكلاس Remote حتى يقوم كلاس أخر (يمثل الكائن البعيد) بعمل تطبيق لهذا الأنترفيس .. اضافه الى أن الدوال التي سنكتبها في هذا الـ Interface يجب أن تقوم بعمل Throws لنوع معين من الException هو RemoteException لأنه قد تحدث مشاكل اثناء استدعاء الداله مثلا قطع الأتصال مع الخادم أو انهيار بالشبكه أو أيه مشكله أخرى .. لذلك جميع الدوال سوف تتعامل مع هذا الـ Exception .

جميع الكلاسات الخاصه ببرمجه RMI موجوده في الباكج java.rmi .. لنرى الأن ملف Interface ولنطلق عليه Hello :

 

 

[color= #666666; font-style: italic;]// Hello Remote Object Interface

 

[color= #000000; font-weight: bold;]import [color= #006699;]java.rmi.Remote [color= #339933;];

[color= #000000; font-weight: bold;]import [color= #006699;]java.rmi.RemoteException [color= #339933;];

 

[color= #000000; font-weight: bold;]public [color= #000000; font-weight: bold;]interface Hello [color= #000000; font-weight: bold;]extends [color= #003399;]Remote

[color= #009900;]{

 [color= #000000; font-weight: bold;]public [color= #003399;]String getHelloMessage[color= #009900;]([color= #009900;]) [color= #000000; font-weight: bold;]throws [color= #003399;]RemoteException [color= #339933;];

[color= #009900;]}

أنتهت الخطوه الأولى ، الأن نقوم بكتابه كلاس يطبق هذا الـInterface ، بالاضافه الى الوراثه من UnicastRemoteObject وهو الكلاس الخاص بأمور الـ Marshalling وارسال واستقبال البيانات ..وهو موجود في الباكج java.rmi.server

لنرى ملف HelloImpl (جرت العاده أن تكون نهايه هذا الملف بـ Impl) :

 

[color= #666666; font-style: italic;]// Hello Implementation

 

[color= #000000; font-weight: bold;]import [color= #006699;]java.rmi.RemoteException [color= #339933;];

[color= #000000; font-weight: bold;]import [color= #006699;]java.rmi.server.UnicastRemoteObject [color= #339933;];

 

[color= #000000; font-weight: bold;]public [color= #000000; font-weight: bold;]class HelloImpl [color= #000000; font-weight: bold;]extends [color= #003399;]UnicastRemoteObject [color= #000000; font-weight: bold;]implements Hello

[color= #009900;]{

 [color= #000000; font-weight: bold;]public HelloImpl [color= #009900;]([color= #009900;]) [color= #000000; font-weight: bold;]throws [color= #003399;]RemoteException 

[color= #009900;]{	[color= #009900;]}

 

[color= #000000; font-weight: bold;]public [color= #003399;]String getHelloMessage[color= #009900;]([color= #009900;]) [color= #000000; font-weight: bold;]throws [color= #003399;]RemoteException

 [color= #009900;]{

[color= #000000; font-weight: bold;]return [color= #0000ff;]"Hello Distributed Computing" [color= #339933;];

 [color= #009900;]}

 

[color= #009900;]}

الخطوه الثالثه وهي كتابه السيرفر Server ، وهنا سوف نقوم بعمل كائن من HelloImpl وندخل هذا الكائن في الـ registry ونعطيه أي اسم ما ، وبالطبع الكلاينت يجب أن يكون لديه هذا الأسم لكي يحصل على الـ reference فيما بعد ..

الداله :

[color= #003399;]Naming.[color= #006633;]rebind[color= #009900;](objectName,myObject[color= #009900;])[color= #339933;];

هي التي تقوم باضافه سجل في ملف registry يحتوي على اسم الكائن وعنوانه Reference . طبعا هذه الخدمه Registry سو ف نقوم بتشغيلها قبل أن يعمل السيرفر حتى تتم اضافه هذا السجل فيه .. يمكنك عن طريق هذه الخدمه أن تسجل كائن باسم ما ، ويقوم الكلاينت فيما بعد بالأتصال بهذه الخدمه بالأسم المعين ليحصل على الكائن .. طبعا الأضافه في ملف Registry تتم فقط من جهه السيرفر .الكلاينت يطلب من الـ Regsitry كائن عن طريق الأسم ..

ملف Server ، لاحظ أنه بعد عمل rebind سوف نقوم بطباعه جمله تدل أن السيرفر يعمل الأن وفي حال اتصال ...

 

[color= #666666; font-style: italic;]// RMI Server 

 

[color= #000000; font-weight: bold;]import [color= #006699;]java.rmi.RemoteException[color= #339933;];

[color= #000000; font-weight: bold;]import [color= #006699;]java.rmi.Naming[color= #339933;];

[color= #000000; font-weight: bold;]import [color= #006699;]java.net.MalformedURLException[color= #339933;];

 

[color= #000000; font-weight: bold;]public [color= #000000; font-weight: bold;]class Server 

[color= #009900;]{

    [color= #000000; font-weight: bold;]final [color= #000000; font-weight: bold;]static [color= #003399;]String HOST [color= #339933;]= [color= #0000ff;]"localhost"[color= #339933;];  

 

[color= #000000; font-weight: bold;]public [color= #000000; font-weight: bold;]static [color= #000066; font-weight: bold;]void main [color= #009900;]([color= #003399;]String args[color= #009900;][[color= #009900;]][color= #009900;])

[color= #009900;]{

[color= #000000; font-weight: bold;]try

[color= #009900;]{

HelloImpl myObject [color= #339933;]= [color= #000000; font-weight: bold;]new HelloImpl[color= #009900;]([color= #009900;])[color= #339933;];

[color= #003399;]String objectName [color= #339933;]= [color= #0000ff;]"rmi://" [color= #339933;]+ HOST [color= #339933;]+ [color= #0000ff;]"/MyHello"[color= #339933;];

[color= #003399;]Naming.[color= #006633;]rebind[color= #009900;](objectName,myObject[color= #009900;])[color= #339933;];

 

[color= #003399;]System.[color= #006633;]out.[color= #006633;]println[color= #009900;]([color= #0000ff;]"Binding Complete ....."[color= #009900;])[color= #339933;];

[color= #009900;]}

[color= #000000; font-weight: bold;]catch [color= #009900;]([color= #003399;]RemoteException e[color= #009900;])

[color= #009900;]{	e.[color= #006633;]printStackTrace[color= #009900;]([color= #009900;])[color= #339933;];	[color= #009900;]}

[color= #000000; font-weight: bold;]catch [color= #009900;]([color= #003399;]MalformedURLException e[color= #009900;])

[color= #009900;]{	e.[color= #006633;]printStackTrace[color= #009900;]([color= #009900;])[color= #339933;];	[color= #009900;]}

[color= #009900;]}

 

[color= #009900;]}

الأن الى هنا أنتهينا من البرمجه في جهه الخادم .. نأتي الأن لجهه العميل ..

كل ما علينا في جهه الكلاينت الحصول على reference للكائن ، ومن ثم استدعاء تلك الداله ، فقط .. ويتم الحصول على الكائن من خلال الداله lookUp الموجوده في الكلاس Naming ..

Hello myObject [color= #339933;]= [color= #009900;](Hello[color= #009900;]) [color= #003399;]Naming.[color= #006633;]lookup[color= #009900;](objectName[color= #009900;])[color= #339933;];

الأن بعد الحصول على reference للكائن سوف يكون نوعه Object ، لذلك نقوم بعمل cast الى النوع Hello .. ومن ثم نستدعي الداله بشكل عادي ..

 

[color= #666666; font-style: italic;]// RMI Client 

 

[color= #000000; font-weight: bold;]import [color= #006699;]java.rmi.Naming[color= #339933;];

[color= #000000; font-weight: bold;]import [color= #006699;]java.rmi.RemoteException[color= #339933;];

[color= #000000; font-weight: bold;]import [color= #006699;]java.rmi.NotBoundException[color= #339933;];

[color= #000000; font-weight: bold;]import [color= #006699;]java.net.MalformedURLException[color= #339933;];

 

[color= #000000; font-weight: bold;]public [color= #000000; font-weight: bold;]class Client

[color= #009900;]{

[color= #000000; font-weight: bold;]public [color= #000000; font-weight: bold;]final [color= #000000; font-weight: bold;]static [color= #003399;]String HOST [color= #339933;]= [color= #0000ff;]"localhost"[color= #339933;];

 

[color= #000000; font-weight: bold;]public [color= #000000; font-weight: bold;]static [color= #000066; font-weight: bold;]void main [color= #009900;]([color= #003399;]String args[color= #009900;][[color= #009900;]][color= #009900;])

[color= #009900;]{

[color= #000000; font-weight: bold;]try	

[color= #009900;]{

 [color= #003399;]String objectName [color= #339933;]= [color= #0000ff;]"rmi://" [color= #339933;]+ HOST [color= #339933;]+ [color= #0000ff;]"/MyHello"[color= #339933;];

 

 Hello myObject [color= #339933;]= [color= #009900;](Hello[color= #009900;]) [color= #003399;]Naming.[color= #006633;]lookup[color= #009900;](objectName[color= #009900;])[color= #339933;];

 

[color= #003399;]System.[color= #006633;]out.[color= #006633;]println[color= #009900;](myObject.[color= #006633;]getHelloMessage[color= #009900;]([color= #009900;])[color= #009900;])[color= #339933;];

[color= #009900;]}

[color= #000000; font-weight: bold;]catch [color= #009900;]([color= #003399;]RemoteException e[color= #009900;])

[color= #009900;]{	e.[color= #006633;]printStackTrace[color= #009900;]([color= #009900;])[color= #339933;];	[color= #009900;]}

[color= #000000; font-weight: bold;]catch [color= #009900;]([color= #003399;]NotBoundException e[color= #009900;])

[color= #009900;]{	e.[color= #006633;]printStackTrace[color= #009900;]([color= #009900;])[color= #339933;];	[color= #009900;]}

[color= #000000; font-weight: bold;]catch [color= #009900;]([color= #003399;]MalformedURLException e[color= #009900;])

[color= #009900;]{	e.[color= #006633;]printStackTrace[color= #009900;]([color= #009900;])[color= #339933;];	[color= #009900;]}

[color= #009900;]}

 

[color= #009900;]}

الأن في البدايه وللتبسيط وقبل أن نرى موضوع RMI Deployment ، ضع كل الملفات في مجلد واحد ، نريد أن نختبر البرنامج في Local Machine .

الأن قم أولا بترجمه جميع الملفات:

javac *.java

الأن ستخرج لك 4 ملفات .class

قم بترجمه ملف HelloImpl.class باستخدام المترجم الخاص بـ rmi :

rmic HelloImpl

(من غير كتابه الأمتداد) ..

الناتج هنا سوف يكون ملف Stub باسم HelloImpl_Stub.class . هذا الملف يجب أن يتواجد عند الكلاينت حتى يعمل البرنامج بشكل صحيح هو والملف Hello.class (لأننا في الكلاينت سوف نقوم بعمل Cast لهذا النوع) .. لذلك عليك أن نتسخ هذين الملفات الى الكلاينت في حال كان مجلد الكلاينت في مكان أخر أو في جهاز ثاني .. حاليا انسى الكلام واستمر على أساس أنهم جميعهم في نفس المسار ، وبعد قليل نتناول هذا الموضوع بشكل أوسع .

الأن (في جهه السيرفر ، لكننا حاليا نعمل وجميعهم في جهاز واحد ) قم بتشغيل ملف rmiregistry .. وذلك من خلال سطر الأوامر أكتب :

rmiregistry

ولن يكون هناك مؤشر أو جمله طباعه تدل على أنه يعمل .. فقط ستجد أن title Bar لسطر الأوامر أصبح يحتوي على rmiregistry وهو دليل على عمل هذه الخدمه ..

الأن شغل الخادم في نافذه اوامر جديده :

java server

وستجد أن الجمله Binding Complete .....تم طباعتها على الشاشه ..وهكذا يكون السيرفر يعمل ، والخدمه registry تعمل ايضا .. لن تستطيع ايقافها الا بالضفط على CTRL+C .

أخيرا شغل ملف الكلاينت :

java Client

وستجد جمله Hello Distributed Computing امامك في الشاشه (حيث تم استدعاء الداله getHelloMessage في السيرفر ، والناتج من هذه الداله هذا الString الذي تم طباعته في الكلاينت ) ..

تأكد من فهم ماذا حصل ،، تم استدعاء الداله getHelloMessage الموجوده في السيرفر ، وانتفذت هناك والقيمه الراجعه رجعت للعميل ، الذي قام بطباعتها على الشاشه .. هذه هي RMI بمنتهى البساطه :wink: .

Bank Service

نأخذ مثال أخر لتأكيد فهم الموضوع ، على مثال لعميل يريد أن يدخل بياناته لدى البنك ، ويقوم بالبحث عن معلوماته من خلال الأسم .. هذه الدوال سوف تكون في السيرفر ويقوم الكلاينت باستدعاء هذه الدوال من بعد ، ويمرر لها المعاملات وترجع له الناتج ..

ملف BankAccount وهو يمثل حساب العميل في البنك .. لاحظ أنه يطبق الأنترفيس Serializable.. وهكذا لجميع الكائنات في RMI يجب أن تكون مطبقه لهذا الأنترفيس والى فلن تستطيع ارسالها ..

 

[color= #666666; font-style: italic;]// Person Account

 

[color= #000000; font-weight: bold;]import [color= #006699;]java.io.Serializable[color= #339933;];

 

[color= #000000; font-weight: bold;]public [color= #000000; font-weight: bold;]class BankAccount [color= #000000; font-weight: bold;]implements [color= #003399;]Serializable  

[color= #009900;]{

[color= #000000; font-weight: bold;]private [color= #003399;]String firstName[color= #339933;];

[color= #000000; font-weight: bold;]private [color= #003399;]String lastName[color= #339933;];

[color= #000000; font-weight: bold;]private [color= #000066; font-weight: bold;]double balance [color= #339933;];

 

[color= #000000; font-weight: bold;]public BankAccount [color= #009900;]([color= #009900;])

[color= #009900;]{

setFirstName[color= #009900;]([color= #0000ff;]""[color= #009900;])[color= #339933;];

setLastName[color= #009900;]([color= #0000ff;]""[color= #009900;])[color= #339933;];

setBalance[color= #009900;]([color= #cc66cc;]0.0[color= #009900;])[color= #339933;];

[color= #009900;]}

 

[color= #000000; font-weight: bold;]public BankAccount [color= #009900;]([color= #003399;]String fn , [color= #003399;]String ln , [color= #000066; font-weight: bold;]double balance[color= #009900;])

[color= #009900;]{

setFirstName[color= #009900;](fn[color= #009900;])[color= #339933;];

setLastName[color= #009900;](ln[color= #009900;])[color= #339933;];

setBalance[color= #009900;](balance[color= #009900;])[color= #339933;];

[color= #009900;]}

 

[color= #000000; font-weight: bold;]public [color= #003399;]String getFirstName[color= #009900;]([color= #009900;])

[color= #009900;]{	[color= #000000; font-weight: bold;]return firstName[color= #339933;]; 	[color= #009900;]}

 

[color= #000000; font-weight: bold;]public [color= #003399;]String getLastName [color= #009900;]([color= #009900;])

[color= #009900;]{	[color= #000000; font-weight: bold;]return lastName[color= #339933;];	[color= #009900;]}

 

[color= #000000; font-weight: bold;]public [color= #000066; font-weight: bold;]double getBalance [color= #009900;]([color= #009900;])

[color= #009900;]{	[color= #000000; font-weight: bold;]return balance[color= #339933;];	[color= #009900;]}

 

[color= #000000; font-weight: bold;]public [color= #000066; font-weight: bold;]void setFirstName [color= #009900;]([color= #003399;]String firstName [color= #009900;])

[color= #009900;]{	

[color= #000000; font-weight: bold;]this.[color= #006633;]firstName [color= #339933;]= firstName[color= #339933;];

[color= #009900;]}

 

[color= #000000; font-weight: bold;]public [color= #000066; font-weight: bold;]void setLastName [color= #009900;]([color= #003399;]String lastName[color= #009900;])

[color= #009900;]{	[color= #000000; font-weight: bold;]this.[color= #006633;]lastName [color= #339933;]= lastName[color= #339933;];	[color= #009900;]}

 

[color= #000000; font-weight: bold;]public [color= #000066; font-weight: bold;]void setBalance [color= #009900;]([color= #000066; font-weight: bold;]double balance [color= #009900;])

[color= #009900;]{	[color= #000000; font-weight: bold;]this.[color= #006633;]balance [color= #339933;]= balance[color= #339933;];	[color= #009900;]}

 

[color= #009900;]}

ملف BankAccountInterface :

 

[color= #666666; font-style: italic;]// Bank Account Remote Object Interface

 

[color= #000000; font-weight: bold;]import [color= #006699;]java.rmi.Remote[color= #339933;];

[color= #000000; font-weight: bold;]import [color= #006699;]java.rmi.RemoteException[color= #339933;];

 

[color= #000000; font-weight: bold;]public [color= #000000; font-weight: bold;]interface BankAccountInterface [color= #000000; font-weight: bold;]extends [color= #003399;]Remote

[color= #009900;]{

[color= #000000; font-weight: bold;]public [color= #000066; font-weight: bold;]void insertEmployee[color= #009900;]([color= #003399;]String firstName,

[color= #003399;]String lastName,[color= #000066; font-weight: bold;]double balance[color= #009900;])

 [color= #000000; font-weight: bold;]throws [color= #003399;]RemoteException[color= #339933;];

[color= #000000; font-weight: bold;]public [color= #003399;]String getEmployee [color= #009900;]([color= #003399;]String firstName , 

[color= #003399;]String lastName [color= #009900;]) 

[color= #000000; font-weight: bold;]throws [color= #003399;]RemoteException[color= #339933;];

[color= #009900;]}

ملف BankAccountImpl :

 

[color= #666666; font-style: italic;]// Bank Account Implementation

 

[color= #000000; font-weight: bold;]import [color= #006699;]java.rmi.RemoteException[color= #339933;];

[color= #000000; font-weight: bold;]import [color= #006699;]java.rmi.server.UnicastRemoteObject[color= #339933;];

[color= #000000; font-weight: bold;]import [color= #006699;]java.util.Vector[color= #339933;];

 

[color= #000000; font-weight: bold;]public [color= #000000; font-weight: bold;]class BankAccountImpl 

[color= #000000; font-weight: bold;]extends [color= #003399;]UnicastRemoteObject 

[color= #000000; font-weight: bold;]implements BankAccountInterface

[color= #009900;]{

[color= #000000; font-weight: bold;]private Vector[color= #339933;]<BankAccount[color= #339933;]> dataBase [color= #339933;]= 

[color= #000000; font-weight: bold;]new Vector[color= #339933;]<BankAccount[color= #339933;]>[color= #009900;]([color= #009900;])[color= #339933;];

 

[color= #000000; font-weight: bold;]public BankAccountImpl[color= #009900;]([color= #009900;]) [color= #000000; font-weight: bold;]throws [color= #003399;]RemoteException 

[color= #009900;]{	[color= #009900;]}

 

[color= #000000; font-weight: bold;]public [color= #000066; font-weight: bold;]void insertEmployee [color= #009900;]([color= #003399;]String fn , [color= #003399;]String ln , 

[color= #000066; font-weight: bold;]double balance [color= #009900;])

 [color= #000000; font-weight: bold;]throws [color= #003399;]RemoteException

[color= #009900;]{

BankAccount tmp [color= #339933;]= [color= #000000; font-weight: bold;]new BankAccount[color= #009900;](fn,ln,balance[color= #009900;])[color= #339933;];

dataBase.[color= #006633;]add[color= #009900;](tmp[color= #009900;])[color= #339933;];

[color= #009900;]}

 

[color= #000000; font-weight: bold;]public [color= #003399;]String getEmployee [color= #009900;]([color= #003399;]String fn , [color= #003399;]String ln [color= #009900;]) 

[color= #000000; font-weight: bold;]throws [color= #003399;]RemoteException 

[color= #009900;]{

[color= #003399;]String result [color= #339933;]= [color= #0000ff;]""[color= #339933;];

 

[color= #000000; font-weight: bold;]for [color= #009900;]([color= #000066; font-weight: bold;]int i[color= #339933;]=[color= #cc66cc;]0[color= #339933;]; i[color= #339933;]<dataBase.[color= #006633;]size[color= #009900;]([color= #009900;])[color= #339933;]; i[color= #339933;]++[color= #009900;])

[color= #009900;]{

	BankAccount tmp [color= #339933;]= dataBase.[color= #006633;]elementAt[color= #009900;](i[color= #009900;])[color= #339933;];

 

	[color= #000000; font-weight: bold;]if [color= #009900;]( tmp.[color= #006633;]getFirstName[color= #009900;]([color= #009900;]).[color= #006633;]equals[color= #009900;](fn[color= #009900;]) [color= #339933;]&&

	 tmp.[color= #006633;]getLastName[color= #009900;]([color= #009900;]).[color= #006633;]equals[color= #009900;](ln[color= #009900;]) [color= #009900;])

	result [color= #339933;]= tmp.[color= #006633;]getFirstName[color= #009900;]([color= #009900;]) [color= #339933;]+ [color= #0000ff;]"  " [color= #339933;]+

	 tmp.[color= #006633;]getLastName [color= #009900;]([color= #009900;]) [color= #339933;]+ [color= #0000ff;]"  " [color= #339933;]+ 

	 tmp.[color= #006633;]getBalance  [color= #009900;]([color= #009900;])[color= #339933;];

[color= #009900;]}

[color= #000000; font-weight: bold;]return result[color= #339933;];

[color= #009900;]}

 

[color= #009900;]}

ملف BankServer ولن يختلف عن السيرفر في المثال السابق :

 

[color= #666666; font-style: italic;]// Bank Server

 

[color= #000000; font-weight: bold;]import [color= #006699;]java.rmi.RemoteException[color= #339933;];

[color= #000000; font-weight: bold;]import [color= #006699;]java.rmi.Naming[color= #339933;];

[color= #000000; font-weight: bold;]import [color= #006699;]java.net.MalformedURLException[color= #339933;];

 

[color= #000000; font-weight: bold;]public [color= #000000; font-weight: bold;]class BankServer 

[color= #009900;]{

[color= #000000; font-weight: bold;]private [color= #000000; font-weight: bold;]static [color= #000000; font-weight: bold;]final [color= #003399;]String HOST [color= #339933;]= [color= #0000ff;]"localhost"[color= #339933;];

 

[color= #000000; font-weight: bold;]public [color= #000000; font-weight: bold;]static [color= #000066; font-weight: bold;]void main [color= #009900;]([color= #003399;]String args[color= #009900;][[color= #009900;]][color= #009900;])

[color= #009900;]{

[color= #000000; font-weight: bold;]try

[color= #009900;]{

 [color= #003399;]String myObjectName [color= #339933;]= [color= #0000ff;]"rmi://" [color= #339933;]+ HOST [color= #339933;]+ [color= #0000ff;]"/MyAccount"[color= #339933;]; 

BankAccountImpl  myObject [color= #339933;]= [color= #000000; font-weight: bold;]new BankAccountImpl[color= #009900;]([color= #009900;])[color= #339933;];

[color= #003399;]Naming.[color= #006633;]rebind[color= #009900;](myObjectName,myObject[color= #009900;])[color= #339933;];

 

[color= #003399;]System.[color= #006633;]out.[color= #006633;]println[color= #009900;]([color= #0000ff;]"Binding Complete ...."[color= #009900;])[color= #339933;];	

[color= #009900;]}

[color= #000000; font-weight: bold;]catch [color= #009900;]([color= #003399;]RemoteException e[color= #009900;])

[color= #009900;]{	e.[color= #006633;]printStackTrace[color= #009900;]([color= #009900;])[color= #339933;];	[color= #009900;]}

[color= #000000; font-weight: bold;]catch [color= #009900;]([color= #003399;]MalformedURLException e[color= #009900;])

[color= #009900;]{	e.[color= #006633;]printStackTrace[color= #009900;]([color= #009900;])[color= #339933;];	[color= #009900;]}

[color= #009900;]}

 

[color= #009900;]}

ملف BankClient وسوف نقوم باستدعاء الدوال ..

 

[color= #666666; font-style: italic;]// Bank Client 

 

[color= #000000; font-weight: bold;]import [color= #006699;]java.rmi.RemoteException[color= #339933;];

[color= #000000; font-weight: bold;]import [color= #006699;]java.rmi.NotBoundException[color= #339933;];

[color= #000000; font-weight: bold;]import [color= #006699;]java.rmi.Naming[color= #339933;];

[color= #000000; font-weight: bold;]import [color= #006699;]java.net.MalformedURLException[color= #339933;];

 

[color= #000000; font-weight: bold;]public [color= #000000; font-weight: bold;]class BankClient 

[color= #009900;]{

[color= #000000; font-weight: bold;]private [color= #000000; font-weight: bold;]static [color= #000000; font-weight: bold;]final [color= #003399;]String HOST [color= #339933;]= [color= #0000ff;]"localhost"[color= #339933;];

 

[color= #000000; font-weight: bold;]public [color= #000000; font-weight: bold;]static [color= #000066; font-weight: bold;]void main [color= #009900;]([color= #003399;]String args[color= #009900;][[color= #009900;]][color= #009900;])

[color= #009900;]{

[color= #000000; font-weight: bold;]try

[color= #009900;]{

[color= #003399;]String myObjectName [color= #339933;]= [color= #0000ff;]"rmi://" [color= #339933;]+ HOST [color= #339933;]+ [color= #0000ff;]"/MyAccount"[color= #339933;];

 

BankAccountInterface myObject [color= #339933;]= 

[color= #009900;](BankAccountInterface[color= #009900;]) [color= #003399;]Naming.[color= #006633;]lookup[color= #009900;](myObjectName[color= #009900;])[color= #339933;];

 

myObject.[color= #006633;]insertEmployee[color= #009900;]([color= #0000ff;]"Wajdy",[color= #0000ff;]"Essam",[color= #cc66cc;]30444[color= #009900;])[color= #339933;];

myObject.[color= #006633;]insertEmployee[color= #009900;]([color= #0000ff;]"Ahmed",[color= #0000ff;]"Ali",[color= #cc66cc;]2324[color= #009900;])[color= #339933;];

myObject.[color= #006633;]insertEmployee[color= #009900;]([color= #0000ff;]"Sami",[color= #0000ff;]"Essam",[color= #cc66cc;]3131[color= #009900;])[color= #339933;];

 

[color= #003399;]System.[color= #006633;]out.[color= #006633;]println [color= #009900;]( myObject.[color= #006633;]getEmployee[color= #009900;]([color= #0000ff;]"Wajdy",[color= #0000ff;]"Essam"[color= #009900;]) [color= #009900;])[color= #339933;];

[color= #009900;]}

[color= #000000; font-weight: bold;]catch [color= #009900;]([color= #003399;]RemoteException e[color= #009900;])

[color= #009900;]{	e.[color= #006633;]printStackTrace[color= #009900;]([color= #009900;])[color= #339933;];	[color= #009900;]}

[color= #000000; font-weight: bold;]catch [color= #009900;]([color= #003399;]NotBoundException e[color= #009900;])

[color= #009900;]{	e.[color= #006633;]printStackTrace[color= #009900;]([color= #009900;])[color= #339933;];	[color= #009900;]}

[color= #000000; font-weight: bold;]catch [color= #009900;]([color= #003399;]MalformedURLException e[color= #009900;])

[color= #009900;]{	e.[color= #006633;]printStackTrace[color= #009900;]([color= #009900;])[color= #339933;];	[color= #009900;]}

[color= #009900;]}

 

[color= #009900;]}

لتشغيل الملفات ، أكتب هذه الأوامر في سطر الأوامر ، وقد تحتاج الى 3 نوافذ أحدها لـ registry والأخرى للخادم والثالثه للعميل :

javac *.java

rmic BankAccountImpl

registry

java BankServer

java BankClient

واستمتع بالنتائج ..

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

لكن ما هي ملفات السيرفر -في المثال السابق- :

BankAccount.java

BankAccountImpl.java

BankAccountInterface.java

BankServer.java

وبعد الترجمه سوف يخرج لنا :

BankAccount.class

BankAccountInterface.class

BankAccountImpl.class

BankServer.class

BankAccountImpl_Stub.class

أما ملفات الكلاينت :

BankClient.java

ولن نستطيع ترجمه الكلاينت (الذي يعمل في جهاز منفصل ) والسبب أنه يحتوي على عمليه كاست من Object الى الكائن BankAccountInterface وهو لا يملكه ، أيضا كما ذكرنا أن الStub هو الذي يتعامل حقيقه مع السيرفر ونحن هنا لا نملك ملف BankAccountImpl_Stub.class .. لذلك يجب أن يتوفر للكلاينت هذين الملفين حتى يعمل ...

السؤال الأن كيف يمكن أن نتقل ملفات class هذه الى الجهاز الثاني ؟

بالطبع يمكن أن تقوم بارسالها بشكل عادي ، أي تقوم بعمل نسخ ولصق لهذه الملفات في الكلاينت ، وهكذا كل شيء سيعمل بشكل سليم 100%،

لكن في حال كان الجهاز الثاني بعيد ، فلن نستطيع ذلك .. الحل هو تحميل هذه الكلاسات من مكان ما (web server ) وقت تشغيل العميل Loading Classes at Runtime .. أي أننا سنقوم بتحميل الملفات وقت تشغيل العميل ،

Dynamic code downloading

أحد ميزات لغه جافا هي أنك تستطيع تحميل كود Byte Code (ملفات .class) من موقع URL وتنفذه في جهازك ... مثلا عندما تقوم بفتح Applet فإن الـ JVM الموجوده في متصفح الأنترنت تقوم أولا بتحميل الByte Code من السيرفر الى جهازك ، ومن ثم تبدأ في تنفيذه ..

RMI قامت بتطيق نفس الفكره ، حيث تستطيع باستخدام RMI API (وليس JVM كما في الأبليت) بتحميل الكلاسات الموجوده في الجهاز البعيد ، وهنا نحن نريد تحميل ملف Stub الذي يجب أن يكون موجود لدى الكلاينت حتى يعمل بشكل صحيح ..

وتستطيع تحميل الكلاسات من الجهاز البعيد عن طريق تحديد الموقع في CodeBase .. هذه الcodebase يمكن أن نعتبرها مصدر للكود ، ويقوم العميل عندما يبدأ في العمل بالذهاب الى هذا المكان وتحميل الكود ويبدأ العمل بعدها . ويكمن التعامل مع codebase في الأبليت أو الApplication العادي .

حاليا نريد تحميل كلاس Stub (واي كلاسات ثانيه في حال أحتاج العميل لذلك ، مثلا القيمه الراجعه من السيرفر كائن غير معروف لدي الكلاينت) ، هنا سنقوم بتحديد موقع الكلاسات عندما نشغل السيرفر .. وعندما يتصل الكلاينت بالسيرفر سوف يبحث لديه في classPath ، فاذا لم يجد الكلاسات فسوف يذهب الى ذلك المكان ويقوم بتحميل الكلاسات التي يحتاجها ..

يقوم السيرفر بتحديد موقع الكلاسات عن طريق :

java.rmi.server.codebase=http://MyHost/MyFile/

نقوم بكتابتها وقت تشغيل السيرفر ، بالمعامل -D .. لا تنسى / في الأخر ..

الصور التاليه ، تشرح طريقه العمل بشكل مبسط وجيد ...

codebase-1.gif

هنا الصوره السابقه تشرح كيف يقوم الأبليت بتحميل ملفات الكلاسات التي يحتاجها ثم يقوم بتنفيذها .

codebase-2.gif

هنا نوضح الصوره السابقه الكثير من الأمور ، تبدأ بتحديد الخادم موقع ملفات الكلاسات ، بعدها (الخطوه1) يقوم بتسجل اسم الكائن ، بعدها يقوم الكلاينت بأخذ الكائن الراجع بعد طلبه ، ويقوم بتحميل ملف Stub من المكان الذي حدده الخادم ويعود للكلاينت ومنه في الصوره القادمه) يقوم باستدعاء الداله البعيده ..

codebase-3.gif

طبعا يمكن أن يرسل الكلاينت كائن غير معروف للسيرفر ، هنا في هذه الحاله سوف يضطر الكلاينت أن يحدد للسيرفر موقع الكلاس لهذا الكائن حتى يقوم بتحميله ، يعني أي جهه تحدد للأخرى موقع تحميل الكلاسات للكائنات التي لا تعرفها الجهه ... الصوره التاليه توضح ذلك .

codebase-4.gif

هناك مسأله بسيطه وهي أن الكلاينت (في Application ) لا يستطيع تحميل الكلاسات بشكل مباشر لأنها قد تحتوي على كود خبيث :) -هي من أعدادات اللغه الأفتراضيه- ، لذلك يجب أن تحدد له ذلك ، وهو موضوع RMI Security .

RMI Security

في حال كانت جميع الكلاسات متوفره في الجهازين ، فلن توجد مشكله لأننا لن نحمل أي كود من أي جهه .. لكن في حال أحتاج الكلاينت لكلاس من السيرفر غير موجود في الكلاينت ، سوف يرسل له السيرفر هذا الكلاس ولكن سوف يبدأ هذا الكائن بالعمل مباشره بدون تدخل منك فورا بعد عمليه Deserialization .. وبالطبع مثل هذا قد يمثل خطوره لذلك لغه جافا بالوضع الأفتراضي لها لا تسمح لك بهذا (ما عدا في الأبليت) .

المسؤول عن تحميل هذه الكلاسات هو SecureClassLoader ويجب أن نحدد لهذا الكائن حدود عمله restrictions موجوده في java.policy ، ويمكن أن نغير فيها عن طريق الأداه policytool .. أيضا هناك الكائن RMISecurityManager (وهو أبن لـ SecurityManager) ويمكن عن طريقه أن نتحكم بـ security policy . وهو الذي يسمح في الحقيقه للـ Applet بالعمل وتحميل الملفات، لذلك لكي نستطيع تحميل الكلاسات يجب أن نقوم بعمل كائن من RMISecurityManager .

اذا يمكننا أن نتحكم بـ security policy عن طريق الأداه policytool أو عن طريق العمل مع الكائن RMISecurityManager .. لكن العمل المباشر مع هذا الكائن لن يفيد كثيرا لأنه يعتمد على الأعدادات الأفتراضيه في security policy ، لذلك يمكن أن ننشيء Security Manager خاص بنا يسمح بكل شيء وهو عن طريق وراثه الكلاس RMISecurityManager واعاده تعريف الداله checkPermission ..

مثال :

 

[color= #666666; font-style: italic;]// Zero security Manager

 

[color= #000000; font-weight: bold;]import [color= #006699;]java.security.Permission[color= #339933;];

[color= #000000; font-weight: bold;]import [color= #006699;]java.rmi.RMISecurityManager[color= #339933;];

 

[color= #000000; font-weight: bold;]public [color= #000000; font-weight: bold;]class ZeroSecurityManager [color= #000000; font-weight: bold;]extends 

[color= #003399;]RMISecurityManager

[color= #009900;]{

[color= #000000; font-weight: bold;]public [color= #000066; font-weight: bold;]void checkPermission [color= #009900;]( [color= #003399;]Permission permission [color= #009900;])

[color= #009900;]{

[color= #003399;]System.[color= #006633;]out.[color= #006633;]println[color= #009900;]([color= #0000ff;]"Check Permission for : " [color= #339933;]+

 permission.[color= #006633;]toString[color= #009900;]([color= #009900;])[color= #009900;])[color= #339933;];

[color= #009900;]}

 

[color= #009900;]}

الأن عندما يبدأ الكلاينت العمل يجب أن نحدد له ما يفعله عن طريق الداله setSecurityManager ونمرر له الكائن SecurityManager أو أي Subclass منه .

Fibonacci Computing

المثال الأخير (شامل لكل المفاهيم ان شاء الله) وهو لسيرفر لديه داله تحسب رقم الحد الفلاني في سلسله Fibonacci الشهيره .. ونريد بأن يعمل على جهازين منفصلين ، وقد تمت تجربته بين نظام windows Server2003 و Windows Xp ..

الغرض من البرنامج هو أن يقوم العميل باستدعاء داله حساب Fibonacci في السيرفر ويمرر لها رقم ، وترجع هذه الداله الناتج ويطبع لدى الكلاينت .. نريد أن يكون الكلاينت مره Application ومره Applet ..

نبدأ من جهه الخادم :

سوف يكون لديه ثلاثه ملفات :

Fibonacci هو الأنترفيس

FibonacciImpl هو تطبيق لهذا الأنترفيس

FibonacciServer هو ملف السيرفر

ملف Fibonacci.java :

 

[color= #000000; font-weight: bold;]import [color= #006699;]java.rmi.RemoteException[color= #339933;];

[color= #000000; font-weight: bold;]import [color= #006699;]java.rmi.Remote[color= #339933;];

[color= #000000; font-weight: bold;]import [color= #006699;]java.math.BigInteger[color= #339933;];

 

[color= #000000; font-weight: bold;]public [color= #000000; font-weight: bold;]interface Fibonacci [color= #000000; font-weight: bold;]extends [color= #003399;]Remote

[color= #009900;]{

[color= #000000; font-weight: bold;]public [color= #003399;]BigInteger getFibonacci[color= #009900;]([color= #000066; font-weight: bold;]int number[color= #009900;])

 [color= #000000; font-weight: bold;]throws [color= #003399;]RemoteException[color= #339933;];

[color= #000000; font-weight: bold;]public [color= #003399;]BigInteger getFibonacci[color= #009900;]([color= #003399;]BigInteger bi[color= #009900;])

 [color= #000000; font-weight: bold;]throws [color= #003399;]RemoteException[color= #339933;];

[color= #009900;]}

ملف FibonacciImpl.java :

 

[color= #666666; font-style: italic;]// Fibonacci Implementation

 

[color= #000000; font-weight: bold;]import [color= #006699;]java.rmi.server.UnicastRemoteObject[color= #339933;];

[color= #000000; font-weight: bold;]import [color= #006699;]java.rmi.RemoteException[color= #339933;];

[color= #000000; font-weight: bold;]import [color= #006699;]java.math.BigInteger[color= #339933;];

 

[color= #000000; font-weight: bold;]public [color= #000000; font-weight: bold;]class FibonacciImpl [color= #000000; font-weight: bold;]extends 

[color= #003399;]UnicastRemoteObject

 [color= #000000; font-weight: bold;]implements Fibonacci

[color= #009900;]{

[color= #000000; font-weight: bold;]public FibonacciImpl [color= #009900;]([color= #009900;]) [color= #000000; font-weight: bold;]throws [color= #003399;]RemoteException 

[color= #009900;]{	[color= #009900;]}

 

[color= #000000; font-weight: bold;]public [color= #003399;]BigInteger getFibonacci [color= #009900;]([color= #000066; font-weight: bold;]int number [color= #009900;]) 

[color= #000000; font-weight: bold;]throws [color= #003399;]RemoteException

[color= #009900;]{

[color= #000000; font-weight: bold;]return [color= #000000; font-weight: bold;]this.[color= #006633;]getFibonacci[color= #009900;]( [color= #000000; font-weight: bold;]new [color= #003399;]BigInteger[color= #009900;]([color= #0000ff;]"" [color= #339933;]+ number[color= #009900;]) [color= #009900;])[color= #339933;];

[color= #009900;]}

 

[color= #000000; font-weight: bold;]public [color= #003399;]BigInteger getFibonacci [color= #009900;]([color= #003399;]BigInteger bInt[color= #009900;])

 [color= #000000; font-weight: bold;]throws [color= #003399;]RemoteException

[color= #009900;]{

[color= #003399;]BigInteger zero [color= #339933;]= [color= #000000; font-weight: bold;]new [color= #003399;]BigInteger[color= #009900;]([color= #0000ff;]"0"[color= #009900;])[color= #339933;];

[color= #003399;]BigInteger one  [color= #339933;]= [color= #000000; font-weight: bold;]new [color= #003399;]BigInteger[color= #009900;]([color= #0000ff;]"1"[color= #009900;])[color= #339933;];

 

[color= #000000; font-weight: bold;]if [color= #009900;](bInt.[color= #006633;]equals[color= #009900;](zero[color= #009900;])[color= #009900;]) [color= #000000; font-weight: bold;]return one[color= #339933;];

[color= #000000; font-weight: bold;]if [color= #009900;](bInt.[color= #006633;]equals[color= #009900;](one[color= #009900;])[color= #009900;])  [color= #000000; font-weight: bold;]return one[color= #339933;]; 

 

[color= #003399;]BigInteger i	 [color= #339933;]= one[color= #339933;];

[color= #003399;]BigInteger low   [color= #339933;]= one[color= #339933;];

[color= #003399;]BigInteger high  [color= #339933;]= one[color= #339933;];

 

[color= #000000; font-weight: bold;]while [color= #009900;](i.[color= #006633;]compareTo[color= #009900;](bInt[color= #009900;]) [color= #339933;]== [color= #cc66cc;]-1[color= #009900;]) 

[color= #009900;]{

	[color= #003399;]BigInteger temp [color= #339933;]= high[color= #339933;];

	high [color= #339933;]= high.[color= #006633;]add[color= #009900;](low[color= #009900;])[color= #339933;];

	low [color= #339933;]= temp[color= #339933;]; 

	i [color= #339933;]= i.[color= #006633;]add[color= #009900;](one[color= #009900;])[color= #339933;];

[color= #009900;]}

 

[color= #000000; font-weight: bold;]return high[color= #339933;];

[color= #009900;]}

 

[color= #009900;]}

ملف FibonacciServer.java :

 

[color= #666666; font-style: italic;]// Fibonacci Server

 

[color= #000000; font-weight: bold;]import [color= #006699;]java.rmi.Naming[color= #339933;];

[color= #000000; font-weight: bold;]import [color= #006699;]java.rmi.RemoteException[color= #339933;];

[color= #000000; font-weight: bold;]import [color= #006699;]java.net.MalformedURLException[color= #339933;];

 

[color= #000000; font-weight: bold;]public [color= #000000; font-weight: bold;]class FibonacciServer 

[color= #009900;]{

[color= #000000; font-weight: bold;]private [color= #000000; font-weight: bold;]static [color= #000000; font-weight: bold;]final [color= #003399;]String HOST [color= #339933;]= [color= #0000ff;]"yourHost"[color= #339933;];

 

[color= #000000; font-weight: bold;]public [color= #000000; font-weight: bold;]static [color= #000066; font-weight: bold;]void main [color= #009900;]([color= #003399;]String args[color= #009900;][[color= #009900;]][color= #009900;])

[color= #009900;]{

[color= #000000; font-weight: bold;]try

[color= #009900;]{

FibonacciImpl myObject [color= #339933;]= [color= #000000; font-weight: bold;]new FibonacciImpl[color= #009900;]([color= #009900;])[color= #339933;];

[color= #003399;]String myObjectName [color= #339933;]= [color= #0000ff;]"rmi://" [color= #339933;]+ HOST [color= #339933;]+ [color= #0000ff;]"/MyFib"[color= #339933;];

 

[color= #003399;]Naming.[color= #006633;]rebind[color= #009900;](myObjectName,myObject[color= #009900;])[color= #339933;];

[color= #003399;]System.[color= #006633;]out.[color= #006633;]println[color= #009900;]([color= #0000ff;]"Server Ready to Word ......"[color= #009900;])

[color= #009900;]}

[color= #000000; font-weight: bold;]catch [color= #009900;]([color= #003399;]RemoteException e[color= #009900;])

[color= #009900;]{	e.[color= #006633;]printStackTrace[color= #009900;]([color= #009900;])[color= #339933;];	[color= #009900;]}

[color= #000000; font-weight: bold;]catch [color= #009900;]([color= #003399;]MalformedURLException e[color= #009900;])

[color= #009900;]{	e.[color= #006633;]printStackTrace[color= #009900;]([color= #009900;])[color= #339933;];	[color= #009900;]}

[color= #009900;]}

 

[color= #009900;]}

نقوم في السيرفر بترجمه هذه الملفات :

javac *.java

ونقوم بتفح مترجم rmi :

rmic FibonacciImpl

الأن خرجت الملفات :

Fibonacci.class

FibonacciImpl.class

FibonacciServer.class

FibonacciImpl_Stub.class

نقوم الأن بنقل ملفات الـ Class التي سوف يقوم الكلاينت بتحميلها من هناك .. ننقلها في web-Server .. بعدها أحذف ملف FibonacciImpl_Stub.class من المجلد الحالي ، فقط دع هذا الملف في الويب سيرفر ، حتى عندما نشغل السيرفر يتصل بهذا الملف ..

ونشغل ملف registry :

rmiregistry

وأخيرا نشغل الخادم :

java - Djava.rmi.server.codebase=http://MySite/file/ FibonacciServer

الأن نبدأ العمل في الكلاينت :

سوف يكون لديه ثلاثه ملفات :

Fibonacci.java هو الأنترفيس

ZeroSecurityManager.java

FibonacciClient.java

ملف الأنترفيس هو نفسه الذي في السيرفر ويجب أن يكون هنا ، حتى يتم عمل Compiler للـFibonacciClient.java .

ملف ZeroSecurityManager.java عرضنا كوده في الأعلى ..

FibonacciClient.java :

 

[color= #000000; font-weight: bold;]import [color= #006699;]java.rmi.RemoteException[color= #339933;];

[color= #000000; font-weight: bold;]import [color= #006699;]java.rmi.Naming[color= #339933;];

[color= #000000; font-weight: bold;]import [color= #006699;]java.rmi.NotBoundException[color= #339933;];

[color= #000000; font-weight: bold;]import [color= #006699;]java.net.MalformedURLException[color= #339933;];

 

[color= #000000; font-weight: bold;]public [color= #000000; font-weight: bold;]class FibonacciClient

[color= #009900;]{

[color= #000000; font-weight: bold;]private [color= #000000; font-weight: bold;]static [color= #000000; font-weight: bold;]final [color= #003399;]String HOST [color= #339933;]= [color= #0000ff;]"ServerHost"[color= #339933;];

 

[color= #000000; font-weight: bold;]public [color= #000000; font-weight: bold;]static [color= #000066; font-weight: bold;]void main[color= #009900;]([color= #003399;]String args[color= #009900;][[color= #009900;]][color= #009900;])

[color= #009900;]{

[color= #000000; font-weight: bold;]try

[color= #009900;]{

[color= #000000; font-weight: bold;]if [color= #009900;]( [color= #003399;]System.[color= #006633;]getSecurityManager[color= #009900;]([color= #009900;]) [color= #339933;]== [color= #000066; font-weight: bold;]null [color= #009900;])

    [color= #003399;]System.[color= #006633;]setSecurityManager[color= #009900;]([color= #000000; font-weight: bold;]new ZeroSecurityManager[color= #009900;]([color= #009900;])[color= #009900;])[color= #339933;];

 

[color= #003399;]String myObjectName [color= #339933;]= [color= #0000ff;]"rmi://" [color= #339933;]+ HOST [color= #339933;]+ [color= #0000ff;]"/MyFib"[color= #339933;];

Fibonacci myObject [color= #339933;]= [color= #009900;](Fibonacci[color= #009900;]) [color= #003399;]Naming.[color= #006633;]lookup[color= #009900;](myObjectName[color= #009900;])[color= #339933;];

 

[color= #003399;]System.[color= #006633;]out.[color= #006633;]println[color= #009900;]( myObject.[color= #006633;]getFibonacci[color= #009900;]([color= #cc66cc;]34[color= #009900;])  [color= #009900;])[color= #339933;];

[color= #003399;]System.[color= #006633;]out.[color= #006633;]println[color= #009900;]( myObject.[color= #006633;]getFibonacci[color= #009900;]([color= #cc66cc;]55[color= #009900;])  [color= #009900;])[color= #339933;];

[color= #003399;]System.[color= #006633;]out.[color= #006633;]println[color= #009900;]( myObject.[color= #006633;]getFibonacci[color= #009900;]([color= #cc66cc;]155[color= #009900;]) [color= #009900;])[color= #339933;];

[color= #009900;]}

[color= #000000; font-weight: bold;]catch [color= #009900;]([color= #003399;]RemoteException e[color= #009900;])

[color= #009900;]{	e.[color= #006633;]printStackTrace[color= #009900;]([color= #009900;])[color= #339933;];	[color= #009900;]}

[color= #000000; font-weight: bold;]catch [color= #009900;]([color= #003399;]NotBoundException e[color= #009900;])

[color= #009900;]{	e.[color= #006633;]printStackTrace[color= #009900;]([color= #009900;])[color= #339933;];	[color= #009900;]}

[color= #000000; font-weight: bold;]catch [color= #009900;]([color= #003399;]MalformedURLException e[color= #009900;])

[color= #009900;]{	e.[color= #006633;]printStackTrace[color= #009900;]([color= #009900;])[color= #339933;];	[color= #009900;]}

[color= #009900;]}

 

[color= #009900;]}

الأن :

ترجم الملفات الثلاثه :

javac *.java

وشغل الكلاينت :

java FibonacciClient

وسترى المخرج أخييييرا على الشاشه :) .

في حال أردنا العمل على Applet ، نكتب ملف AppletFibonacci.java ونضعه في السيرفر ، ونترجمه ، وننقل ملف الكلاس الى الويب سيرفر مع بقيه الكلاسات .. بالأضافه الى صفحه html يتصل من خلالها العميل ..

AppletFibonacci.java

 

[color= #000000; font-weight: bold;]import [color= #006699;]java.applet.Applet[color= #339933;];

[color= #000000; font-weight: bold;]import [color= #006699;]java.awt.*[color= #339933;];

[color= #000000; font-weight: bold;]import [color= #006699;]java.awt.event.*[color= #339933;];

[color= #000000; font-weight: bold;]import [color= #006699;]java.rmi.*[color= #339933;];

[color= #000000; font-weight: bold;]import [color= #006699;]java.math.BigInteger[color= #339933;];

 

[color= #000000; font-weight: bold;]public [color= #000000; font-weight: bold;]class AppletFibonacci [color= #000000; font-weight: bold;]extends [color= #003399;]Applet

[color= #009900;]{

  [color= #000000; font-weight: bold;]private [color= #003399;]TextArea  resultArea [color= #339933;]= 

[color= #000000; font-weight: bold;]new [color= #003399;]TextArea[color= #009900;]([color= #0000ff;]"", [color= #cc66cc;]20, [color= #cc66cc;]72, [color= #003399;]TextArea.[color= #006633;]SCROLLBARS_BOTH[color= #009900;])[color= #339933;];

  [color= #000000; font-weight: bold;]private [color= #003399;]TextField inputArea  [color= #339933;]= [color= #000000; font-weight: bold;]new [color= #003399;]TextField[color= #009900;]([color= #cc66cc;]24[color= #009900;])[color= #339933;];

  [color= #000000; font-weight: bold;]private [color= #003399;]Button calculate [color= #339933;]= [color= #000000; font-weight: bold;]new [color= #003399;]Button[color= #009900;]([color= #0000ff;]"Calculate"[color= #009900;])[color= #339933;];

  [color= #000000; font-weight: bold;]private [color= #003399;]String server[color= #339933;];

 

  [color= #000000; font-weight: bold;]public [color= #000066; font-weight: bold;]void init[color= #009900;]( [color= #009900;])

  [color= #009900;]{

[color= #000000; font-weight: bold;]this.[color= #006633;]setLayout[color= #009900;]([color= #000000; font-weight: bold;]new [color= #003399;]BorderLayout[color= #009900;]( [color= #009900;])[color= #009900;])[color= #339933;];

[color= #003399;]Panel north [color= #339933;]= [color= #000000; font-weight: bold;]new [color= #003399;]Panel[color= #009900;]( [color= #009900;])[color= #339933;];

north.[color= #006633;]add[color= #009900;]([color= #000000; font-weight: bold;]new [color= #003399;]Label[color= #009900;]([color= #0000ff;]"Type a non-negative integer"[color= #009900;])[color= #009900;])[color= #339933;];

north.[color= #006633;]add[color= #009900;](inputArea[color= #009900;])[color= #339933;];

north.[color= #006633;]add[color= #009900;](calculate[color= #009900;])[color= #339933;];

[color= #000000; font-weight: bold;]this.[color= #006633;]add[color= #009900;](resultArea, [color= #003399;]BorderLayout.[color= #006633;]CENTER[color= #009900;])[color= #339933;];

[color= #000000; font-weight: bold;]this.[color= #006633;]add[color= #009900;](north, [color= #003399;]BorderLayout.[color= #006633;]NORTH[color= #009900;])[color= #339933;];

 

Calculator c [color= #339933;]= [color= #000000; font-weight: bold;]new Calculator[color= #009900;]( [color= #009900;])[color= #339933;];

inputArea.[color= #006633;]addActionListener[color= #009900;](c[color= #009900;])[color= #339933;];

calculate.[color= #006633;]addActionListener[color= #009900;](c[color= #009900;])[color= #339933;];

resultArea.[color= #006633;]setEditable[color= #009900;]([color= #000066; font-weight: bold;]false[color= #009900;])[color= #339933;];

 

server [color= #339933;]= [color= #0000ff;]"rmi://" [color= #339933;]+ [color= #000000; font-weight: bold;]this.[color= #006633;]getCodeBase[color= #009900;]( [color= #009900;]).[color= #006633;]getHost[color= #009900;]( [color= #009900;]) [color= #339933;]+ [color= #0000ff;]"/MyFib"[color= #339933;];

  [color= #009900;]}

 

  [color= #000000; font-weight: bold;]class Calculator [color= #000000; font-weight: bold;]implements [color= #003399;]ActionListener 

  [color= #009900;]{

[color= #000000; font-weight: bold;]public [color= #000066; font-weight: bold;]void actionPerformed[color= #009900;]([color= #003399;]ActionEvent evt[color= #009900;]) 

[color= #009900;]{

  [color= #000000; font-weight: bold;]try 

  [color= #009900;]{

[color= #003399;]String input [color= #339933;]= inputArea.[color= #006633;]getText[color= #009900;]( [color= #009900;])[color= #339933;];

[color= #000000; font-weight: bold;]if [color= #009900;](input [color= #339933;]!= [color= #000066; font-weight: bold;]null[color= #009900;]) 

[color= #009900;]{

  [color= #003399;]BigInteger index [color= #339933;]= [color= #000000; font-weight: bold;]new [color= #003399;]BigInteger[color= #009900;](input[color= #009900;])[color= #339933;];			

  Fibonacci f [color= #339933;]= [color= #009900;](Fibonacci[color= #009900;]) [color= #003399;]Naming.[color= #006633;]lookup[color= #009900;](server[color= #009900;])[color= #339933;];

  [color= #003399;]BigInteger result [color= #339933;]=  f.[color= #006633;]getFibonacci[color= #009900;](index[color= #009900;])[color= #339933;];

  resultArea.[color= #006633;]setText[color= #009900;](result.[color= #006633;]toString[color= #009900;]( [color= #009900;])[color= #009900;])[color= #339933;];

[color= #009900;]}

  [color= #009900;]} 

  [color= #000000; font-weight: bold;]catch [color= #009900;]([color= #003399;]Exception ex[color= #009900;])

 [color= #009900;]{ resultArea.[color= #006633;]setText[color= #009900;](ex.[color= #006633;]getMessage[color= #009900;]( [color= #009900;])[color= #009900;])[color= #339933;]; [color= #009900;]}   

[color= #009900;]}

  [color= #009900;]}

[color= #009900;]}

HTML Page :

 

[color= #339933;]<html[color= #339933;]>

[color= #339933;]<head[color= #339933;]>

[color= #339933;]<title[color= #339933;]>RMI Applet[color= #339933;]</title[color= #339933;]>

[color= #339933;]</head[color= #339933;]>

[color= #339933;]<body[color= #339933;]>

[color= #339933;]<h1[color= #339933;]>RMI Applet[color= #339933;]</h1[color= #339933;]>

[color= #339933;]<p[color= #339933;]>

[color= #339933;]<applet align[color= #339933;]=[color= #0000ff;]"center" code[color= #339933;]=[color= #0000ff;]"AppletFibonacci" 

width[color= #339933;]=[color= #0000ff;]"300" height[color= #339933;]=[color= #0000ff;]"100"[color= #339933;]>

[color= #339933;]</applet[color= #339933;]>

[color= #339933;]</p[color= #339933;]>

[color= #339933;]</body[color= #339933;]>

[color= #339933;]</html[color= #339933;]>

بعد ترجمه AppletFibonacci ضع ملف الكلاس في السيرفر هو وصفحه الـ HTML ، وسوف تتم العمليه بنجاح ..

الأن ترجم في السيرفر :

java FibonacciServer

لا داعى لوجود codebase لأن الأبليت سوف يحمل الملفات بنفسه ..

وفي الكلاينت أدخل على صفحه HTML الموجوده بالسيرفر من خلال متصفح الأنترنت

RMI5.PNG

الى هنا نكون أنهينا الموضوع وليس بشكل كامل ، فموضوع الـ security موضوع متداخل كثيرا وبه الكثير من الأمور الغامضه .. اضافه الى أن تحميل كود من الجهه الثانيه غير مضمون كثيرا ، فقد تحصل مشاكل مثلا قطع الأتصال أو مشكله في اعدادك للبرنامج ، لذلك اذا استطعت نقل الكلاسات يدويا فهذا أفضل بكثير ويخرجك من جميع هذه المتاهات .

المصادر :

Dynamic code downloading using RMI

Java Network Programming, 3rd Edition

Introduction to Network Programming in java

Core Java 2 , Part II

أرجوا أن يفهم أحد شيئا من الكلام المشوش أعلاه :) ..

والسلام عليكم ....

تم تعديل هذه المشاركة بواسطة Wajdy Essam في 23 سبتمبر 2008 في 09:28

http://informatic-ar.com منصة تعليمية عربية في علوم الحاسب والبرمجة

https://moalfat.com  للكتب الالكترونية والكورسات التعليمية

Everything we see now is just an engineering solution based on old science

#2

تدري صارلي اسبوع ادرس هالشي ,, انذبحت مالكيت ولا موقع عربي يتكلم عليها بس اخ مصري اهنا مسوي كتيب بي دي اف صغير يتكلم عنها ,,,

احسنت وبارك الله بيك موضوع مهم جدا

في فقرة انه ممكن نعمل ال rmiregistry عن طريق الكود كالاتي

java.rmi.registry.LocateRegistry.createRegistry(1099);

وايضا ممكن نضيف عملية ال rmic كأمر دوز شيل داخل الكود حتى توفر علينا وقت

Process process=Runtime.getRuntime().exec("cmd.exe /C rmic HelloWorldServer");

تحياتي الك اخوية العزيز

m16.jpg

سأحمل روحي على راحتي والقي بها في مهاوي الردا

فأما حياة تسر الصديق و اما ممات يغيض العدا

#3

java.rmi.registry.LocateRegistry.createRegistry(1099);

بالنسبة للبورة1099 فهو اختياري و الايرمي سهلة بس يلزمك بعض الاوامر و شكرا

image-27.gif

concepteur realisateur java j2ee

php5 and asp

#4

السلام عليكم ... و حياك الله استاذنا وجــدي

في الحقيقة درس و مقالة رائعة من استاذ رائع .. من الجيد فعلاً ان نقرأ دروساً و مقالات بهذه الجودة العالية ... فجزاك الله كل الخير.

احببت قراءة بعض الشروحات اكثر من مرة ... خصوصاً و اني منذ فترة اعمل على فهم و تطبيق معمارية SOA على احد المشاريع )و التي تتضمن البروتوكول SOPA و الـ web services و WSDL و UDDI و XML... الخ) ...

و لكن انت تعرف اني تلميذة مزعجة و ان استفساراتي عديدة ... فتحملني ...

كبداية عندما حاولت تشغيل الريجستري بالامر rmiregistry فيظهر لي في الـ title Bar ان الريجستري شغال .. و لكني لا استطيع طتابة شيء (لا تظهر لي الكتابة) .. و بعد ان اعمل ctr+c يظهر لي الخطأ (Exception) بشكل التالي :

post-20471-1222205252_thumb.jpg

ايضاً لماذا حينما نطبق الواجهة Interface الحاوية على تعريف الخدمات المقدمة في السيرفر (في المثال هنا HellpImpl) نرث الكلاس UnicastRemoteObject ؟؟؟ و هنا اريد ان اعرف هل ممكن ان يرتبط السيرفر باكثر من عميل ... ايضاً هل ممكن ان يرتبط العميل بأكثر من سيرفر ؟؟؟

اخيراً و كما تعرف ان الـ RMI (و ايضاً الـ CORBA) هيtightly-coupled بمعنى ان العميل و السيرفر يكونا مرتبطين تماماً ... فهل سيكون لها مستقبلاً مكان بين تطبيقات الويب مثل الـ web-services و التي هي loosely-coupled و تدعم تكامل التطبيقات ؟؟؟

ما زلت اقرأ في درسك اخي الكريم ... و اكرر شكري لك على هذا الموضوع :)

لا اله الا الله .. محمد رسول الله

(* ربِ اجعلني مقيم الصلاة و من ذريتي ، ربنا و تقبل دعـاء *)

يا حيّ يا قيـوم ... برحمتك استغيث ... اصلح لي شأني كله و لا تكلني الى نفسي طرفة عين

#5

حياكم الله ، وأسأل الله أن يغفر لنا في هذه الأيام المباركه ..

اقتباس
كبداية عندما حاولت تشغيل الريجستري بالامر rmiregistry فيظهر لي في الـ title Bar ان الريجستري شغال .. و لكني لا استطيع طتابة شيء (لا تظهر لي الكتابة) .. و بعد ان اعمل ctr+c يظهر لي الخطأ (Exception) بشكل التالي

نعم ، الrmiregistry بعد تشغيله فلن تستطيع التعامل معه ولا كتابه أي شيء ,,, الا برمجيا عن طريق الداله Naming.bind و Naming.lookup .. أو يمكن أن نستخدم دوال الأنترفيس java.rmi.registry.Registry وهي بنفس الأسم bind و lookup ، ولكن يمكن أن لا نحتاج لها (لأن الـ Naming يطبق implements الأنترفيس java.rmi.registry.Registry ) ..

لذلك نقوم بتشغيل الـ rmiregistry فقط وبرمجيا سوف نتعامل معه (نقوم بتسجيل كائن -من جهه السيرفر- ، ونقوم بالحصول على هذا الكائن -من خلال الكلاينت) ...

تبدوا المشكله لديك أنك شغلت الكلاينت و السيرفر لا يعمل ، والrmiregistry لا يعمل لأنك ضغت CTRL+C ..

لذلك قم أولا بتشغيل الrmiregistry .. بعدها شغل السيرفر ، بعدها شغل الكلاينت ... (كل واحد منهم في نافذه ) ..

وحتى تريح من فتح عده نوافذ كل مره ، هناك أمر في DOS يقوم بتنفيذ الأمر الحالي في نافذه جديده ، الأمر هو start .

مثلا نريد أن نشغل rmiregistry :

start rmiregistry

وسوف يعمل في نافذه دوز جديده ..

بعدها :

start server

وسوف يعمل في نافذه جديده ..

وأخيرا نشغل الكلاينت ...

فقط تأكد من أن يكون الrmiregistry هو أول من يعمل ، لأن السيرفر سوف يسجل الكائن باسم ما في هذه الخدمه ..

اقتباس
ايضاً لماذا حينما نطبق الواجهة Interface الحاوية على تعريف الخدمات المقدمة في السيرفر (في المثال هنا HellpImpl) نرث الكلاس UnicastRemoteObject ؟؟؟

أولا الـ Interfcae يجب أن يرث Remote حتى يتم أعتباره Remote Interface وهو الذي أن يكون للكلاينت نسخه منه ، حتى يستطيع الترجمه بشكل صحيح ..

أما الكلاس Imp ، فهو يعتبر Remote Object وذلك عن طريق عمل Implement لأي أنترفيس يرث Remote ، بالأضافه الى الوراثه من الكلاس RemoteObject أو أحد أبناء هذا الكلاس ..

الكلاس RemoteObject الموجود في الباكج java.rmi.server هو كلاس Abstract ، أضا هناك كلاس ورث منه هو RemoteServer موجود في نفس الباكج لكنه أيضا هو كلاس Abstract ، بمعنى اذا أردت العمل مع هذه الكلاسات سوف نضطر الى اعاده تعريف بعض الدوال ...

أخيرا الكلاس UnicastRemoteObject ورث RemoteServer وقام بتعريف جميع الدوال (هو الذي يقوم بعمليه Marshalling و Unmarsalling ، هو الذي سوف يقوم بعمليه الأتصال حيث يستخدم TCP-Socket ) لذلك هو جاهز لكي يكون Remote Object ...

لذلك سوف نستخدم UnicastRemoteObject في السيرفر لكي يقوم بهذه الأمور ،، لكن مثلا في حال أراد السيرفر أن يتعامل مع UDP-Socket أو Mutlcast-Socet فهنا عليه أن يرث الكلاس RemoteServer ويقوم بتعريف دوال الأتصال بنفسه .

اقتباس
هنا اريد ان اعرف هل ممكن ان يرتبط السيرفر باكثر من عميل ... ايضاً هل ممكن ان يرتبط العميل بأكثر من سيرفر ؟؟؟

نعم يستطيع السيرفر بأن يتعامل مع أكثر من عميل ... فقط العميل يقوم بالحصول على اسم الكائن ، ويأخذ نسخه Reference منه ، ويقوم باستدعاء الداله المناسبه ، والسيرفر سوف يتعامل معه بلا مشاكل .. (لم أجربها حقيقه ولكن مجرد تخمين ، سأتأكد منه لاحقا ، وأنتم أيضا) .

ويستطيع العميل بأن يتعامل مع أكثر من سيرفر ، مثلا يأخذ على أسم كائن موجود في جهه ما ، وأسم كائن موجود في جهه أخرى ، ويقوم باستدعاء الدوال الموجوده في كل كائن ... ولن توجد مشاكل ان شاء الله ..

ويمكن للعميل أن يطلب من السيرفر (أو بالأصح الRegistry في السيرفر ) جميع أسماء الكائنات الموجوده لديك ، وذلك عن طريق الداله list الموجوده في الكلاس Naming ..

public static String[] list(String url) throws RemoteException, MalformedURLException

لكي نستخدم هذه الداله نرسل معامل url ، وسوف يرجع لنا جميع اسماء الكائنات الموجوده في الـ Registry .. طبعا الداله قد تصلح عند اصلاح المشاكل RMI Debuggin ... فمن خلالها سوف نعرف أسم الكائن الموجود في السيرفر حتى نستدعيه بشكل صحيح ، فربما هناك خطأ في كتابه الأسم ، أو قام السيرفر بتغيير الأسم والعميل لا يدري .. وغيرها من المشاكل التي قد تحدث ..

اقتباس
اخيراً و كما تعرف ان الـ RMI (و ايضاً الـ CORBA) هيtightly-coupled بمعنى ان العميل و السيرفر يكونا مرتبطين تماماً ... فهل سيكون لها مستقبلاً مكان بين تطبيقات الويب مثل الـ web-services و التي هي loosely-coupled و تدعم تكامل التطبيقات ؟؟؟

حقيقه لا أعلم ،،أصلا معلوماتي صفر في تطبيقات الويب .. لذلك يحول السؤال اليك ، وعليك الاجابه :P

http://informatic-ar.com منصة تعليمية عربية في علوم الحاسب والبرمجة

https://moalfat.com  للكتب الالكترونية والكورسات التعليمية

Everything we see now is just an engineering solution based on old science

#6

فعلا درس جميل يا وجدي , وفعلا تكنولوجيا RMI اخقت تعقيدات Sockets , لذلك هي تكنولوجيا سهلة ومتطورة وجميلة , حيث استخدمتها في تصميم مشروعي النحرج في الجامعة ,,,, بارك الله فيك مجددا يا وجدي.

#7

لديك صبر عجيب وجدي

ماشاء الله

لا أعلم كيف تصبر على كتابة هذه المواضيع الطويلة

أتمنى لو أن لي القدرة على الصبر هكذا

بالنسبة لموضوع Dynamic code downloading

هلا كتبت الأمر كاملاً من فضلك؟؟

ما أهمية هذا الموضوع؟؟

يهيألي أنه في بعض الأحيان تتم تعديلات على الخدمات التي يقدمها الكائن البعيد

تتطلب التعديل على هيكلية الواجهة الواصلة بينه وبين السيرفر

لهذا يتم تحميل الموصل stub بدلاً من اللجوء في كل مرة إلى تغييره يدوياً

هل كلامي صحيح أم هناك شيء آخر؟

تحياتي

حزمة المحرك الإصدارة 0.8

أي أحد يجد أني ظلمته فليراسلني

وبإذن الله لو كان له حق سيأخذه

728x90.png

#8

حياك اخي وجــدي ...

فهمت الآن قصدك تمام ... بس عندما اعمل start لـ Server (بعد ما اشغل الريجستري في نافذة منفصلة) يظهر لي الخطأ التالي:

post-20471-1222291305_thumb.jpg

لكن بعدها عملت (java Server) بدلاً من (start Server) و مشي الحال :

post-20471-1222290906_thumb.png

بس عندما اعمل الشغله كلها خارج الـ command line (يعني من خلال IDE مثل الـ JCreator) يصير معي التالي:

post-20471-1222291265_thumb.png

عمومــاً لا تشغل بالك ... راح احاول كمان عشان اضبط الامر معي .... :) ما قصرت ...

اقتباس
حقيقه لا أعلم ،،أصلا معلوماتي صفر في تطبيقات الويب .. لذلك يحول السؤال اليك ، وعليك الاجابه

الله يفتح عليك اخي الكريم :) ما ادري بالضبط .. عشان كذا احببت ان اقرأ موضوعك حتى افهم التقنيات التي سبقت الـ web services و من الطبيعي ان اقرأ انتقاد لها

لكن لا ننسى ان الـ RMI ما زالت مرتبطة بـ EJB و غيرها ... و هذا لا يفقدها شيء من اهميتها في مجالها ..

اتمنى فعلاً ان نحدد موضوع لنقاش هذه التقنيات بتسلسل يعمق فهمنا لها ... بحيث ننتج موضوع قيم يشبهك موضوعك هذا ...

جزاك الله خير ...

لا اله الا الله .. محمد رسول الله

(* ربِ اجعلني مقيم الصلاة و من ذريتي ، ربنا و تقبل دعـاء *)

يا حيّ يا قيـوم ... برحمتك استغيث ... اصلح لي شأني كله و لا تكلني الى نفسي طرفة عين

#9

حياكم الله ،،

أخوي علاء ، فعلا كتابه مواضيع طويله أمر مرهق ، ولكن الفائده النهائيه هو الأهم وهو الذي يجعلني أستمر في الكتابه ، بالأضافه الى محاوله النشاط ولو قليلا واللحاق بكم وجهودكم الواضحه في القسم ،أنت وبقيه الأعضاء المتفاعلين ..

لم أفهم قصدك من الأمر كاملا ،، لكن بشكل عام لو أحتجت أن أقوم بتحميل كلاسات من الكلاينت للسيرفر ، يجب وقت تشغيل السيرفر أن نحدد موقع الكلاسات codebase (ملف Stub) لكي يستخدمها هو والكلاينت الذي سوف يتصل به ..

عن طريق هذا الأمر ، مثلا أسم السيرفر كان Example :

java - Djava.rmi.server.codebase=http://MySite/file/ Example

MySite/file/ هي موقع ملفات الكلاس التى سوف يقوم العميل بالتحميل منها .. ويجب أن تنتهي بالعلامه Forword Slash .

اقتباس
يهيألي أنه في بعض الأحيان تتم تعديلات على الخدمات التي يقدمها الكائن البعيد

تتطلب التعديل على هيكلية الواجهة الواصلة بينه وبين السيرفر

لهذا يتم تحميل الموصل stub بدلاً من اللجوء في كل مرة إلى تغييره يدوياً

نعم صحيح ، أيضا لتخفيف العبئ على المستخدم وابعاده عن التفاصيل فقط كل ما عليه أستخدام الـ Interface لديه بدون الحاجه لتفاصيل أخرى ..

أختى بنت اليمن ،

الأمر start فقط يقوم بفتح نافذه جديده ... لذلك لو أردت أن أشغل السيرفر في نافذه جديده سأكتب :

start java Server

بنفس الطريقه العاديه لتشغيل أي ملف ، بس في البدايه نحط start لكي يعمل في نافذه جديده .. والمعذره على عدم توضيحه بشكل جيد ..

بالنسبه للتنفيذ في IDE المفترض أن لا نواجه أي مشاكل .. لأن الـ IDE في النهايه بيستخدم نفس JDK .. بس واجهه GUI تخفى التعقيد .. لذلك تأكدي من تسلسل الخطوات والتأكد من تشغيل Registry قبل تشغيل السيرفر وان شاء الله يضبط معاك ، والا نقوم بتجربته في JCreater لنرى أين المشكله ...

وان شاء الله نتوسع في النقاش في التقنيات الأخرى بمساعدتكم ...

http://informatic-ar.com منصة تعليمية عربية في علوم الحاسب والبرمجة

https://moalfat.com  للكتب الالكترونية والكورسات التعليمية

Everything we see now is just an engineering solution based on old science

#10
اقتباس
الأمر start فقط يقوم بفتح نافذه جديده ... لذلك لو أردت أن أشغل السيرفر في نافذه جديده سأكتب :

start java Server

مفهوم الآن استاذنا ... مشكور :)

و سأراجع التسلسل مع JCreator و راح شوف ...

جزاك الله خير ،،،

لا اله الا الله .. محمد رسول الله

(* ربِ اجعلني مقيم الصلاة و من ذريتي ، ربنا و تقبل دعـاء *)

يا حيّ يا قيـوم ... برحمتك استغيث ... اصلح لي شأني كله و لا تكلني الى نفسي طرفة عين

#11

أية جهود أخي وجدي

أظن أن ما أقوم به في شهر

تقوم به أنت في موضوع واحد من مواضيعك الشيقة

وقد وصل المعنى بالنسبة لي تاماً

كنت أريد هذا السطر

java - Djava.rmi.server.codebase=http://MySite/file/ Example

بقي علي الجزئية الخاصة بالأمان

سأطالعها لاحقاً بإذن الله

تحياتي

حزمة المحرك الإصدارة 0.8

أي أحد يجد أني ظلمته فليراسلني

وبإذن الله لو كان له حق سيأخذه

728x90.png

#12

الموضوع رائع أخ وجدي ونتمنى منك الاستمرار في طرح مثل هذه المواضيع

لانك والله أعلم ذو خبرة في مجال التطبيقات الشبكية في الجافا

وشكرا لك ..........

#13

السلام عليكم ورحمة الله وبركاته

مشكور على الدرس الاكثر من رائع اخي الكريم

لكن ارجوا اعادة توضيح الكود

او ارفاق ملف بي دي اف للدرس

بارك الله بك

#14

اعتقد ان التنسيق القديم غير متوافق مع نسخة المنتدى الجديدة..

على العموم الحمد لله انني قد كتبت الموضوع في منتدى أخر .. حيث اني لا أحتفظ بالأكواد الصغيرة في الغالب..

بالتوفيق..

http://informatic-ar.com منصة تعليمية عربية في علوم الحاسب والبرمجة

https://moalfat.com  للكتب الالكترونية والكورسات التعليمية

Everything we see now is just an engineering solution based on old science

مواضيع مشابهة