Skip to content

10. بناء تطبيقات CORBA الموزعة

10.1. مقدمة

في الفصل السابق، رأينا كيفية إنشاء تطبيقات موزعة في Java باستخدام حزمة RMI. هنا، نتناول نفس المشكلة، ولكن هذه المرة باستخدام بنية CORBA. CORBA (Common Object Request Broker Architecture) هي مواصفة حددتها OMG (Object Management Group)، التي تضم العديد من الشركات في صناعة تكنولوجيا المعلومات. تحدد CORBA "ناقل برمجي" يمكن الوصول إليه من قبل التطبيقات المكتوبة بلغات مختلفة:

Image

سنرى أن بناء تطبيق موزع باستخدام CORBA يشبه الطريقة المستخدمة مع Java RMI: فالمفاهيم متشابهة. توفر CORBA ميزة قابلية التشغيل البيني مع التطبيقات المكتوبة بلغات أخرى.

10.2. عملية تطوير تطبيق CORBA

10.2.1. مقدمة

لتطوير تطبيق خادم-عميل باستخدام CORBA، سنتبع الخطوات التالية:

  1. كتابة واجهة الخادم باستخدام IDL (لغة تعريف الواجهة)
  2. إنشاء فئات "الهيكل" و"الركيزة" للخادم
  3. كتابة الخادم
  4. كتابة العميل
  5. تجميع جميع الفئات
  6. تشغيل دليل خدمات CORBA
  7. تشغيل الخادم
  8. تشغيل العميل

سنستخدم خادم echo، الذي سبق استخدامه في سياق RMI، كمثال أول لنا. سيسمح هذا للقارئ برؤية الاختلافات بين الطريقتين.

تم اختبار التطبيق باستخدام JDK 1.2.

10.2.2. كتابة واجهة الخادم

كما هو الحال مع Java RMI، يتم تعريف الخادم من خلال واجهته فيما يتعلق بالعميل. في حين أن الفئات التي تنفذ الخادم ليست مطلوبة من قبل العميل، فإن فئات واجهته مطلوبة. في حين استخدمت Java RMI واجهة Java لإنشاء فئات "الهيكل" و"الركيزة" للخادم، تتطلب بنية Java CORBA أن توصف الواجهة بلغة أخرى غير Java. ستنشئ هذه الواجهة عدة فئات، يستخدم العميل بعضها ويستخدم الخادم البعض الآخر.

سيكون وصف واجهة echo كما يلي:

module echo{
    interface iSrvEcho{
        string echo(in string msg);
    };
};

سيتم تخزين وصف الواجهة في ملف echo.idl. وهو مكتوب بلغة IDL (لغة تعريف الواجهة) الخاصة بـ OMG. ولكي يكون قابلاً للاستخدام، يجب تحليله بواسطة برنامج يقوم بإنشاء ملفات مصدر باللغة المستخدمة لتطوير تطبيق CORBA. هنا، سنستخدم برنامج idltojava.exe، الذي سيقوم بإنشاء ملفات المصدر .java اللازمة للتطبيق استنادًا إلى الواجهة السابقة. لا يتم تضمين برنامج idltojava.exe مع JDK. ويمكن تنزيله من موقع Sun على الويب على http://java.sun.com.

دعونا نحلل بضع أسطر من واجهة IDL السابقة:

module echo
مكافئ **لحزمة** <bdi dir="ltr" class="odt-ltr-term">Java</bdi> **<bdi dir="ltr" class="odt-ltr-term">echo</bdi>**. سيؤدي ترجمة الواجهة إلى إنشاء حزمة <bdi dir="ltr" class="odt-ltr-term">Java echo</bdi>، أي دليل يحتوي على فئات <bdi dir="ltr" class="odt-ltr-term">Java.</bdi>
interface iSrvEcho
تعادل **واجهة** <bdi dir="ltr" class="odt-ltr-term">Java</bdi> **<bdi dir="ltr" class="odt-ltr-term">iSrvEcho</bdi>**. ستؤدي إلى إنشاء واجهة <bdi dir="ltr" class="odt-ltr-term">Java.</bdi>
string echo(in string msg)
تعادل عبارة <bdi dir="ltr" class="odt-ltr-term">Java</bdi> **<bdi dir="ltr" class="odt-ltr-term">String echo</bdi>(<bdi dir="ltr" class="odt-ltr-term">String msg</bdi>)**. لا تتطابق الأنواع في لغة <bdi dir="ltr" class="odt-ltr-term">IDL</bdi> تمامًا مع تلك الموجودة في لغة <bdi dir="ltr" class="odt-ltr-term">Java.</bdi> سيتم شرح أوجه التطابق لاحقًا في هذا الفصل. في لغة <bdi dir="ltr" class="odt-ltr-term">IDL</bdi>، يمكن أن تكون معلمات الدالة معلمات إدخال (**in**) أو إخراج (**out**) أو إدخال-إخراج (**<bdi dir="ltr" class="odt-ltr-term">inout</bdi>**). هنا، تستقبل طريقة *<bdi dir="ltr" class="odt-ltr-term">echo</bdi>* معلمة إدخال *msg،* وهي سلسلة، وتُرجع سلسلة كنتيجة.

الواجهة السابقة هي واجهة خادم echo الخاص بنا. تذكر أن الواجهة البعيدة تصف طرق كائن الخادم التي يمكن للعملاء الوصول إليها. هنا، ستكون طريقة echo فقط متاحة للعملاء.

10.2.3. ترجمة واجهة IDL للخادم

بمجرد تعريف واجهة الخادم، نقوم بإنشاء ملفات Java المقابلة.

E:\data\java\corba\ECHO>dir *.idl

ECHO     IDL            78  15/03/99  13:56 ECHO.IDL

E:\data\java\corba\ECHO>d:\javaidl\idltojava.exe -fno-cpp echo.idl

يُستخدم الخيار -fno-cpp للإشارة إلى عدم استخدام أي معالج مسبق (يُستخدم بشكل شائع مع C/C++). يؤدي ترجمة ملف echo.idl إلى إنشاء دليل فرعي echo يحتوي على الملفات التالية:

E:\data\java\corba\ECHO>dir echo

_ISRVE~1 JAV         1 095  17/03/99  17:19 _iSrvEchoStub.java
ISRVEC~1 JAV           311  17/03/99  17:19 iSrvEcho.java
ISRVEC~2 JAV           825  17/03/99  17:19 iSrvEchoHolder.java
ISRVEC~3 JAV         1 827  17/03/99  17:19 iSrvEchoHelper.java
_ISRVE~2 JAV         1 803  17/03/99  17:19 _iSrvEchoImplBase.java

