Skip to content

10. ساخت برنامه‌های توزیع‌شده CORBA

10.1. مقدمه

در فصل قبلی، ما نحوه ایجاد برنامه‌های توزیع‌شده در جاوا با استفاده از بسته RMI را بررسی کردیم. در اینجا، همان مشکل را بررسی می‌کنیم، این بار با استفاده از معماری CORBA. CORBA (معماری مشترک کارگزار درخواست شیء) یک مشخصه‌ای است که توسط OMG (گروه مدیریت شیء) تعریف شده است، که شرکت‌های متعددی از بخش فناوری اطلاعات را گرد هم می‌آورد. CORBA یک «اتوبوس نرم‌افزاری» را تعریف می‌کند که برای برنامه‌های نوشته شده به زبان‌های مختلف قابل دسترسی است:

Image

در ادامه خواهیم دید که ساخت یک برنامه توزیع‌شده با استفاده از CORBA مشابه روشی است که در جاوا RMI استفاده می‌شود: مفاهیم مشابه هستند. CORBA مزیت تعامل‌پذیری با برنامه‌های نوشته شده به زبان‌های دیگر را ارائه می‌دهد.

10.2. فرآیند توسعه یک برنامه CORBA

10.2.1. مقدمه

برای توسعه یک برنامهٔ کلاینت-سرور CORBA، مراحل زیر را دنبال خواهیم کرد:

  1. نوشتن رابط سرور با استفاده از IDL (زبان تعریف رابط)
  2. تولید کلاس‌های «اسکلت» و «استاب» سرور
  3. نوشتن سرور
  4. نوشتن کلاینت
  5. کامپایل کردن تمام کلاس‌ها
  6. راه‌اندازی دایرکتوری سرویس CORBA
  7. راه‌اندازی سرور
  8. راه‌اندازی کلاینت

ما اولین مثال خود را با سرور اکو که قبلاً در زمینه RMI استفاده شده است، در نظر می‌گیریم. این به خواننده امکان می‌دهد تا تفاوت‌های بین این دو روش را ببیند.

این برنامه با jdk1.2 آزمایش شده است.

10.2.2. نوشتن رابط سرور

همانند Java RMI، از دیدگاه کلاینت، سرور با رابط آن تعریف می‌شود. در حالی که کلاس‌های پیاده‌ساز سرور برای کلاینت لازم نیستند، کلاس‌های رابط آن ضروری‌اند. در حالی که Java RMI از یک رابط جاوا برای تولید کلاس‌های «اسکلت» و «استاب» سرور استفاده می‌کرد، معماری Java CORBA ایجاب می‌کند که رابط در زبانی غیر از جاوا تعریف شود. این رابط چندین کلاس تولید می‌کند که برخی از آن‌ها توسط کلاینت و برخی دیگر توسط سرور استفاده می‌شوند.

توضیحات رابط echo به شرح زیر خواهد بود:

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

توضیحات این رابط در فایلی به نام echo.idl ذخیره خواهد شد. این فایل به زبان IDL (زبان تعریف رابط) نوشته شده است. برای اینکه قابل استفاده باشد، باید توسط برنامه‌ای تحلیل شود که فایل‌های منبعی را در زبان مورد استفاده برای توسعه برنامه CORBA ایجاد کند. در اینجا، ما از برنامه idltojava.exe استفاده خواهیم کرد که بر اساس رابط قبلی، فایل‌های منبعی .java مورد نیاز برنامه را ایجاد می‌کند. برنامه idltojava.exe همراه با JDK ارائه نمی‌شود. می‌توان آن را از وب‌سایت Sun به آدرس http://java.sun.com دانلود کرد.

بیایید چند خط از رابط کاربری قبلی idl را تحلیل کنیم:

module echo

معادل **package echo** در جاوا است. کامپایل کردن این رابط، بسته جاوا echo c.a.d را تولید می‌کند، یک دایرکتوری حاوی کلاس‌های جاوا.

interface iSrvEcho

معادل رابط جاوا iSrvEcho است. این یک رابط جاوا تولید خواهد کرد.

string echo(in string msg)

معادل دستور جاوا **String echo(String msg)** است. انواع در زبان IDL دقیقاً با انواع در زبان جاوا مطابقت ندارند. تطابق‌ها بعداً در این فصل ارائه می‌شوند. در زبان IDL، پارامترهای تابع می‌توانند ورودی (in)، خروجی (out) یا ورودی-خروجی (inout) باشند. در اینجا، متد echo یک پارامتر ورودی msg را که یک رشته است، می‌پذیرد و یک رشته را به عنوان نتیجه برمی‌گرداند.

رابط قبلی مربوط به سرور اکو ما است. به یاد داشته باشید که یک رابط از راه دور، متدهای شیء سرور را که برای کلاینت‌ها قابل دسترسی هستند، توصیف می‌کند. در اینجا، تنها متد echo برای کلاینت‌ها در دسترس خواهد بود.

10.2.3. کامپایل رابط IDL سرور

پس از تعریف رابط سرور، فایل‌های جاوا متناظر آن تولید می‌شوند.

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، فایل جاوا است که رابط سرور را توصیف می‌کند:

/*  * فایل: ./ECHO/ISRVECHO.JAVA  * از: ECHO.IDL  * تاریخ: دوشنبه ۱۵ مارس ۱۹۹۹، ساعت ۱۳:۵۶:۰۸ *   توسط: D:\JAVAIDL\IDLTOJ~1.EXE Java IDL 1.2 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

این «استاب» (stub) سرور است که کلاینت از آن استفاده خواهد کرد. این کلاس، قابلیت‌های 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 را پیاده‌سازی کرده است.

//بستههای واردشده
import echo.*;

//کلاس پیادهسازیکننده اکو از راه دور
public class srvEcho extends _iSrvEchoImplBase{
    // متد پیادهسازی اکو
    public String echo(String msg){
        return  "["  + msg + "]";
    }// پایان اکو
}// پایان کلاس

این کد خودگویا است. این کلاس در فایلی به نام 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 استفاده می‌کنید، متفاوت است. در اینجا سرور اکو CORBA در فایل serveurEcho.java ثبت شده است:

// پکیج‌های واردشده
import echo.*;
import org.omg.CosNaming.*;
import org.omg.CosNaming.NamingContextPackage.*;
import org.omg.CORBA.*;

//----------- کلاس serveurEcho
public class serveurEcho{
    // ------- main: سرور اکو را راه‌اندازی می‌کند
    // دستور زبان pg machineAnnuaire portAnnuaire nomService
    // machine: ماشینی که میزبان دایرکتوری است CORBA
    // port: پورت دایرکتوری CORBA
    // nomService: نام سرویس برای ثبت

  public static void main(String arg[]){
    //آیا آرگومان‌ها موجود هستند؟
    if(arg.length!=3){
        System.err.println("Syntaxe : pg machineAnnuaire portAnnuaire nomService");
        System.exit(1);
    }
    // بازیابی آرگومان‌ها
    String machine=arg[0];
    String port=arg[1];
    String nomService=arg[2];

    try{

    // ما به یک شیء CORBA برای کار نیاز داریم
    String[] initORB={"-ORBInitialHost",machine,"-ORBInitialPort",port};
      ORB orb=ORB.init(initORB,null);
    // افزودن سرویس به دایرکتوری سرویس‌ها
    // این srvEcho نامیده خواهد شد
      org.omg.CORBA.Object objRef=
        orb.resolve_initial_references("NameService");
      NamingContext ncRef=NamingContextHelper.narrow(objRef);
      NameComponent nc= new NameComponent(nomService,"");
      NameComponent path[]={nc};
    // ما سرور را ایجاد می‌کنیم و آن را با سرویس srvEcho مرتبط می‌سازیم
    srvEcho serveurEcho=new srvEcho();
      ncRef.rebind(path,serveurEcho);
    orb.connect(serveurEcho);
    // نظارت
    System.out.println("Serveur d'écho prêt");
    //در انتظار درخواست‌های کلاینت
      java.lang.Object sync=new java.lang.Object();
      synchronized(sync){
        sync.wait();
      }
    } catch(Exception e){
    // یک خطا رخ داده است
      System.err.println("Erreur " + e);
      e.printStackTrace(System.err);
    }
  }//اصلی
}// serveurEcho

در زیر، مراحل اصلی را برای راه‌اندازی سرور بدون ورود به جزئیات تشریح می‌کنیم که ممکن است در نگاه اول پیچیده به نظر برسد. مهم است که نکات کلیدی مثال قبلی را در نظر داشته باشید، زیرا این موارد برای هر سرور CORBA صدق می‌کند.

10.2.5.2.1. تنظیمات سرور

یک سرور CORBA باید در یک سرویس دایرکتوری که روی یک ماشین و پورت مشخص در حال اجرا است، ثبت‌نام کند. برنامه ما این دو اطلاعات را به عنوان پارامتر دریافت خواهد کرد. سرویس ثبت‌شده باید نامی داشته باشد؛ این نام، پارامتر سوم خواهد بود.

10.2.5.2.2. ایجاد شیء دسترسی به سرویس دایرکتوری CORBA

برای دسترسی به سرویس دایرکتوری و ثبت سرور اکو خود، به یک شیء به نام ORB (واسطه درخواست شیء) نیاز داریم که با استفاده از روش کلاسی زیر به دست می‌آید:

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 تنظیم شده بود، جفت (ماشین،پورت) استفاده‌شده پیش‌فرض (localhost,900) می‌بود.

10.2.5.2.3. ثبت سرور در دایرکتوری خدمات CORBA

سرور با انجام مراحل زیر در دایرکتوری ثبت می‌شود:

    // افزودن سرویس به دایرکتوری سرویس‌ها
    // این srvEcho نامیده خواهد شد
      org.omg.CORBA.Object objRef=
        orb.resolve_initial_references("NameService");
      NamingContext ncRef=NamingContextHelper.narrow(objRef);
      NameComponent nc= new NameComponent(nomService,"");
      NameComponent path[]={nc};
    // سرور را ایجاد کرده و آن را با سرویس srvEcho مرتبط کنید
    srvEcho serveurEcho=new srvEcho();
          ncRef.rebind(path,serveurEcho);
            orb.connect(serveurEcho);

بخش اول کد شامل آماده‌سازی نام سرویس است. این نام در کد با متغیر </span>**<span style="color: #000000">path **نشان داده می‌شود. نام یک سرویس از چندین مؤلفه تشکیل شده است:

  • یک مؤلفهٔ اولیه objRef، یک شیء عمومی که باید به نوع NamingContext تبدیل شود، در این مورد ncRef.
  • نام سرویس، در این مورد nomService، که به‌عنوان پارامتر به سرور ارسال شده است

این اجزای نام (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. کد

ما در حال نوشتن یک کلاینت برای آزمایش سرویس اکو خود هستیم. ما همان سه پارامتر را که به سرور دادیم، به کلاینت نیز ارسال خواهیم کرد:

ماشین: ماشینی که میزبان دایرکتوری سرویس CORBA است

پورت: پورتی که این دایرکتوری روی آن کار می‌کند

nomService: نام سرویس اکو

کلاینت به سرویس اکو متصل می‌شود و سپس از کاربر می‌خواهد که پیام‌ها را از روی صفحه‌کلید تایپ کند. این پیام‌ها به سرور اکو ارسال می‌شوند و سرور آن‌ها را بازمی‌گرداند. این مکالمه روی صفحه نمایش داده می‌شود.

کلاینت سرویس اکو CORBA بسیار شبیه به کلاینت RMI است که قبلاً نوشته شده است. در اینجا نیز، کلاینت باید به یک سرویس دایرکتوری متصل شود تا مرجعی به شیء سرور مورد نظر خود برای اتصال به دست آورد. تفاوت بین این دو کلاینت تنها در همین نکته نهفته است. در اینجا کد کلاینت اکو CORBA آورده شده است:


    

10.2.6.2. اتصال کلاینت به سرور

کلاینت CORBA فوق با استفاده از عبارت زیر به سرور متصل می‌شود:

        //ما یک اتصال با سرور echo برقرار می‌کنیم
        iSrvEcho serveurEcho=getServeurEcho(machine,port,nomService);

پس از اتمام این عملیات، کلاینت یک مرجع به سرور echo در اختیار دارد. پس از آن، کلاینت CORBA تفاوتی با کلاینت RMI ندارد. متد خصوصی که اتصال به سرور را برقرار می‌کند به شرح زیر است:

//بسته‌های واردشده
import java.io.*;
import echo.*;
import org.omg.CosNaming.*;
import org.omg.CORBA.*;

// ---------- کلاس cltEcho
public class cltEcho {

    public static void main(String arg[]){
         // دستور زبان: cltEcho machineAnnuaire portAnnuaire نام سرویس
         // ماشین: ماشینی که دایرکتوری خدمات روی آن اجرا می‌شود CORBA
         // پورت: پورتی که دایرکتوری سرویس روی آن کار می‌کند
         // nomService: نام سرویس اکو

         //اعتبارسنجی آرگومان
        if(arg.length!=3){
            System.err.println("Syntaxe : pg machineAnnuaire portAnnuaire nomservice");
            System.exit(1);
        }

         // بازیابی پارامترها
        String machine=arg[0];
        String port=arg[1];
        String nomService=arg[2];

         // برقراری ارتباط با سرور اکو
        iSrvEcho serveurEcho=getServeurEcho(machine,port,nomService);

         // گفتگوی کلاینت-سرور
        BufferedReader in=null;
        String msg=null;
        String reponse=null;
        iSrvEcho serveur=null;

        try{
             // باز کردن جریان صفحه‌کلید
            in=new BufferedReader(new InputStreamReader(System.in));
             // حلقه برای خواندن پیام‌ها جهت ارسال به سرور اکو
            System.out.print("Message : ");
            msg=in.readLine().toLowerCase().trim();
            while(! msg.equals("fin")){
                 //ارسال پیام به سرور و دریافت پاسخ
                reponse=serveurEcho.echo(msg);
                 //پیگیری
                System.out.println("Réponse serveur : " + reponse);
                 // پیام بعدی
                System.out.print("Message : ");                
                msg=in.readLine().toLowerCase().trim();
            }// در حالی که
             // تمام شد
            System.exit(0);
         // مدیریت خطا         
        } catch (Exception e){
            System.err.println("Erreur : " + e);
            System.exit(2);
        }// سعی کنید
    }// main

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

         // درخواست ارجاع از سرور اکو می‌کند
         //ردیابی
        System.out.println("--> Connexion au serveur CORBA en cours...");
         // مرجع سرور اکو
        iSrvEcho serveurEcho=null;
        try{
             //یک شیء CORBA برای پردازش درخواست شده است
            String[] initORB={"-ORBInitialHost",machine,"-ORBInitialPort",port};    
          ORB orb=ORB.init(initORB,null);
             //سرویس دایرکتوری برای یافتن سرور اکو استفاده می‌شود
          org.omg.CORBA.Object objRef=
                orb.resolve_initial_references("NameService");
          NamingContext ncRef=NamingContextHelper.narrow(objRef);
             // خدمتی که مورد جستجو است srvEcho نامیده می‌شود – در حال درخواست است
          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 serveurEcho;
    }//getServeurEcho

}//کلاس

ما همان توالی‌های کد را که در سرور وجود دارد، پیدا می‌کنیم:

  • ما یک شیء 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};
  • ما یک مرجع برای سرویس اکو از سرویس دایرکتوری درخواست می‌کنیم (اینجا است که ما با سرور متفاوت هستیم)
serveurEcho=iSrvEchoHelper.narrow(ncRef.resolve(path));

10.2.6.3. Compilation

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. راه‌اندازی سرویس دایرکتوری

در یک سیستم ویندوزی، سرویس دایرکتوری را به شرح زیر راه‌اندازی می‌کنیم:

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

این کار سرویس دایرکتوری را روی پورت ۱۰۰۰ در ماشین راه‌اندازی می‌کند.

سرویس دایرکتوری tnameserv یک نمایش روی صفحه ایجاد می‌کند که شبیه به شکل زیر است:

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

خواندنش کمی سخت است، اما به خط آخر توجه کنید: سرویس روی پورت ۱۰۰۰ در حال اجرا است.

10.2.7.2. راه‌اندازی سرور اکو

سرویس اکو با سه پارامتر راه‌اندازی می‌شود:


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. راه‌اندازی کلاینت روی یک رایانه ویندوزی دیگر به غیر از سرور

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. مثال ۲: یک سرور SQL

10.3.1. مقدمه

در اینجا پیاده‌سازی سرور SQL را که پیش‌تر در زمینه RMI جاوا بررسی کرده‌ایم، بار دیگر مرور می‌کنیم تا شباهت‌ها و تفاوت‌های بین این دو روش را برجسته سازیم. بیایید نقش این سرور SQL را به یاد بیاوریم: این سرور بر روی یک ماشین ویندوزی قرار دارد و به کلاینت‌های راه دور امکان دسترسی به پایگاه‌های داده عمومی ODBC را بر روی آن ایستگاه کاری ویندوزی می‌دهد.

Image

کلاینت CORBA می‌تواند سه عملیات را انجام دهد:

  • اتصال به پایگاه‌داده دلخواه
  • ارسال پرس‌وجوهای SQL
  • بستن اتصال

سرور پرس‌وجوهای SQL کلاینت را اجرا می‌کند و نتایج را به کلاینت بازمی‌گرداند. این کارکرد اصلی آن است و به همین دلیل آن را سرور SQL می‌نامیم. ما مراحل مختلفی را که قبلاً با سرور اکو بررسی کردیم، اعمال می‌کنیم.

10.3.2. نوشتن رابط IDL سرور

به‌عنوان یادآوری، در اینجا رابط RMI را که برای سرور استفاده کردیم، می‌بینید:

import java.rmi.*;

// رابط راه دور
public interface interSQL extends Remote{
    public String connect(String pilote, String url, String id, String mdp)
        throws java.rmi.RemoteException;
    public String[] executeSQL(String requete, String separateur) 
        throws java.rmi.RemoteException;
    public String close()
        throws java.rmi.RemoteException;
}

نقش روش‌های مختلف به شرح زیر بود:

Connect: کلاینت به یک پایگاه دادهٔ راه دور متصل می‌شود و درایور، URL JDBC و نام کاربری (id) و رمز عبور (mdp) خود را برای دسترسی به پایگاه داده مشخص می‌کند. سرور رشته‌ای را بازمی‌گرداند که نتیجهٔ اتصال را نشان می‌دهد:

    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();
    };//رابط
};// ماژول

تنها تفاوت نسبت به آنچه در رابط IDL سرور echo دیدیم، استفاده از کلمه کلیدی sequence است. این کلمه کلیدی به شما امکان می‌دهد یک آرایه یک‌بعدی تعریف کنید. تعریف در دو مرحله انجام می‌شود:

  • تعریف یک نوع برای مشخص کردن آرایه، در این مورد `resultats`:
typedef sequence<string> resultats;

کلیدواژه typedef برای برنامه‌نویسان C/C++ کاملاً آشناست: از آن برای تعریف یک نوع جدید استفاده می‌شود. در اینجا نوع resultats به‌عنوان معادل نوع sequence<string> تعریف شده است، c.a.d – یک آرایهٔ پویا (بدون اندازه) از رشته‌های کاراکتری.

  • استفاده از نوع جدید در جایی که لازم است
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 است. این دایرکتوری همچنین شامل فایل‌های مربوط به نوع resultats است که در رابط IDL ایجاد شده است.

فایل interSQL.java فایل جاوا برای رابط سرور ما است. مهم است که بررسی کنیم آنچه به‌طور خودکار تولید شده با نیازهای ما مطابقت دارد. فایل تولیدشده interSQL.java به شرح زیر است:

/*  * فایل: ./SRVSQL/INTERSQL.JAVA  * از: SRVSQL.IDL  * تاریخ: جمعه ۱۹ مارس ۱۹۹۹، ساعت ۰۹:۵۹:۴۸ *   توسط: D:\JAVAIDL\IDLTOJ~1.EXE Java IDL 1.2 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

ما اکنون در حال نوشتن کد سرور SQL هستیم. به یاد داشته باشید که این کلاس باید از کلاس انتزاعی _nomInterfaceImplBase که با کامپایل کردن فایل IDL تولید می‌شود، ارث ببرد. به جز این ویژگی خاص، و با مستثنی کردن توالی‌های کدی که به ثبت سرویس در یک دایرکتوری مربوط می‌شوند، کد سرور CORBA با کد سرور RMI یکسان است:

//بسته‌های واردشده
import java.sql.*;
import java.util.*;
import srvSQL.*;

// کلاس SQLServant
public class SQLServant extends _interSQLImplBase{

    // داده‌های کلاس جهانی
    private Connection DB;

    // --------------- اتصال
    public String connect(String pilote, String url, String id,
        String mdp){

        //اتصال به URL پایگاه داده با استفاده از درایور
        // احراز هویت با استفاده از شناسه و رمز عبور

        String resultat=null;            // نتیجهٔ متد
        try{
            // بارگذاری درایور
            Class.forName(pilote);
            // درخواست اتصال
            DB=DriverManager.getConnection(url,id,mdp);
            // OK
            resultat="200 Connexion réussie";
        } catch (Exception e){
            // خطا
            resultat="500 Echec de la connexion (" + e + ")";
        }
        // پایان
        return resultat;
    }            

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

        // یک پرس‌وجو SQL را روی پایگاه داده DB اجرا می‌کند
        //و نتایج را در آرایه‌ای از رشته‌ها قرار می‌دهد

        //داده‌های مورد نیاز برای اجرای پرس‌وجو
        Statement S=null;
        ResultSet RS=null;
        String[] lignes=null;
        Vector resultats=new Vector();
        String ligne=null;

        try{
            //ایجاد مخزن پرس‌وجو
            S=DB.createStatement();
            //اجرای پرس‌وجو
            if (! S.execute(requete)){
                // پرس‌وجوی به‌روزرسانی
                // تعداد ردیف‌های به‌روزرسانی‌شده را برمی‌گرداند
                lignes=new String[1];
                lignes[0]="100 "+S.getUpdateCount();
                return lignes;
            }
            // این یک پرس‌وجو بود
            // بازیابی نتایج
            RS=S.getResultSet();
            // تعداد ستون‌ها در مجموعه نتایج
            int nbChamps=RS.getMetaData().getColumnCount();
            // آنها را پردازش می‌کند
            while(RS.next()){
                // ایجاد سطر نتایج
                ligne="101 ";
                for (int i=1;i<nbChamps;i++)
                    ligne+=RS.getString(i)+separateur;
                ligne+=RS.getString(nbChamps);
                // افزودن به بردار نتایج
                resultats.addElement(ligne);
            }// در حالی که
            //پایان پردازش نتایج
            //منابع آزاد می‌شوند
            RS.close();
            S.close();
            // بازگرداندن نتایج
            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);
            }//اگر
            return lignes;
        } catch (Exception e){
            //خطا
            lignes=new String[1];
            lignes[0]="500 " + e;
            return lignes;
        }// try-catch
    }//executeSQL

    // --------------- بستن
    public String close(){
        // اتصال پایگاه داده را می‌بندد
        String resultat=null;
        try{
            DB.close();
            resultat="200 Base fermée";
        } catch (Exception e){
            resultat="500 Erreur à la fermeture de la base ("+e+")";
        }
        // نتیجه را بازمی‌گرداند
        return resultat;
    }
}// کلاس 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

کلاس قبلی نمایانگر سرور SQL پس از راه‌اندازی آن است. پیش از آن، باید در دایرکتوری سرویس CORBA ثبت شود. همانند سرویس echo، این کار را با استفاده از یک کلاس ویژه انجام می‌دهیم که در زمان اجرا سه پارامتر را به آن ارسال می‌کنیم:

Machine: ماشینی که دایرکتوری سرویس CORBA روی آن قرار دارد

پورت: پورتی که این دایرکتوری روی آن کار می‌کند

nomService: نام سرویس SQL

کد این کلاس تقریباً با کد کلاسی که همان وظیفه را برای سرویس اکو انجام می‌داد، یکسان است. ما خطی را که بین این دو کلاس متفاوت است، پررنگ کرده‌ایم: این خط شیء سرور یکسانی را ایجاد نمی‌کند. بنابراین می‌توانیم ببینیم که مکانیزم راه‌اندازی سرور همچنان یکسان باقی می‌ماند. اگر این مکانیزم را، همانطور که در اینجا انجام شده است، در یک کلاس مجزا قرار دهیم، عملاً برای توسعه‌دهنده شفاف می‌شود.

//بسته‌های واردشده
import srvSQL.*;
import org.omg.CosNaming.*;
import org.omg.CosNaming.NamingContextPackage.*;
import org.omg.CORBA.*;

//----------- کلاس serveurSQL
public class serveurSQL{
    // ------- main: سرور SQL را راه‌اندازی می‌کند
  public static void main(String arg[]){
        //سرویس پورت ماشین serveurSQL

        //آیا تعداد آرگومان‌ها صحیح است
        if(arg.length!=3){
            System.err.println("Syntaxe : pg machineAnnuaireCorba portAnnuaireCorba nomService");
            System.exit(1);
        }
        // بازیابی آرگومان‌ها
        String machine=arg[0];
        String port=arg[1];
        String nomService=arg[2];
        String[] initORB={"-ORBInitialHost",machine,"-ORBInitialPort",port};
    try{
            //ما برای کار کردن به یک شیء CORBA نیاز داریم
      ORB orb=ORB.init(initORB,null);
            // ما سرویس را به دایرکتوری سرویس‌ها اضافه می‌کنیم
      org.omg.CORBA.Object objRef=
        orb.resolve_initial_references("NameService");
      NamingContext ncRef=NamingContextHelper.narrow(objRef);
      NameComponent nc= new NameComponent(nomService,"");
      NameComponent path[]={nc};
        // ما سرور را ایجاد می‌کنیم و آن را با سرویس srvSQL مرتبط می‌سازیم
        SQLServant serveurSQL=new SQLServant();
      ncRef.rebind(path,serveurSQL);
        orb.connect(serveurSQL);
        // نظارت
        System.out.println("Serveur SQL prêt");
        // در انتظار درخواست‌های مشتری
      java.lang.Object sync=new java.lang.Object();
      synchronized(sync){
        sync.wait();
      }
    } catch(Exception e){
            //خطایی رخ داده است
      System.err.println("Erreur " + e);
      e.printStackTrace(System.err);
    }
  }// آرام
}// 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 روی آن قرار دارد

port: پورتی که این دایرکتوری روی آن کار می‌کند

nomService: نام سرویس SQL

driver: درایوری که سرور SQL باید برای مدیریت پایگاه داده مورد نظر از آن استفاده کند

urlBase: URL پایگاه داده برای مدیریت

id: شناسهٔ مشتری یا null اگر شناسه وجود نداشته باشد

رمز عبور: رمز عبور مشتری یا null در صورت عدم وجود رمز عبور

جداساز: کاراکتری که سرور SQL باید برای جدا کردن فیلدها در سطرهای نتیجه یک پرس‌وجو استفاده کند

در اینجا مثالی از پارامترهای ممکن آورده شده است:

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

where:

machine: ماشینی که دایرکتوری سرویس CORBA روی آن قرار دارد

پورت: پورتی که این دایرکتوری روی آن کار می‌کند

srvSQL: srvSQL، نام سرور CORBA

راننده: sun.jdbc.odbc.JdbcOdbcDriver, رانر استاندارد برای پایگاه‌های داده با رابط ODBC

urlBase: jdbc:odbc:articles، برای استفاده از پایگاه داده «articles» که در لیست پایگاه داده‌های عمومی ODBC روی ماشین ویندوز اعلام شده است

شناسه: null، بدون شناسه

رمز عبور: 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 {

    // داده‌های سراسری کلاس
    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[]){
        // دستور دستوری: cltSQL پورت ماشین جداکننده درایور آدرس اینترنتی شناسه رمز عبور
        // پورت ماشین: ماشین و پورت دایرکتوری سرویس CORBA برای تماس
        // سرویس: نام سرویس
        // راننده: راننده‌ای که برای دسترسی به پایگاه داده باید استفاده شود
        // urlBase: URL JDBC پایگاه داده مورد استفاده
        // id: شناسه کاربر
        // رمز عبور: رمز عبور کاربر
        // جداساز: رشته‌ای که فیلدها را در نتایج پرس‌وجو از هم جدا می‌کند

        // بررسی تعداد آرگومان‌ها
        if(arg.length!=8)
            erreur(syntaxe,1);

        // ابتدایی‌سازی پارامترهای اتصال پایگاه داده
        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];        

        //پارامترهای سرویس دایرکتوری CORBA
        String[] initORB={"-ORBInitialHost",arg[0],"-ORBInitialPort",arg[1]};
        // کلاینت CORBA – درخواست مرجع سرور SQL
        interSQL serveurSQL=getServeurSQL(machine,port,service);

        // گفتگوی کلاینت-سرور
        String requete=null;
        String reponse=null;
        String[] lignes=null;
        String codeErreur=null;

        try{
            // باز کردن جریان صفحه‌کلید
            in=new BufferedReader(new InputStreamReader(System.in));
            //ردیابی
            System.out.println("--> Connexion à la base de données en cours");
            //درخواست اتصال اولیه به پایگاه داده
            reponse=serveurSQL.connect(pilote,urlBase,id,mdp);
            //پیگیری
            System.out.println("<-- "+reponse);
            // تحلیل پاسخ
            codeErreur=reponse.substring(0,3);
            if(codeErreur.equals("500")) 
                erreur("Abandon sur erreur de connexion à la base",3);
            // حلقه خواندن پرس‌وجوهایی که باید به سرور ارسال شوند SQL
            System.out.print("--> Requête : ");
            requete=in.readLine().toLowerCase().trim();
            while(! requete.equals("fin")){
                //ارسال درخواست به سرور و دریافت پاسخ
                lignes=serveurSQL.executeSQL(requete,separateur);
                // نظارت
                afficheLignes(lignes);
                // درخواست بعدی
                System.out.print("--> Requête : ");                
                requete=in.readLine().toLowerCase().trim();
            }// در حالی که
            //پیگیری
            System.out.println("--> Fermeture de la connexion à la base de données distante");
            // اتصال بسته شد
            reponse=serveurSQL.close();
            //پیگیری
            System.out.println("<-- " + reponse);
            // پایان
            System.exit(0);
        // پردازش خطا        
        } catch (Exception e){
            erreur("Abandon sur erreur : " + e,2);
        }// سعی کنید
    }// اصلی

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

    // ------------ خطا
    private static void erreur(String msg, int exitCode){
        // پیام خطا نمایش داده شد
        System.err.println(msg);
        // آزادسازی احتمالی منابع
        try{
            in.close();
            serveurSQL.close();
        } catch(Exception e){}
        //خروج
        System.exit(exitCode);
    }// خطا

    // ---------------------- getServeurSQL
    private static interSQL getServeurSQL(String machine, String port, String service){
        // درخواست ارجاع سرور SQL
        // ماشین: ماشین از دایرکتوری خدمات CORBA
        // پورت: پورت دایرکتوری سرویس CORBA
        // سرویس: نام سرویس CORBA که باید درخواست شود

        // ردیابی
        System.out.println("--> Connexion au serveur CORBA en cours...");
        // مرجع سرور SQL
        interSQL serveurSQL=null;
        //پارامترهای سرویس دایرکتوری برای CORBA
        String[] initORB={"-ORBInitialHost",machine,"-ORBInitialPort",port};
                try{
            //یک شیء CORBA برای اجرا درخواست می‌شود – پورت برای این منظور ارسال می‌شود 
            // پورت گوش‌دادن سرویس دایرکتوری CORBA
      ORB orb=ORB.init(initORB,null);
            // از سرویس دایرکتوری برای یافتن سرور SQL استفاده می‌شود
      org.omg.CORBA.Object objRef=
        orb.resolve_initial_references("NameService");
      NamingContext ncRef=NamingContextHelper.narrow(objRef);
            // خدمتی که به دنبال آن هستیم srvSQL نام دارد – ما آن را درخواست می‌کنیم
      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 serveurSQL;
    }//getServeurSQL

}//کلاس

بیایید کلاس کلاینت را کامپایل کنیم:

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. Pré-requis

ما فرض می‌کنیم که یک پایگاه داده به نام «Articles» به صورت عمومی روی سرور ویندوز در دسترس است:

Image

این پایگاه داده ساختار زیر را دارد:

نام
نوع
code
کد آیتم ۴ کاراکتری
nom
نام آن (رشتهٔ کاراکتری)
prix
قیمت آن (واقعی)
stock_actu
موجودی فعلی آن (اعداد صحیح)
stock_mini
حداقل سطح موجودی (اعداد صحیح) که پس از آن باید کالا دوباره موجودی‌گذاری شود

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 ,
--> Connexion au serveur CORBA en cours...
--> Connexion à la base de données en cours
<-- 200 Connexion réussie
--> پرس‌وجو: select name, stock_actu, stock_mini from articles
<-- 101 vélo,31,8
<-- 101 arc,9,8
<-- 101 canoé,7,7
<-- 101 fusil,9,8
<-- 101 skis nautiques,13,8
<-- 101 essai3,13,9
<-- 101 cachalot,6,6
<-- 101 léopard,7,7
<-- 101 panthère,7,7
--> پرس‌وجو: حذف از مقالات جایی که stock_mini<7
<-- 100 1
--> پرس‌وجو: select name, stock_actu, stock_mini from articles
<-- 101 vélo,31,8
<-- 101 arc,9,8
<-- 101 canoé,7,7
<-- 101 fusil,9,8
<-- 101 skis nautiques,13,8
<-- 101 essai3,13,9
<-- 101 léopard,7,7
<-- 101 panthère,7,7
--> Requête : fin
--> Fermeture de la connexion à la base de données distante
<-- 200 Base fermée

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 ,
--> Connexion au serveur CORBA en cours...
--> Connexion à la base de données en cours
<-- 200 Connexion réussie
--> پرس‌وجو: select * from articles
<-- 101 a300,v_lo,1202,31,8
<-- 101 d600,arc,5000,9,8
<-- 101 d800,canoé,1502,7,7
<-- 101 x123,fusil,3000,9,8
<-- 101 s345,skis nautiques,1800,13,8
<-- 101 f450,essai3,3,13,9
<-- 101 z400,léopard,500000,7,7
<-- 101 g457,panthère,800000,7,7
--> Requête : fin
--> Fermeture de la connexion à la base de données distante
<-- 200 Base fermée

10.4. ماتریس تطبیق برای IDL – JAVA

در اینجا ما تطابق‌ها بین انواع ساده IDL و JAVA را ارائه می‌دهیم:

نوع IDL
نوع جاوا
boolean
boolean
char
char
wchar
char
byte
بايت
رشته
java.lang.String
string
java.lang.String
کوتاه
شورت
شورت بدون علامت
کوتاه
long
integer
long بدون علامت
int
long long
long
long long بدون علامت
long
شناور
float
double
double

به خاطر داشته باشید که برای تعریف آرایه‌ای از عناصر از نوع T در رابط IDL، از عبارت زیر استفاده می‌کنید:

    typedef   sequence<T> nomType;

و سپس از nomType برای ارجاع به نوع آرایه استفاده می‌شود. بنابراین، در رابط سرور SQL، اعلامیه‌ی زیر استفاده شد:

    typedef sequence<string> resultats;

به طوری که resultats به یک آرایه String[] در جاوا اشاره می‌کند.