ملف iSrvEcho.java هو ملف Java الذي يصف واجهة الخادم:

/*
 * File: ./ECHO/ISRVECHO.JAVA
 * From: ECHO.IDL
 * Date: Mon Mar 15 13:56:08 1999
 *   By: D:\JAVAIDL\IDLTOJ~1.EXE Java IDL 1.2 Aug 18 1998 16:25:34
 */

package echo;
public interface iSrvEcho
    extends org.omg.CORBA.Object, org.omg.CORBA.portable.IDLEntity {
    String echo(String msg)
;
}

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

iSrvEcho.java

واجهة الخادم

_iSrvEchoImplbase.java

تنفذ واجهة iSrvEcho السابقة. وهي فئة مجردة، "هيكل" الخادم، تزود الخادم بوظائف CORBA التي يتطلبها التطبيق الموزع.

_iSrvEchoStub.java

هذا هو "البديل" للخادم الذي سيستخدمه العميل. ويوفر للعميل وظائف CORBA اللازمة للوصول إلى الخادم.

iSrvEchoHelper.java

يوفر الطرق اللازمة لإدارة مراجع كائنات CORBA

iSrvEchoHolder.java

يوفر الطرق اللازمة لإدارة معلمات الإدخال والإخراج لطرق الواجهة.

10.2.4. ترجمة الفئات التي تم إنشاؤها من واجهة IDL

من المستحسن ترجمة الفئات السابقة. سنرى في مثال آخر أنه يمكن هنا اكتشاف الأخطاء الناتجة عن التشغيل غير الصحيح لمولد idltojava. هنا، يسير كل شيء بسلاسة، وبعد الترجمة، توجد الملفات التالية في دليل حزمة echo:

E:\data\java\corba\ECHO\echo>dir

_ISRVE~1 JAV         1 095  17/03/99  17:19 _iSrvEchoStub.java
ISRVEC~1 JAV           311  17/03/99  17:19 iSrvEcho.java
ISRVEC~2 JAV           825  17/03/99  17:19 iSrvEchoHolder.java
ISRVEC~3 JAV         1 827  17/03/99  17:19 iSrvEchoHelper.java
_ISRVE~2 JAV         1 803  17/03/99  17:19 _iSrvEchoImplBase.java
_ISRVE~1 CLA         2 275  18/03/99  11:25 _iSrvEchoImplBase.class
_ISRVE~2 CLA         1 383  18/03/99  11:25 _iSrvEchoStub.class
ISRVEC~1 CLA           251  18/03/99  11:25 iSrvEcho.class
ISRVEC~2 CLA         2 078  18/03/99  11:25 iSrvEchoHelper.class
ISRVEC~3 CLA           858  18/03/99  11:25 iSrvEchoHolder.class

10.2.5. تنفيذ الخادم

10.2.5.1. تنفيذ واجهة iSrvEcho

لقد قمنا بتعريف واجهة iSrvEcho سابقًا. سنقوم الآن بكتابة الفئة التي تنفذ هذه الواجهة. وستكون مشتقة من فئة _iSrvEchoImplbase.java، والتي، كما هو مذكور أعلاه، تنفذ بالفعل واجهة iSrvEcho.

// imported packages
import echo.*;

// class implementing remote echo
public class srvEcho extends _iSrvEchoImplBase{
    // method performing the echo
    public String echo(String msg){
        return  "["  + msg + "]";
    }// fine echo
}// end of class

الرمز واضح بذاته. يتم حفظ هذه الفئة في الملف srvEcho.java في الدليل الأصلي لحزمة واجهة iSrvEcho.

يمكنك ترجمته للتحقق:

E:\data\java\corba\ECHO>j:\jdk12\bin\javac srvEcho.java

E:\data\java\corba\ECHO>dir

ECHO     IDL            78  15/03/99  13:56 ECHO.IDL
SRVECH~1 CLA           488  18/03/99  11:30 srvEcho.class
SRVECH~1 JAV           252  15/03/99  14:02 srvEcho.java
ECHO           <REP>        17/03/99  17:19 echo

10.2.5.2. كتابة فئة إنشاء الخادم

كما هو الحال مع تطبيق RMI للعميل والخادم، يجب تسجيل خادم CORBA في دليل حتى يتمكن العملاء من الوصول إليه. إن إجراء التسجيل هذا هو الذي يختلف، على مستوى التطوير، اعتمادًا على ما إذا كان لديك تطبيق CORBA أو RMI. فيما يلي الإجراء الخاص بخادم echo CORBA المسجل في ملف serverEcho.java:

// imported packages
 import echo.*
; import org.omg.CosNaming.
*; import org.omg.CosNaming.NamingContextPackage
.*; import org.omg.CORB

A.*; //----------- class serveu
rEcho public class serveu
    rEcho{ // ------- main: launches the ech
    o server // syntax pg machineAnnuaire portAnnuaire n
    omService // machine: machine supporting CORBA 
    directory // port: directory port
     CORBA // nomService: name of the service t

o  be registered public static void 
    main(String arg[]){ // are th
    e arguments there?
         if(arg.length!=3){ System.err.println("Syntaxe : pg machineAnnuaire por
        tAnnuaire nomSe
    r
    vice"); System.exit(1); }
     // retrieve the argu
    ments String machi
    ne=arg[0]; String port=arg[1]; String nomService=arg[2]; 

    try{

    // you need a CORBA object to work
    String[] initORB={"-ORBInitialHost",machine,"-ORBInitialPort",port};
      ORB orb=ORB.init(initORB,null);
    // put the service in the service directory
    // it will be called srvEcho
      org.omg.CORBA.Object objRef=
        orb.resolve_initial_references("NameService");
      NamingContext ncRef=NamingContextHelper.narrow(objRef);
      NameComponent nc= new NameComponent(nomService,"");
      NameComponent path[]={nc};
    // create the server and associate it with the srvEcho service
    srvEcho serveurEcho=new srvEcho();
      ncRef.rebind(path,serveurEcho);
    orb.connect(serveurEcho);
    // follow-up
    System.out.println("Serveur d'écho prêt");
    // waiting for customer requests
      java.lang.Object sync=new java.lang.Object();
      synchronized(sync){
        sync.wait();
      }
    } catch(Exception e){
    // there has been an error
      System.err.println("Erreur " + e);
      e.printStackTrace(System.err);
    }
  }// hand
}// serveurEcho

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

10.2.5.2.1. معلمات الخادم

يجب أن يسجل خادم CORBA نفسه لدى خدمة دليل تعمل على جهاز ومنفذ معينين. سيتلقى تطبيقنا هاتين المعلومتين كمعلمات. يجب أن يكون للخدمة المسجلة اسم، والذي سيكون المعلمة الثالثة.

10.2.5.2.2. إنشاء كائن الوصول إلى خدمة الدليل CORBA

للوصول إلى خدمة الدليل وتسجيل خادم echo الخاص بنا، نحتاج إلى كائن يسمى ORB (Object Request Broker)، يتم الحصول عليه باستخدام طريقة الفئة التالية:

ORB ORB.init(String [] args, Properties prop)


Args :     tableau de paires de chaînes de caractères, chaque paire étant de la forme (paramètre,valeur)


Prop :     propriétés de l’application

يستخدم المثال التسلسل التالي للحصول على كائن ORB:


    String[] initORB={"-ORBInitialHost",machine,"-ORBInitialPort",port};
       ORB orb=ORB.init(initORB,null);

أزواج (المعلمة، القيمة) المستخدمة هي كما يلي:


("-ORBInitialHost",machine) : ce couple précise la machine ou opère l’annuaire des services CORBA, ici la machine passée en paramètre au serveur.


("-ORBInitialPort",port ) : ce couple précise le port ou opère l’annuaire des services CORBA, ici le port passé en paramètre au serveur.

تم تعيين المعلمة الثانية لطريقة init على القيمة null. ولو تم تعيين المعلمة الأولى أيضًا على القيمة null، لكان الزوج (machine,port) المستخدم هو الزوج الافتراضي (localhost,900).

10.2.5.2.3. تسجيل الخادم في دليل خدمات CORBA

يتم تسجيل الخادم في الدليل من خلال العمليات التالية:

     // put the service in the service directory // 
    it will be called srvEcho
       org.omg.CORBA.Object o
b       jRef= orb.resolve_initial_references("
N     ameService"); NamingContext ncRef=NamingContextHe
l     per.narrow(objRef); NameComponent nc= new Nam
e     Component(nomService,""); 
     NameComponent path[]={nc}; // create the server a
    nd associate it with the srvEcho s
e         rvice srvEcho serveurEcho=new 
            srvEcho(); ncRef.rebind(path,serveurEcho); orb.connect(serveurEcho);

يتضمن الجزء الأول من الكود إعداد اسم الخدمة. يتم تمثيل هذا الاسم في الكود بواسطة المتغير path. يتكون اسم الخدمة من عدة مكونات:

  • مكون أولي objRef، وهو كائن عام يجب تحويله إلى نوع NamingContext، وهو هنا ncRef.
  • اسم الخدمة، هنا `serviceName`، الذي تم تمريره كمعلمة إلى الخادم

يتم تجميع مكونات الاسم هذه (NameComponent) في مصفوفة، هنا path. وهذه المصفوفة هي التي "تسمي" الخدمة التي تم إنشاؤها بدقة. بمجرد إنشاء الاسم، فإنه يبقى

  • ربطه بمثيل للخادم (فئة srvEcho التي تم إنشاؤها سابقًا)


srvEcho serveurEcho=new srvEcho();
  • وتسجيلها في الدليل

    ncRef.rebind(path,serveurEcho);
    orb.connect(serveurEcho);

10.2.5.3. تجميع فئة تشغيل الخادم

ترجمة الفئة السابقة:

E:\data\java\corba\ECHO>j:\jdk12\bin\javac serveurEcho.java

E:\data\java\corba\ECHO>dir

ECHO     IDL            78  15/03/99  13:56 ECHO.IDL
SERVEU~1 CLA         1 793  18/03/99  13:18 serveurEcho.class
SERVEU~1 JAV         1 806  16/03/99  15:38 serveurEcho.java
SRVECH~1 CLA           488  18/03/99  11:30 srvEcho.class
SRVECH~1 JAV           252  15/03/99  14:02 srvEcho.java
ECHO           <REP>        17/03/99  17:19 echo

10.2.6. كتابة العميل

10.2.6.1. الرمز

نقوم بكتابة عميل لاختبار خدمة echo الخاصة بنا. سنمرر إلى العميل نفس المعلمات الثلاثة التي مررناها إلى الخادم:

الجهاز: الجهاز الذي يوجد عليه دليل خدمة CORBA

المنفذ: المنفذ الذي يعمل عليه هذا الدليل

serviceName: اسم خدمة الصدى

يتصل العميل بخدمة echo ثم يطلب من المستخدم كتابة الرسائل على لوحة المفاتيح. يتم إرسال هذه الرسائل إلى خادم echo، الذي يعيد إرسالها. يتم عرض سجل لهذا الحوار على الشاشة.

يشبه عميل CORBA لخدمة echo إلى حد كبير عميل RMI الذي تمت كتابته مسبقًا. هنا أيضًا، يجب على العميل الاتصال بخدمة الدليل للحصول على مرجع إلى كائن الخادم الذي يرغب في الاتصال به. يكمن الاختلاف بين العميلين في هذا الأمر وحده. فيما يلي كود عميل CORBA echo:


    

10.2.6.2. توصيل العميل بالخادم

يتصل عميل CORBA أعلاه بالخادم باستخدام العبارة التالية:

        // on fait la liaison avec le serveur d'écho
        iSrvEcho serveurEcho=getServeurEcho(machine,port,nomService);

بعد هذه العملية، يحتفظ العميل بمرجع إلى خادم echo. ومن هذه النقطة فصاعدًا، لا يختلف عميل CORBA عن عميل RMI. الطريقة الخاصة التي تنشئ الاتصال بالخادم هي كما يلي:

// imported packages
import java.io.*;
import echo.*;
import org.omg.CosNaming.*;
import org.omg.CORBA.*;

// ---------- class cltEcho
public class cltEcho {

    public static void main(String arg[]){
         // syntax : cltEcho machineAnnuaire portAnnuaire nameservice
         // machine: machine where the CORBA service directory operates
         // port: port where the service directory operates
         // nomService: echo service name

         // argument verification
        if(arg.length!=3){
            System.err.println("Syntaxe : pg machineAnnuaire portAnnuaire nomservice");
            System.exit(1);
        }

         // parameters are retrieved
        String machine=arg[0];
        String port=arg[1];
        String nomService=arg[2];

         // link to echo server
        iSrvEcho serveurEcho=getServeurEcho(machine,port,nomService);

         // client-server dialogue
        BufferedReader in=null;
        String msg=null;
        String reponse=null;
        iSrvEcho serveur=null;

        try{
             // open keyboard flow
            in=new BufferedReader(new InputStreamReader(System.in));
             // loop for reading msg to be sent to echo server
            System.out.print("Message : ");
            msg=in.readLine().toLowerCase().trim();
            while(! msg.equals("fin")){
                 // send msg to server and receive response
                reponse=serveurEcho.echo(msg);
                 // follow-up
                System.out.println("Réponse serveur : " + reponse);
                 // next msg
                System.out.print("Message : ");                
                msg=in.readLine().toLowerCase().trim();
            }// while
             // it's over
            System.exit(0);
         // error management         
        } catch (Exception e){
            System.err.println("Erreur : " + e);
            System.exit(2);
        }// try
    }// hand

     // ---------------------- getServeurEcho
    private static iSrvEcho getServeurEcho(String machine, String port, 
            String nomService){

         // requests an echo server reference
         // follow-up
        System.out.println("--> Connexion au serveur CORBA en cours...");
         // echo server reference
        iSrvEcho serveurEcho=null;
        try{
             // we request a CORBA object to work with
            String[] initORB={"-ORBInitialHost",machine,"-ORBInitialPort",port};    
          ORB orb=ORB.init(initORB,null);
             // use the directory service to locate the echo server
          org.omg.CORBA.Object objRef=
                orb.resolve_initial_references("NameService");
          NamingContext ncRef=NamingContextHelper.narrow(objRef);
             // the service required is called srvEcho - it is requested
          NameComponent nc= new NameComponent(nomService,"");
          NameComponent path[]={nc};
          serveurEcho=iSrvEchoHelper.narrow(ncRef.resolve(path));
        } catch (Exception e){
            System.err.println("Erreur lors de la localisation du serveur d'écho ("
                + e + ")");
            System.exit(10);
        }// try-catch
         // return the reference to the server
        return serveurEcho;
    }// getServeurEcho

}// class

نرى نفس تسلسلات الكود الموجودة في الخادم:

  • نقوم بإنشاء كائن ORB الذي سيسمح لنا بالاتصال بدليل خدمات CORBA

String[] initORB={"-ORBInitialHost",machine,"-ORBInitialPort",port};    
      ORB orb=ORB.init(initORB,null);

  • نحدد المكونات المختلفة لاسم خدمة echo

        org.omg.CORBA.Object objRef=orb.resolve_initial_references("NameService");
      NamingContext ncRef=NamingContextHelper.narrow(objRef);
      NameComponent nc= new NameComponent(nomService,"");
      NameComponent path[]={nc};
  • نطلب مرجعًا لخدمة echo من خدمة الدليل (وهذا هو ما يميزنا عن الخادم)

serveurEcho=iSrvEchoHelper.narrow(ncRef.resolve(path));

10.2.6.3. التجميع

E:\data\java\corba\ECHO>j:\jdk12\bin\javac cltEcho.java

E:\data\java\corba\ECHO>dir

CLTECH~1 CLA         2 599  18/03/99  13:51 cltEcho.class
CLTECH~1 JAV         2 907  16/03/99  16:15 cltEcho.java
ECHO     IDL            78  15/03/99  13:56 ECHO.IDL
SERVEU~1 CLA         1 793  18/03/99  13:18 serveurEcho.class
SERVEU~1 JAV         1 806  16/03/99  15:38 serveurEcho.java
SRVECH~1 CLA           488  18/03/99  11:30 srvEcho.class
SRVECH~1 JAV           252  15/03/99  14:02 srvEcho.java
ECHO           <REP>        17/03/99  17:19 echo

10.2.7. الاختبارات

10.2.7.1. بدء خدمة الدليل

على جهاز يعمل بنظام Windows، نبدأ خدمة الدليل على النحو التالي:

E:\data\java\corba\ECHO>start j:\jdk12\bin\tnameserv -ORBInitialPort 1000

وهذا يبدأ خدمة الدليل على المنفذ 1000 للجهاز.

تعرض خدمة الدليل tnameserv شاشة تبدو كما يلي:

Initial Naming Context:
IOR:000000000000002849444c3a6f6d672e6f72672f436f734e616d696e672f4e616d696e67436f
6e746578743a312e3000000000010000000000000030000100000000000a69737469612d30303900
044700000018afabcafe000000027620dd9a000000080000000000000000
TransientNameServer: setting port for initial object references to: 1000

من الصعب قراءة النص، لكن لاحظ السطر الأخير: الخدمة نشطة على المنفذ 1000.

10.2.7.2. تشغيل خادم echo

يتم تشغيل خدمة echo بثلاثة معلمات:


E:\data\java\corba\ECHO>start j:\jdk12\bin\java serveurEcho localhost 1000 srvEcho

يعرض الخادم:

    Serveur d’écho prêt

10.2.7.3. تشغيل العميل على نفس الجهاز الذي يعمل عليه الخادم


E:\data\java\corba\ECHO>j:\jdk12\bin\java cltEcho localhost 1000 srvEcho
--> Connexion au serveur CORBA en cours...
Message : msg1
Réponse serveur : [msg1]
Message : msg2
Réponse serveur : [msg2]
Message : fin

10.2.7.4. تشغيل العميل على جهاز يعمل بنظام Windows بخلاف الخادم

E:\data\java\corba\ECHO>j:\jdk12\bin\java cltEcho tahe.istia.univ-angers.fr 1000 srvEcho
--> Connexion au serveur CORBA en cours...
Message : abcd
Réponse serveur : [abcd]
Message : efgh
Réponse serveur : [efgh]
Message : fin

10.3. مثال 2: خادم SQL

10.3.1. مقدمة

هنا نعيد النظر في كود خادم SQL الذي تم فحصه سابقًا في سياق Java RMI، مرة أخرى لتسليط الضوء على أوجه التشابه والاختلاف بين النهجين. للتذكير، دور خادم SQL هذا هو كما يلي: يعمل على جهاز يعمل بنظام Windows ويسمح للعملاء عن بُعد بالوصول إلى قواعد بيانات ODBC العامة الموجودة على جهاز Windows هذا.

Image

يمكن لعميل CORBA تنفيذ ثلاث عمليات:

  • الاتصال بقاعدة البيانات التي يختارها
  • إرسال استعلامات SQL
  • إغلاق الاتصال

يقوم الخادم بتنفيذ استعلامات SQL الخاصة بالعميل وإرسال النتائج مرة أخرى إلى العميل. هذه هي وظيفته الأساسية، ولهذا السبب نسميه خادم SQL. نطبق الخطوات المختلفة التي تمت تغطيتها سابقًا مع خادم echo.

10.3.2. كتابة واجهة IDL للخادم

للتذكير، إليك واجهة RMI التي استخدمناها للخادم:

import java.rmi.*;

 // remote interface p
ublic interface interSQL extends Remote{ 
     public String connect(String pilote, String url, String id, String mdp
        ) throws java.rmi.RemoteExcept
    ion; public String[] executeSQL(String requete, String separa
        teur) throws java.rmi.RemoteE
    xception; public Str
        ing close() throws java.rmi.Re
moteException; }

وكانت أدوار الطرق المختلفة كما يلي:

Connect: يتصل العميل بقاعدة بيانات بعيدة، موفراً برنامج التشغيل وعنوان URL الخاص بـ JDBC، بالإضافة إلى معرفه وكلمة مروره للوصول إلى قاعدة البيانات. يعرض الخادم سلسلة تشير إلى نتيجة الاتصال:

    200 - Connexion réussie
    500 - Echec de la connexion

executeSQL: يطلب العميل تنفيذ استعلام SQL على قاعدة البيانات التي يتصل بها. ويحدد الحرف الذي يجب أن يفصل بين الحقول في النتائج التي يتم إرجاعها إليه. يعرض الخادم مصفوفة من السلاسل:

    100 n
لاستعلام تحديث قاعدة البيانات، حيث n هو عدد الصفوف التي تم تحديثها
    500 msg d’erreur
    إذا تسبب الاستعلام في حدوث خطأ
    501 Pas de résultats
    إذا لم يُرجع الاستعلام أي نتائج
    101 ligne1
    101 ligne2
    101 ...
إذا أعاد الاستعلام نتائج. الصفوف التي يعيدها الخادم هي صفوف نتائج الاستعلام.

إغلاق: يقوم العميل بإغلاق اتصاله بقاعدة البيانات البعيدة. يعرض الخادم سلسلة تشير إلى نتيجة هذا الإغلاق:

    200 Base fermée
    500 Erreur lors de la fermeture de la base (msg d’erreur)

ستكون واجهة IDL للخادم كما يلي:

module srvSQL{

    typedef sequence<string> resultats;

    interface interSQL{
        string connect(in string pilote, in string urlBase, in string id, in string mdp);
        resultats executeSQL(in string requete, in string separateur);
        string close();
    };// interface
};// module

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

  • تحديد نوع لتعيين المصفوفة، وهنا النتائج:
typedef sequence<string> resultats;

كلمة typedef معروفة جيدًا لمبرمجي C/C++: فهي تسمح لك بتعريف نوع جديد. هنا، يتم تعريف النوع resultats على أنه مكافئ للنوع sequence<string>، أي مصفوفة ديناميكية (غير محددة الحجم) من سلاسل الأحرف.

  • استخدام النوع الجديد عند الحاجة
resultats executeSQL(in string requete, in string separateur);

وبالتالي، تُرجع طريقة executeSQL مصفوفة من السلاسل.

10.3.3. تجميع واجهة IDL للخادم

يتم وضع واجهة IDL السابقة في الملف srvSQL.idl. نقوم بترجمة هذا الملف:

E:\data\java\corba\sql>d:\javaidl\idltojava -fno-cpp srvSQL.idl

E:\data\java\corba\sql>dir

SRVSQL   IDL           275  19/03/99   9:59 srvSQL.idl
SRVSQL         <REP>        19/03/99   9:41 srvSQL

يمكننا أن نرى أن عملية التجميع أنشأت مجلدًا يحمل اسم وحدة واجهة IDL (srvSQL). دعونا نلقي نظرة على محتويات هذا المجلد:

E:\data\java\corba\sql>dir srvSQl

RESULT~1 JAV           833  19/03/99  10:00 resultatsHolder.java
RESULT~2 JAV         1 883  19/03/99  10:00 resultatsHelper.java
_INTER~1 JAV         2 474  19/03/99  10:00 _interSQLStub.java
INTERS~1 JAV           448  19/03/99  10:00 interSQL.java
INTERS~2 JAV           841  19/03/99  10:00 interSQLHolder.java
INTERS~3 JAV         1 855  19/03/99  10:00 interSQLHelper.java
_INTER~2 JAV         4 535  19/03/99  10:00 _interSQLImplBase.java

يرجى ملاحظة أن ملفات Helper و Holder هي فئات مرتبطة بمعلمات الإدخال/الإخراج ونتائج أساليب الواجهة البعيدة. يحتوي الدليل srvSQL على جميع ملفات .java المتعلقة بواجهة interSQL المُعرَّفة في ملف .idl. كما يحتوي على الملفات المتعلقة بنوع النتائج الذي تم إنشاؤه في واجهة IDL.

ملف interSQL.java هو ملف Java الخاص بواجهة الخادم الخاص بنا. من المهم التحقق من أن ما تم إنشاؤه تلقائيًا يتطابق مع توقعاتنا. ملف interSQL.java الذي تم إنشاؤه هو كما يلي:

/*
 * File: ./SRVSQL/INTERSQL.JAVA
 * From: SRVSQL.IDL
 * Date: Fri Mar 19 09:59:48 1999
 *   By: D:\JAVAIDL\IDLTOJ~1.EXE Java IDL 1.2 Aug 18 1998 16:25:34
 */

package srvSQL;
public interface interSQL
    extends org.omg.CORBA.Object, org.omg.CORBA.portable.IDLEntity {
    String connect(String pilote, String urlBase, String id, String mdp)
;
    String[] executeSQL(String requete, String separateur)
;
    String close()
;
}

يمكننا أن نرى أن لدينا نفس الواجهة المستخدمة في خادم-عميل RMI. لذا يمكننا المتابعة. دعونا نقوم بتجميع كل ملفات .java هذه:

E:\data\java\corba\sql\srvSQL>j:\jdk12\bin\javac *.java

Note: _interSQLImplBase.java uses or overrides a deprecated API.  Recompile with
 "-deprecation" for details.
1 warning

E:\data\java\corba\sql\srvSQL>dir *.class

_INTER~1 CLA         3 094  19/03/99  10:01 _interSQLImplBase.class
_INTER~2 CLA         1 953  19/03/99  10:01 _interSQLStub.class
INTERS~1 CLA           430  19/03/99  10:01 interSQL.class
INTERS~2 CLA         2 096  19/03/99  10:01 interSQLHelper.class
INTERS~3 CLA           870  19/03/99  10:01 interSQLHolder.class
RESULT~1 CLA         2 047  19/03/99  10:01 resultatsHelper.class
RESULT~2 CLA           881  19/03/99  10:01 resultatsHolder.class

10.3.4. كتابة SQL Server

سنقوم الآن بكتابة كود خادم SQL. تذكر أن هذه الفئة يجب أن تنحدر من الفئة المجردة _nomInterfaceImplBase التي تم إنشاؤها عن طريق ترجمة ملف IDL. وبصرف النظر عن هذه الميزة الخاصة، وباستثناء تسلسلات الكود المتعلقة بتسجيل الخدمة في الدليل، فإن كود خادم CORBA مطابق تمامًا لكود خادم RMI:

// imported packages
import java.sql.*;
import java.util.*;
import srvSQL.*;

// class SQLServant
public class SQLServant extends _interSQLImplBase{

    // global class data
    private Connection DB;

    // --------------- connect
    public String connect(String pilote, String url, String id,
        String mdp){

        // connection to url database via driver
        // identification with identity id and password mdp

        String resultat=null;            // result of the method
        try{
            // loading the driver
            Class.forName(pilote);
            // connection request
            DB=DriverManager.getConnection(url,id,mdp);
            // ok
            resultat="200 Connexion réussie";
        } catch (Exception e){
            // error
            resultat="500 Echec de la connexion (" + e + ")";
        }
        // end
        return resultat;
    }            

    // ------------- executeSQL
    public String[] executeSQL(String requete, String separateur){

        // executes a SQL query on the DB database
        // and puts the results in an array of strings

        // data required to execute the request
        Statement S=null;
        ResultSet RS=null;
        String[] lignes=null;
        Vector resultats=new Vector();
        String ligne=null;

        try{
            // create query container
            S=DB.createStatement();
            // request execution
            if (! S.execute(requete)){
                // update request
                // returns the number of lines updated
                lignes=new String[1];
                lignes[0]="100 "+S.getUpdateCount();
                return lignes;
            }
            // it was a query request
            // retrieve results
            RS=S.getResultSet();
            // number of Resultset fields
            int nbChamps=RS.getMetaData().getColumnCount();
            // we exploit them
            while(RS.next()){
                // create results line
                ligne="101 ";
                for (int i=1;i<nbChamps;i++)
                    ligne+=RS.getString(i)+separateur;
                ligne+=RS.getString(nbChamps);
                // add to results vector
                resultats.addElement(ligne);
            }// while
            // end of results processing
            // free up resources
            RS.close();
            S.close();
            // we return the results
            int nbLignes=resultats.size();
            if (nbLignes==0){
                lignes=new String[1];
                lignes[0]="501 Pas de résultats";
            } else {
                lignes=new String[resultats.size()];
                for(int i=0;i<lignes.length;i++)
                    lignes[i]=(String) resultats.elementAt(i);
            }//if
            return lignes;
        } catch (Exception e){
            // error
            lignes=new String[1];
            lignes[0]="500 " + e;
            return lignes;
        }// try-catch
    }// executeSQL

    // --------------- close
    public String close(){
        // closes database connection
        String resultat=null;
        try{
            DB.close();
            resultat="200 Base fermée";
        } catch (Exception e){
            resultat="500 Erreur à la fermeture de la base ("+e+")";
        }
        // return result
        return resultat;
    }
}// class SQLServant

توجد هذه الفئة في ملف SQLServant.java الذي نقوم بتركيبه:

E:\data\java\corba\sql>dir

SRVSQL   IDL           275  19/03/99   9:59 srvSQL.idl
SRVSQL         <REP>        19/03/99   9:41 srvSQL
SQLSER~1 JAV         2 941  15/03/99   9:09 SQLServant.java

E:\data\java\corba\sql>j:\jdk12\bin\javac SQLServant.java

E:\data\java\corba\sql>dir *.class

SQLSER~1 CLA         2 568  19/03/99  10:19 SQLServant.class

10.3.5. كتابة برنامج تشغيل SQL Server

تمثل الفئة السابقة خادم SQL بمجرد تشغيله. قبل ذلك، يجب تسجيله في دليل خدمات CORBA. كما هو الحال مع خدمة echo، سنقوم بذلك باستخدام فئة خاصة سنمرر إليها ثلاثة معلمات في وقت التشغيل:

الجهاز: الجهاز الذي يوجد عليه دليل خدمة CORBA

المنفذ: المنفذ الذي يعمل عليه هذا الدليل

serviceName: اسم خدمة SQL

كود هذه الفئة مطابق تقريبًا لكود الفئة التي قامت بنفس المهمة لخدمة echo. وقد قمنا بتمييز السطر الذي يختلف بين الفئتين بالخط العريض: فهو لا ينشئ نفس كائن الخادم. وبالتالي، يمكننا أن نرى أن آلية تشغيل الخادم تظل كما هي. وإذا قمنا بعزل هذه الآلية في فئة، كما تم هنا، فإنها تصبح شبه شفافة بالنسبة للمطور.

// imported packages
import srvSQL.*;
import org.omg.CosNaming.*;
import org.omg.CosNaming.NamingContextPackage.*;
import org.omg.CORBA.*;

//----------- class serveurSQL
public class serveurSQL{
    // ------- main: launches the SQL server
  public static void main(String arg[]){
        // serveurSQL machine port service

        //do we have the right number of arguments
        if(arg.length!=3){
            System.err.println("Syntaxe : pg machineAnnuaireCorba portAnnuaireCorba nomService");
            System.exit(1);
        }
        // retrieve the arguments
        String machine=arg[0];
        String port=arg[1];
        String nomService=arg[2];
        String[] initORB={"-ORBInitialHost",machine,"-ORBInitialPort",port};
    try{
            // you need a CORBA object to work
      ORB orb=ORB.init(initORB,null);
            // put the service in the service directory
      org.omg.CORBA.Object objRef=
        orb.resolve_initial_references("NameService");
      NamingContext ncRef=NamingContextHelper.narrow(objRef);
      NameComponent nc= new NameComponent(nomService,"");
      NameComponent path[]={nc};
        // create the server and associate it with the srvSQL service
        SQLServant serveurSQL=new SQLServant();
      ncRef.rebind(path,serveurSQL);
        orb.connect(serveurSQL);
        // follow-up
        System.out.println("Serveur SQL prêt");
        // waiting for customer requests
      java.lang.Object sync=new java.lang.Object();
      synchronized(sync){
        sync.wait();
      }
    } catch(Exception e){
            // there has been an error
      System.err.println("Erreur " + e);
      e.printStackTrace(System.err);
    }
  }// hand
}// srvSQL

نقوم بترجمة هذه الفئة الجديدة:

E:\data\java\corba\sql>j:\jdk12\bin\javac serveurSQL.java

E:\data\java\corba\sql>dir *.class

SQLSER~1 CLA         2 568  19/03/99  10:19 SQLServant.class
SERVEU~1 CLA         1 800  19/03/99  10:33 serveurSQL.class

10.3.6. رمز العميل

يتم استدعاء عميل خادم CORBA بالمعلمات التالية:

    machine port nomServiceAnnuaire pilote urlBase id mdp separateur

الجهاز: الجهاز الذي يوجد عليه دليل خدمة CORBA

المنفذ: المنفذ الذي يعمل عليه هذا الدليل

serviceName: اسم خدمة SQL

driver: برنامج التشغيل الذي يجب أن يستخدمه خادم SQL لإدارة قاعدة البيانات المطلوبة

baseUrl: عنوان URL JDBC لقاعدة البيانات المراد إدارتها

id: معرف العميل أو null في حالة عدم وجود معرف

password: كلمة مرور العميل أو null في حالة عدم وجود كلمة مرور

separator: الحرف الذي يجب أن يستخدمه خادم SQL لفصل الحقول في صفوف نتائج الاستعلام

فيما يلي مثال على المعلمات المحتملة:

    localhost 1000 srvSQL sun.jdbc.odbc.JdbcOdbcDriver jdbc:odbc:articles null null ,

حيث:

الجهاز: الجهاز الذي يوجد عليه دليل خدمة CORBA

المنفذ: المنفذ الذي يعمل عليه هذا الدليل

srvSQL: srvSQL، اسم CORBA لخادم SQL

driver: sun.jdbc.odbc.JdbcOdbcDriver، برنامج التشغيل القياسي لقواعد البيانات المزودة بواجهة ODBC

urlBase: jdbc:odbc:articles، لاستخدام قاعدة بيانات articles المعلنة في قائمة قواعد بيانات ODBC العامة على جهاز Windows

id: null، لا يوجد معرف

password: null، لا توجد كلمة مرور

الفاصل: ، سيتم فصل حقول النتائج بفاصلة

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

  • يتصل بالجهاز machine على المنفذ port لطلب خدمة CORBA srvSQL
  • يطلبون اتصالاً بقاعدة بيانات المقالات
connect(‘’sun.jdbc.odbc.JdbcOdbcDriver’’, ‘‘jdbc:odbc:articles’’, ’’’’, ’’’’)
  • يطلبون من المستخدم كتابة استعلام SQL على لوحة المفاتيح
  • يرسله إلى خادم SQL
executeSQL(requete, ’’,’’);
  • يعرض النتائج التي أرجعها الخادم على الشاشة
  • يطلب من المستخدم مرة أخرى كتابة استعلام SQL على لوحة المفاتيح. سيتوقف عند انتهاء الاستعلام.

فيما يلي كود عميل جافا. ومن المفترض أن تكون التعليقات كافية لفهمه. يرجى ملاحظة ما يلي:

  • الرمز مطابق لرمز عميل RMI الذي تمت دراسته سابقًا. ويختلف في عملية طلب الخدمة من الدليل، وهي عملية معزولة في طريقة getServeurSQL.
  • طريقة getServeurSQL مطابقة لتلك المكتوبة لعميل echo

وبالتالي يمكننا أن نلاحظ أن:

  • يختلف عميل CORBA عن عميل RMI فقط في طريقة اتصاله بالخادم
  • هذه الطريقة متطابقة لجميع عملاء CORBA
import srvSQL.*;
import org.omg.CosNaming.*;
import org.omg.CORBA.*;
import java.io.*;

public class clientSQL {

    // global class data
    private static String syntaxe =
        "syntaxe : cltSQL machine port service pilote urlBase id mdp separateur";
    private static BufferedReader in=null;
    private static interSQL serveurSQL=null;

    public static void main(String arg[]){
        // syntax : cltSQL machine port separator driver url id mdp
        // machine port: machine & service directory port CORBA to contact
        // department: department name
        // driver: driver to be used for the database to be processed
        // urlBase: jdbc url of the database to be used
        // id: user identity
        // mdp: password
        // separator: string separating fields in query results

        // check number of arguments
        if(arg.length!=8)
            erreur(syntaxe,1);

        // init database connection parameters
        String machine=arg[0];
        String port=arg[1];
        String service=arg[2];        
        String pilote=arg[3];
        String urlBase=arg[4];
        String id, mdp, separateur;
        if(arg[5].equals("null")) id=""; else id=arg[5];
        if(arg[6].equals("null")) mdp=""; else mdp=arg[6];        
        if(arg[7].equals("null")) separateur=" "; else separateur=arg[7];        

        // directory service parameters CORBA
        String[] initORB={"-ORBInitialHost",arg[0],"-ORBInitialPort",arg[1]};
        // client CORBA - request a server reference SQL
        interSQL serveurSQL=getServeurSQL(machine,port,service);

        // client-server dialogue
        String requete=null;
        String reponse=null;
        String[] lignes=null;
        String codeErreur=null;

        try{
            // open keyboard flow
            in=new BufferedReader(new InputStreamReader(System.in));
            // follow-up
            System.out.println("--> Connexion à la base de données en cours");
            // initial database connection request
            reponse=serveurSQL.connect(pilote,urlBase,id,mdp);
            // follow-up
            System.out.println("<-- "+reponse);
            // response analysis
            codeErreur=reponse.substring(0,3);
            if(codeErreur.equals("500")) 
                erreur("Abandon sur erreur de connexion à la base",3);
            // loop for reading requests to be sent to the server SQL
            System.out.print("--> Requête : ");
            requete=in.readLine().toLowerCase().trim();
            while(! requete.equals("fin")){
                // send request to server and receive response
                lignes=serveurSQL.executeSQL(requete,separateur);
                // follow-up
                afficheLignes(lignes);
                // following request
                System.out.print("--> Requête : ");                
                requete=in.readLine().toLowerCase().trim();
            }// while
            // follow-up
            System.out.println("--> Fermeture de la connexion à la base de données distante");
            // close the connection
            reponse=serveurSQL.close();
            // follow-up
            System.out.println("<-- " + reponse);
            // end
            System.exit(0);
        // error management        
        } catch (Exception e){
            erreur("Abandon sur erreur : " + e,2);
        }// try
    }// hand

    // ----------- AfficheLignes
    private static void afficheLignes(String[] lignes){
        for (int i=0;i<lignes.length;i++)
            System.out.println("<-- " + lignes[i]);
    }// afficheLignes

    // ------------ error
    private static void erreur(String msg, int exitCode){
        // error msg display
        System.err.println(msg);
        // possible release of resources
        try{
            in.close();
            serveurSQL.close();
        } catch(Exception e){}
        // we leave
        System.exit(exitCode);
    }// error

    // ---------------------- getServeurSQL
    private static interSQL getServeurSQL(String machine, String port, String service){
        // requests a server reference SQL
        // machine: service directory machine CORBA
        // port: service directory port CORBA
        // service: service name CORBA to request

        // follow-up
        System.out.println("--> Connexion au serveur CORBA en cours...");
        // server reference SQL
        interSQL serveurSQL=null;
        // directory service parameters CORBA
        String[] initORB={"-ORBInitialHost",machine,"-ORBInitialPort",port};
                try{
            // a CORBA object is required to work - to do this, we pass the port 
            // service directory listening CORBA
      ORB orb=ORB.init(initORB,null);
            // use the directory service to locate the SQL server
      org.omg.CORBA.Object objRef=
        orb.resolve_initial_references("NameService");
      NamingContext ncRef=NamingContextHelper.narrow(objRef);
            // the service required is called srvSQL - it is requested
      NameComponent nc= new NameComponent(service,"");
      NameComponent path[]={nc};
      serveurSQL=interSQLHelper.narrow(ncRef.resolve(path));
        } catch (Exception e){
            System.err.println("Erreur lors de la localisation du serveur SQL ("
                + e + ")");
            System.exit(10);
        }// try-catch
        // return the reference to the server
        return serveurSQL;
    }// getServeurSQL

}// class

دعونا نقوم بتجميع فئة العميل:

E:\data\java\corba\sql>j:\jdk12\bin\javac clientSQL.java

E:\data\java\corba\sql>dir *.class

SQLSER~1 CLA         2 568  19/03/99  10:19 SQLServant.class
SERVEU~1 CLA         1 800  19/03/99  10:33 serveurSQL.class
CLIENT~1 CLA         3 774  19/03/99  10:45 clientSQL.class

نحن جاهزون للاختبار.

10.3.7. الاختبارات

10.3.7.1. المتطلبات

نفترض أن قاعدة بيانات ACCESS باسم Articles متاحة للجميع على جهاز Windows الخاص بخادم SQL:

Image

تتميز قاعدة البيانات هذه بالبنية التالية:

الاسم
النوع
الرمز
رمز العنصر المكون من 4 أحرف
الاسم
اسمه (سلسلة)
السعر
سعره (الفعلي)
المخزون_الحالي
المخزون الحالي (عدد صحيح)
الحد_الأدنى_للمخزون
الحد الأدنى للمخزون (عدد صحيح) الذي يجب تجديد المخزون عند انخفاضه إلى ما دونه

10.3.7.2. بدء خدمة الدليل

E:\data\java\corba\sql>start j:\jdk12\bin\tnameserv -ORBInitialPort 1000

Le service d’annuaire est lancé sur le port 1000. Il affiche dans une fenêtre DOS quelque chose du genre :

Initial Naming Context:
IOR:000000000000002849444c3a6f6d672e6f72672f436f734e616d696e672f4e616d696e67436f
6e746578743a312e3000000000010000000000000030000100000000000a69737469612d30303900
052800000018afabcafe000000027693d3fd000000080000000000000000
TransientNameServer: setting port for initial object references to: 1000

10.3.7.3. بدء تشغيل خادم SQL

بدء تشغيل خادم SQL:

E:\data\java\corba\sql>start j:\jdk12\bin\java serveurSQL localhost 1000 srvSQL

يتم عرض ما يلي في نافذة DOS:

    Serveur SQL prêt

10.3.7.4. تشغيل عميل على نفس الجهاز الذي يعمل عليه الخادم

فيما يلي النتائج التي تم الحصول عليها باستخدام عميل على نفس الجهاز الذي يوجد عليه الخادم:

E:\data\java\corba\sql>j:\jdk12\bin\java clientSQL localhost 1000 srvSQL sun.jdbc.odbc.JdbcOdbcDriver jdbc:odbc:articles null null ,
--> Connection to server CORBA in progress...
--> Current database connection
<-- 200 Successful connection
--> Requête : select nom, stock_actu, stock_mini from articles
<-- 101 bicycle,31,8
<-- 101 arc,9,8
<-- 101 canoeing,7,7
<-- 101 rifle,9,8
<-- 101 water skis,13.8
<-- 101 test3,13,9
<-- 101 sperm whale,6,6
<-- 101 leopard,7,7
<-- 101 panther,7,7
--> Requête : delete from articles where stock_mini<7
<-- 100 1
--> Requête : select nom, stock_actu, stock_mini from articles
<-- 101 bicycle,31,8
<-- 101 arc,9,8
<-- 101 canoeing,7,7
<-- 101 rifle,9,8
<-- 101 water skis,13.8
<-- 101 test3,13,9
<-- 101 leopard,7,7
<-- 101 panther,7,7
--> Query: end
--> Closing the remote database connection
<-- 200 Closed base

10.3.7.5. تشغيل عميل على جهاز آخر غير الخادم

فيما يلي النتائج التي تم الحصول عليها باستخدام عميل على جهاز غير الخادم:

E:\data\java\corba\sql>j:\jdk12\bin\java clientSQL tahe.istia.univ-angers.fr 1000 srvSQL sun.jdbc.odbc.JdbcOdbcDriver jdbc:odbc:articles null null ,
--> Connection to server CORBA in progress...
--> Current database connection
<-- 200 Successful connection
--> Requête : select * from articles
<-- 101 a300,v_lo,1202,31,8
<-- 101 d600,arc,5000,9,8
<-- 101 d800,canoe,1502,7,7
<-- 101 x123,rifle,3000,9,8
<-- 101 s345,water skis,1800,13,8
<-- 101 f450,test3,3,13,9
<-- 101 z400,leopard,500000,7,7
<-- 101 g457,panther,800000,7,7
--> Query: end
--> Closing the remote database connection
<-- 200 Closed base

10.4. مطابقات IDL - JAVA

نقدم هنا التوافقات بين أنواع IDL البسيطة وأنواع Java:

نوع IDL
نوع Java
boolean
منطقية
حرف
حرف
wchar
حرف
بايت
بايت
سلسلة
java.lang.String
wstring
java.lang.String
short
short
short غير موقّع
short
طويل
int
طويل غير موقّع
int
طويل جدًا
طويل
طويل طويل غير موقّع
طويل
float
عدد عائم
double
double

تذكر أنه لتعريف مصفوفة من العناصر من النوع T في واجهة IDL، نستخدم العبارة:

    typedef   sequence<T> nomType;

وأننا نستخدم بعد ذلك TypeName للإشارة إلى نوع المصفوفة. وبالتالي، في واجهة خادم SQL، استخدمنا الإعلان:

    typedef sequence<string> resultats;

بحيث يشير results إلى مصفوفة String[] في Java.